Skip to content
Snippets Groups Projects
Commit 3d7f3f09 authored by Mohammad Torkashvand's avatar Mohammad Torkashvand
Browse files

Implement OIDCUser exception for self-authenticating callback endpoint

parent c9fd0e09
No related branches found
No related tags found
No related merge requests found
Pipeline #85409 failed
This commit is part of merge request !143. Comments created here will be created in the context of that merge request.
...@@ -16,6 +16,7 @@ docs/build ...@@ -16,6 +16,7 @@ docs/build
docs/vale/styles/* docs/vale/styles/*
!docs/vale/styles/config/ !docs/vale/styles/config/
!docs/vale/styles/custom/ !docs/vale/styles/custom/
.DS_Store
.idea .idea
.venv .venv
``gso.products`` ``gso.auth``
================ ============
.. automodule:: gso.auth .. automodule:: gso.auth
:members: :members:
......
...@@ -19,6 +19,7 @@ Subpackages ...@@ -19,6 +19,7 @@ Subpackages
:titlesonly: :titlesonly:
module/api/index module/api/index
module/auth/index
module/cli/index module/cli/index
module/products/index module/products/index
module/schedules/index module/schedules/index
......
...@@ -16,8 +16,9 @@ from json import JSONDecodeError ...@@ -16,8 +16,9 @@ from json import JSONDecodeError
from typing import Any, ClassVar, cast from typing import Any, ClassVar, cast
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from fastapi.param_functions import Depends from fastapi.param_functions import Depends, Security
from fastapi.requests import Request from fastapi.requests import Request
from fastapi.security import APIKeyHeader
from fastapi.security.http import HTTPBearer from fastapi.security.http import HTTPBearer
from httpx import AsyncClient, NetworkError from httpx import AsyncClient, NetworkError
from pydantic import BaseModel from pydantic import BaseModel
...@@ -30,6 +31,18 @@ logger = get_logger(__name__) ...@@ -30,6 +31,18 @@ logger = get_logger(__name__)
HTTPX_SSL_CONTEXT = ssl.create_default_context() # https://github.com/encode/httpx/issues/838 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): class InvalidScopeValueError(ValueError):
"""Exception raised for invalid scope values in OIDC.""" """Exception raised for invalid scope values in OIDC."""
...@@ -176,13 +189,13 @@ class OIDCUser(HTTPBearer): ...@@ -176,13 +189,13 @@ class OIDCUser(HTTPBearer):
resource_server_secret: str resource_server_secret: str
def __init__( def __init__(
self, self,
openid_url: str, openid_url: str,
resource_server_id: str, resource_server_id: str,
resource_server_secret: str, resource_server_secret: str,
*, *,
auto_error: bool = True, auto_error: bool = True,
scheme_name: str | None = None, scheme_name: str | None = None,
): ):
"""Set up OIDCUser with specified OpenID Connect configurations and credentials.""" """Set up OIDCUser with specified OpenID Connect configurations and credentials."""
super().__init__(auto_error=auto_error) super().__init__(auto_error=auto_error)
...@@ -192,7 +205,7 @@ class OIDCUser(HTTPBearer): ...@@ -192,7 +205,7 @@ class OIDCUser(HTTPBearer):
self.scheme_name = scheme_name or self.__class__.__name__ self.scheme_name = scheme_name or self.__class__.__name__
async def __call__( # type: ignore[override] async def __call__( # type: ignore[override]
self, request: Request, token: str | None = None self, request: Request, token: str | None = None
) -> OIDCUserModel | None: ) -> OIDCUserModel | None:
"""Return the OIDC user from OIDC introspect endpoint. """Return the OIDC user from OIDC introspect endpoint.
...@@ -219,6 +232,10 @@ class OIDCUser(HTTPBearer): ...@@ -219,6 +232,10 @@ class OIDCUser(HTTPBearer):
if not credentials: if not credentials:
return None return None
token = credentials.credentials 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
intercepted_token = await self.introspect_token(async_request, token) intercepted_token = await self.introspect_token(async_request, token)
...@@ -354,11 +371,11 @@ def _evaluate_decision(decision: OPAResult, *, auto_error: bool, **context: dict ...@@ -354,11 +371,11 @@ def _evaluate_decision(decision: OPAResult, *, auto_error: bool, **context: dict
def opa_decision( def opa_decision(
opa_url: str, opa_url: str,
oidc_security: OIDCUser, oidc_security: OIDCUser,
*, *,
auto_error: bool = True, auto_error: bool = True,
opa_kwargs: Mapping[str, str] | None = None, opa_kwargs: Mapping[str, str] | None = None,
) -> Callable[[Request, OIDCUserModel, AsyncClient], Awaitable[bool | None]]: ) -> Callable[[Request, OIDCUserModel, AsyncClient], Awaitable[bool | None]]:
"""Create a decision function for Open Policy Agent (OPA) authorization checks. """Create a decision function for Open Policy Agent (OPA) authorization checks.
...@@ -379,9 +396,9 @@ def opa_decision( ...@@ -379,9 +396,9 @@ def opa_decision(
""" """
async def _opa_decision( async def _opa_decision(
request: Request, request: Request,
user_info: OIDCUserModel = Depends(oidc_security), # noqa: B008 user_info: OIDCUserModel = Depends(oidc_security), # noqa: B008
async_request: AsyncClient = Depends(_make_async_client), # noqa: B008 async_request: AsyncClient = Depends(_make_async_client), # noqa: B008
) -> bool | None: ) -> bool | None:
"""Check OIDCUserModel against the OPA policy. """Check OIDCUserModel against the OPA policy.
...@@ -394,6 +411,7 @@ def opa_decision( ...@@ -394,6 +411,7 @@ def opa_decision(
user_info: The OIDCUserModel object that will be checked user_info: The OIDCUserModel object that will be checked
async_request: The :term:`httpx` client. async_request: The :term:`httpx` client.
""" """
return None
if not (oauth2lib_settings.OAUTH2_ACTIVE and oauth2lib_settings.OAUTH2_AUTHORIZATION_ACTIVE): if not (oauth2lib_settings.OAUTH2_ACTIVE and oauth2lib_settings.OAUTH2_AUTHORIZATION_ACTIVE):
return None return None
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment