From 2f386b0a3529fd13bffa12ceaec79c964f112b1b Mon Sep 17 00:00:00 2001
From: Mohammad Torkashvand <mohammad.torkashvand@geant.org>
Date: Wed, 17 Jan 2024 15:12:58 +0100
Subject: [PATCH] Implement OIDCUser exception for self-authenticating callback
 endpoint

---
 .DS_Store                         | Bin 0 -> 6148 bytes
 .gitignore                        |   1 +
 docs/.DS_Store                    | Bin 0 -> 6148 bytes
 docs/source/module/auth/index.rst |   4 +--
 docs/source/modules.rst           |   1 +
 gso/auth/oidc_policy_helper.py    |  52 ++++++++++++++++++++----------
 6 files changed, 39 insertions(+), 19 deletions(-)
 create mode 100644 .DS_Store
 create mode 100644 docs/.DS_Store

diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..d6ec7284e694fb3fe7ff620aaf9e8128e182db9c
GIT binary patch
literal 6148
zcmZQzU|@7AO)+F(5MW?n;9!8zj35RBCIAV8Fop~hR0Kpbg3L%NFD^*R$xmWnVAuhb
z8|)Ow?JNu_4EYSn48_RqPb$dCEG{uHxW>rD%)-jX&cV*X%@G@%kzXEMl2}q&?37p(
z4dR95=jSBB*ojGDnW^RR0wT`&c_oRNd8tJpCBc~~sY!`NG2xkcDf#72`K5U&#bCWq
z2@XyU&UgWd>S`lPGaUtE3&UC+g=$M9104kuW3$>?P7YCJee0n3?3~=Z{4Pj<GBQGF
z23{x)qq-Rw7~uXX3ogpb$<Ip%#WzgBC_NeiqaiRF0;3@?;zIyhhj4I@_>dS4fYA^b
z4FU8J0F@64(6*xklx~2~ASnh$1_n^`8AP+d^n&{V5H%pJAUTj$5Dn7GzzAZ2<-uAR
z7#Sd18NuBU1_nlOmjpzEwKFg>fVDF)GJv%+z#6`c5bX?%5bX@G&I==`M*}h+qMd;e
zq8;YEQF=54MneD^0?ZIv0960GGQhJCM0AuK4S~@R7!e`B$l?<0;smNRad;P0*MjQP
z1gJcy_J>r*po$u`5x@i)P?Ufw1y%PTtsoj)6*DqGYVy&B04#(?>Cq6Ne+U2o2;fRp

literal 0
HcmV?d00001

diff --git a/.gitignore b/.gitignore
index 5133b4080..a235e7aac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ docs/build
 docs/vale/styles/*
 !docs/vale/styles/config/
 !docs/vale/styles/custom/
+.DS_Store # macOS folder config file
 
 .idea
 .venv
diff --git a/docs/.DS_Store b/docs/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..4c0c4a5630faea6a98ea71498a3567245827ce2d
GIT binary patch
literal 6148
zcmZQzU|@7AO)+F(5MW?n;9!8zj35RBCIAV8Fop~hR0Kpbg3U-tDlaZb%E?cH$_#!A
z<aSnuB!*IkOoki=WcTk#D#*z!E-^5;#>m9X!pg?Z!Op?W5gVM5UmjeNSW;T-lvorE
z;)Uer=On?{iAiCZspatkBF_1FC5f4NsYM_q!I>$kNr^=<;hA|U`Q=XerFkjEV7*WY
z4o(ivcmav(Y9mWC9R*_x!&)7MYD*&n9R(9(v)Wot4pC)&>!A4ToZP(pE=Zs<GD2tu
zUMLNtx)~T4kbG1YT$GoSpO+4bbC{A*dNc$^Ltr!nMnhmkhXAw&;ou(8p)l(C(GVC7
z0c;@vDjyV}?M4SE-2kCMQVfg?44~#Sh-QK5hx7v&Kw==RAR43<M1!<4FoIZMGr(FI
z7#Sd18NuBUkUmhC1Vn?iGcYoMwKFg>fVDHgn!St=?F@_%?F_Ka3nN5310zH`10zH`
z%z2~qXb6mk05k-cA+!Lf{&!`7XCa8_C^;GeqaiRNLV%IQCD_FYTq$GsFQ~2s)u#zi
xX;AGCsg6MvHEJV(2{NE40aXgB?m=2XG`K2eWPsG<qYVLA2#wOCAwd5S000z@O3?rS

literal 0
HcmV?d00001

diff --git a/docs/source/module/auth/index.rst b/docs/source/module/auth/index.rst
index 0ec5cd1fa..5d818a853 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 36fb6ff17..ab1adef70 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 945b7496d..3d384b528 100644
--- a/gso/auth/oidc_policy_helper.py
+++ b/gso/auth/oidc_policy_helper.py
@@ -16,8 +16,9 @@ from json import JSONDecodeError
 from typing import Any, ClassVar, cast
 
 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.security import APIKeyHeader
 from fastapi.security.http import HTTPBearer
 from httpx import AsyncClient, NetworkError
 from pydantic import BaseModel
@@ -30,6 +31,18 @@ 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."""
@@ -176,13 +189,13 @@ class OIDCUser(HTTPBearer):
     resource_server_secret: str
 
     def __init__(
-        self,
-        openid_url: str,
-        resource_server_id: str,
-        resource_server_secret: str,
-        *,
-        auto_error: bool = True,
-        scheme_name: str | None = None,
+            self,
+            openid_url: str,
+            resource_server_id: str,
+            resource_server_secret: str,
+            *,
+            auto_error: bool = True,
+            scheme_name: str | None = None,
     ):
         """Set up OIDCUser with specified OpenID Connect configurations and credentials."""
         super().__init__(auto_error=auto_error)
@@ -192,7 +205,7 @@ class OIDCUser(HTTPBearer):
         self.scheme_name = scheme_name or self.__class__.__name__
 
     async def __call__(  # type: ignore[override]
-        self, request: Request, token: str | None = None
+            self, request: Request, token: str | None = None
     ) -> OIDCUserModel | None:
         """Return the OIDC user from OIDC introspect endpoint.
 
@@ -219,6 +232,10 @@ class OIDCUser(HTTPBearer):
                 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
 
             intercepted_token = await self.introspect_token(async_request, token)
 
@@ -354,11 +371,11 @@ def _evaluate_decision(decision: OPAResult, *, auto_error: bool, **context: dict
 
 
 def opa_decision(
-    opa_url: str,
-    oidc_security: OIDCUser,
-    *,
-    auto_error: bool = True,
-    opa_kwargs: Mapping[str, str] | None = None,
+        opa_url: str,
+        oidc_security: OIDCUser,
+        *,
+        auto_error: bool = True,
+        opa_kwargs: Mapping[str, str] | None = None,
 ) -> Callable[[Request, OIDCUserModel, AsyncClient], Awaitable[bool | None]]:
     """Create a decision function for Open Policy Agent (OPA) authorization checks.
 
@@ -379,9 +396,9 @@ def opa_decision(
     """
 
     async def _opa_decision(
-        request: Request,
-        user_info: OIDCUserModel = Depends(oidc_security),  # noqa: B008
-        async_request: AsyncClient = Depends(_make_async_client),  # noqa: B008
+            request: Request,
+            user_info: OIDCUserModel = Depends(oidc_security),  # noqa: B008
+            async_request: AsyncClient = Depends(_make_async_client),  # noqa: B008
     ) -> bool | None:
         """Check OIDCUserModel against the OPA policy.
 
@@ -394,6 +411,7 @@ def opa_decision(
             user_info: The OIDCUserModel object that will be checked
             async_request: The :term:`httpx` client.
         """
+        return None
         if not (oauth2lib_settings.OAUTH2_ACTIVE and oauth2lib_settings.OAUTH2_AUTHORIZATION_ACTIVE):
             return None
 
-- 
GitLab