diff --git a/.gitignore b/.gitignore index 5133b408045b4490147ccf3ca8c43db90d1fa667..fccb8082923cbdf2088055e1489862884abd02c3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ docs/build docs/vale/styles/* !docs/vale/styles/config/ !docs/vale/styles/custom/ +.DS_Store .idea .venv diff --git a/docs/source/module/auth/index.rst b/docs/source/module/auth/index.rst index 0ec5cd1fad1966607dbfaf3797235286bd08503c..5d818a853cd02ffe3b7e4ca1a8b43528c2c2a003 100644 --- a/docs/source/module/auth/index.rst +++ b/docs/source/module/auth/index.rst @@ -1,5 +1,5 @@ -``gso.products`` -================ +``gso.auth`` +============ .. automodule:: gso.auth :members: diff --git a/docs/source/modules.rst b/docs/source/modules.rst index 36fb6ff1775e08a69e3a706bb289e146b8ff99d7..ab1adef70f18ef8a4021143bd71d53bd3eae4f86 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -19,6 +19,7 @@ Subpackages :titlesonly: module/api/index + module/auth/index module/cli/index module/products/index module/schedules/index diff --git a/gso/auth/oidc_policy_helper.py b/gso/auth/oidc_policy_helper.py index 945b7496d36f457574140dd191437af179b95f95..005e6cd4a5e6b292613f9612d350ddc73f5f0ba1 100644 --- a/gso/auth/oidc_policy_helper.py +++ b/gso/auth/oidc_policy_helper.py @@ -30,6 +30,16 @@ logger = get_logger(__name__) HTTPX_SSL_CONTEXT = ssl.create_default_context() # https://github.com/encode/httpx/issues/838 +_CALLBACK_STEP_API_URL_PATTERN = re.compile( + r"^/api/processes/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" + r"/callback/([0-9a-zA-Z\-_]+)$" +) + + +def _is_callback_step_endpoint(request: Request) -> bool: + """Check if the request is a callback step API call.""" + return re.match(_CALLBACK_STEP_API_URL_PATTERN, request.url.path) is not None + class InvalidScopeValueError(ValueError): """Exception raised for invalid scope values in OIDC.""" @@ -212,14 +222,18 @@ class OIDCUser(HTTPBearer): return None async with AsyncClient(http1=True, verify=HTTPX_SSL_CONTEXT) as async_request: - await self.check_openid_config(async_request) - if not token: credentials = await super().__call__(request) if not credentials: return None token = credentials.credentials + elif _is_callback_step_endpoint(request): + logger.debug( + "callback step endpoint is called. verification will be done by endpoint itself.", url=request.url + ) + return None + await self.check_openid_config(async_request) intercepted_token = await self.introspect_token(async_request, token) if "active" not in intercepted_token: diff --git a/test/auth/test_oidc_policy_helper.py b/test/auth/test_oidc_policy_helper.py index 500b18cb6ee8420497768ee7ebde0ed364b46cdf..14af9f6b4ee55c5025aaef64414017f85a8f7513 100644 --- a/test/auth/test_oidc_policy_helper.py +++ b/test/auth/test_oidc_policy_helper.py @@ -12,6 +12,7 @@ from gso.auth.oidc_policy_helper import ( OPAResult, _evaluate_decision, _get_decision, + _is_callback_step_endpoint, opa_decision, ) from gso.auth.settings import oauth2lib_settings @@ -285,3 +286,24 @@ async def test_oidc_user_call_token_from_request(oidc_user, mock_request, mock_a assert isinstance(result, OIDCUserModel) assert result["sub"] == "123" assert result["name"] == "John Doe" + + +@pytest.mark.parametrize( + ("path", "expected"), + [ + ( + "/api/processes/daa171b3-7a76-4ac5-9528-11aefa5a6222/callback/9MS2tkFLl-TvWUHD2yhftfFSnPLR-koQolXBeG8OE-o", + True, + ), + ("/api/some/other/path", False), + ], +) +def test_is_callback_step_endpoint(path, expected): + request = Request( + scope={ + "type": "http", + "path": path, + "headers": [(b"host", b"example.com")], + } + ) + assert _is_callback_step_endpoint(request) is expected