diff --git a/admin/src/portr_admin/apis/security.py b/admin/src/portr_admin/apis/security.py index c6facf86..94f252ee 100644 --- a/admin/src/portr_admin/apis/security.py +++ b/admin/src/portr_admin/apis/security.py @@ -1,8 +1,9 @@ from typing import Annotated -from fastapi import Cookie, Depends, Header +from fastapi import Depends, Header, Request from portr_admin.models.auth import Session from portr_admin.models.user import Role, TeamUser, User +from portr_admin.services.user import get_or_create_user from portr_admin.utils.exception import PermissionDenied @@ -11,8 +12,17 @@ class NotAuthenticated(Exception): async def get_current_user( - portr_session: Annotated[str | None, Cookie()] = None, + request: Request ) -> User: + # TODO: use header from config + # TODO: only use header from config if matching host + proxy_auth_email = request.headers.get("Remote-Email") + + if proxy_auth_email: + return await get_or_create_user(proxy_auth_email) + + portr_session = request.cookies.get("portr_session") + if portr_session is None: raise NotAuthenticated diff --git a/admin/src/portr_admin/main.py b/admin/src/portr_admin/main.py index fef35dc7..aaf15cd0 100644 --- a/admin/src/portr_admin/main.py +++ b/admin/src/portr_admin/main.py @@ -41,11 +41,10 @@ async def lifespan(app: FastAPI): @app.get("/") async def render_index_template( - request: Request, - portr_session: Annotated[str | None, Cookie()] = None, + request: Request ): try: - user: User = await get_current_user(portr_session) + user: User = await get_current_user(request) except NotAuthenticated: return templates.TemplateResponse( request=request, @@ -67,11 +66,10 @@ async def render_index_template( @app.get("/new-team") @app.get("/instance-settings") async def render_index_template_for_setup_route( - request: Request, - portr_session: Annotated[str | None, Cookie()] = None, + request: Request ): try: - user = await get_current_user(portr_session) + user = await get_current_user(request) if not user.is_superuser: return RedirectResponse(url="/") except NotAuthenticated: @@ -98,10 +96,9 @@ async def render_index_template_for_setup_route( async def render_index_template_for_team_routes( request: Request, team: str, - portr_session: Annotated[str | None, Cookie()] = None, ): try: - user: User = await get_current_user(portr_session) + user: User = await get_current_user(request) except NotAuthenticated: next_url = request.url.path + "?" + request.url.query next_url_encoded = urllib.parse.urlencode({"next": next_url}) diff --git a/admin/src/portr_admin/services/user.py b/admin/src/portr_admin/services/user.py index c716efb0..d0bc7a95 100644 --- a/admin/src/portr_admin/services/user.py +++ b/admin/src/portr_admin/services/user.py @@ -14,6 +14,30 @@ class EmailFetchError(ServiceError): pass +async def get_or_create_user(email: str): + is_first_user = not (await User.filter().exists()) + + if is_first_user: + # If this is the first user we can create them + await User.create( + email=email, + is_superuser=True, + ) + + user = await User.get_or_none(email=email) + + if not user: + raise UserNotFoundError("User does not exist yet") + + is_user_on_team = await TeamUser.filter(user__email=email).exists() + + if not user.is_superuser and not is_user_on_team: + # This user MUST be part of a team to authenticate UNLESS they're a superuser + raise UserNotFoundError("User not part of any team") + + return user + + @transactions.atomic() async def get_or_create_user_from_github(code: str): client = GithubOauth( @@ -35,16 +59,7 @@ async def get_or_create_user_from_github(code: str): if not github_user["email"]: raise EmailFetchError("No verified email found") - is_superuser = await User.filter().count() == 0 - - if not is_superuser: - if not await TeamUser.filter(user__email=github_user["email"]).exists(): - raise UserNotFoundError("User not part of any team") - - user, _ = await User.get_or_create( - email=github_user["email"], - defaults={"is_superuser": is_superuser}, - ) + user = await get_or_create_user(github_user["email"]) github_user_obj, created = await GithubUser.get_or_create( user=user,