add helper and simplify auth conditions

This commit is contained in:
Josh Hawkins 2025-11-26 10:18:34 -06:00
parent 07d158aac2
commit d96169eb47

View File

@ -39,15 +39,14 @@ def require_admin_by_default():
This is set as the default dependency on the FastAPI app to ensure all This is set as the default dependency on the FastAPI app to ensure all
endpoints require admin access unless explicitly overridden with endpoints require admin access unless explicitly overridden with
allow_public(), allow_any_authenticated(), or allow_viewer(). allow_public(), allow_any_authenticated(), or require_role().
Port 5000 (internal) always has admin role set by the /auth endpoint, Port 5000 (internal) always has admin role set by the /auth endpoint,
so this check passes automatically for internal requests. so this check passes automatically for internal requests.
Certain paths are exempted from the global admin check because they have Certain paths are exempted from the global admin check because they must
their own route-level authorization dependencies (allow_public(), be accessible before authentication (login, auth) or they have their own
allow_any_authenticated(), etc). The route-level dependencies handle the route-level authorization dependencies that handle access control.
actual authorization for these paths.
""" """
# Paths that have route-level auth dependencies and should bypass global admin check # Paths that have route-level auth dependencies and should bypass global admin check
# These paths still have authorization - it's handled by their route-level dependencies # These paths still have authorization - it's handled by their route-level dependencies
@ -106,6 +105,7 @@ def require_admin_by_default():
return return
# For all other paths, require admin role # For all other paths, require admin role
# Port 5000 (internal) requests have admin role set automatically
role = request.headers.get("remote-role") role = request.headers.get("remote-role")
if role == "admin": if role == "admin":
return return
@ -118,6 +118,17 @@ def require_admin_by_default():
return admin_checker return admin_checker
def _is_authenticated(request: Request) -> bool:
"""
Helper to determine if a request is from an authenticated user.
Returns True if the request has a valid authenticated user (not anonymous).
Port 5000 internal requests are considered anonymous despite having admin role.
"""
username = request.headers.get("remote-user")
return username is not None and username != "anonymous"
def allow_public(): def allow_public():
""" """
Override dependency to allow unauthenticated access to an endpoint. Override dependency to allow unauthenticated access to an endpoint.
@ -139,47 +150,31 @@ def allow_any_authenticated():
""" """
Override dependency to allow any authenticated user (bypass admin requirement). Override dependency to allow any authenticated user (bypass admin requirement).
The user must have valid remote-user and remote-role headers, but any role Allows:
is accepted. Port 5000 requests have "admin" role so they also pass. - Port 5000 internal requests (have admin role despite anonymous user)
- Any authenticated user with a real username (not "anonymous")
Rejects:
- Port 8971 requests with anonymous user (auth disabled, no proxy auth)
Example: Example:
@router.get("/authenticated-endpoint", dependencies=[Depends(allow_any_authenticated())]) @router.get("/authenticated-endpoint", dependencies=[Depends(allow_any_authenticated())])
""" """
async def auth_checker(request: Request): async def auth_checker(request: Request):
if not request.headers.get("remote-user"): # Port 5000 requests have admin role and should be allowed
raise HTTPException( role = request.headers.get("remote-role")
status_code=401, if role == "admin":
detail="Authentication required", return
)
# Otherwise require a real authenticated user (not anonymous)
if not _is_authenticated(request):
raise HTTPException(status_code=401, detail="Authentication required")
return return
return auth_checker return auth_checker
def allow_viewer():
"""
Override dependency to allow viewer or higher roles (admin, custom roles with cameras).
This is useful for read-only endpoints that should allow non-admin authenticated users.
Example:
@router.get("/viewer-endpoint", dependencies=[Depends(allow_viewer())])
"""
async def viewer_checker(request: Request):
role = request.headers.get("remote-role")
if not role:
raise HTTPException(
status_code=401,
detail="Authentication required",
)
# Any role is allowed (admin, viewer, or custom roles)
return
return viewer_checker
router = APIRouter(tags=[Tags.auth]) router = APIRouter(tags=[Tags.auth])