Fixes for public mode authentication flows.

This commit is contained in:
2026-01-31 04:48:44 +00:00
parent 232fd92b22
commit 8f76d770ee
4 changed files with 30 additions and 31 deletions
+13 -8
View File
@@ -95,14 +95,19 @@ async def control(req, ws):
async def watch(req, ws): async def watch(req, ws):
# Build user info from either built-in auth or SSO # Build user info from either built-in auth or SSO
user_info = None user_info = None
if sso_user := getattr(req.ctx, "sso_user", None): if sso.paskia_enabled():
# SSO auth (paskia mode): extract from validation response # SSO auth: call validation to get user info (don't enforce auth in public mode)
ctx = sso_user.get("ctx", {}) try:
perms = ctx.get("permissions", []) await sso.validate_sso_request(req)
user_info = { except Exception:
"username": ctx.get("user", {}).get("display_name", ""), pass # Ignore auth errors, user_info stays None
"privileged": "cista:admin" in perms, if sso_user := getattr(req.ctx, "sso_user", None):
} ctx = sso_user.get("ctx", {})
perms = ctx.get("permissions", [])
user_info = {
"username": ctx.get("user", {}).get("display_name", ""),
"privileged": "cista:admin" in perms,
}
elif req.ctx.user: elif req.ctx.user:
# Built-in auth: use local user database # Built-in auth: use local user database
user_info = { user_info = {
+1 -4
View File
@@ -257,10 +257,7 @@ def get_files(wanted: set) -> list[tuple[PurePosixPath, Path]]:
@app.get("/zip/<keys>/<zipfile:ext=zip>") @app.get("/zip/<keys>/<zipfile:ext=zip>")
async def zip_download(req, keys, zipfile, ext): async def zip_download(req, keys, zipfile, ext):
"""Download a zip archive of the given keys""" """Download a zip archive of the given keys"""
if config.config.authentication == "paskia": await auth.verify(req)
await auth.verify_sso(req)
else:
auth.verify(req)
wanted = set(keys.split("+")) wanted = set(keys.split("+"))
files = get_files(wanted) files = get_files(wanted)
+13 -16
View File
@@ -236,39 +236,36 @@ async def verify(request, *, privileged=False):
For paskia mode (PASKIA_BACKEND_URL set), validates against the SSO backend. For paskia mode (PASKIA_BACKEND_URL set), validates against the SSO backend.
For built-in mode, checks session-based authentication. For built-in mode, checks session-based authentication.
For public mode (config.public=True), allows all requests. For public mode (config.public=True), skips auth unless privileged is required.
All 401/403 responses include auth.iframe URL for consistent frontend handling
via the paskia library's showAuthIframe().
Args: Args:
request: The Sanic request object request: The Sanic request object
privileged: If True, requires admin privileges privileged: If True, requires admin privileges (always enforced even in public mode)
Raises: Raises:
Unauthorized: If authentication is required Unauthorized: If authentication is required
Forbidden: If access is denied Forbidden: If access is denied
""" """
# Public mode: skip auth unless privileged access is required
if config.config.public and not privileged:
return
sso = _get_sso() sso = _get_sso()
if sso.paskia_enabled(): if sso.paskia_enabled():
# SSO validation against auth backend
# Always check cista:login; privileged flag comes from response perm list
perm = "cista:admin" if privileged else "cista:login" perm = "cista:admin" if privileged else "cista:login"
await sso.validate_sso_request(request, perm=perm) await sso.validate_sso_request(request, perm=perm)
return return
user = getattr(request.ctx, "user", None) user = getattr(request.ctx, "user", None)
if privileged: if privileged:
if user: if user and user.privileged:
if user.privileged: return
return raise Forbidden(
raise Forbidden( "Access Forbidden: Only for privileged users",
"Access Forbidden: Only for privileged users", quiet=True,
quiet=True, )
) if user:
elif config.config.public or user:
return return
# Return iframe URL for paskia library to show login dialog
raise Unauthorized( raise Unauthorized(
f"Login required for {request.path}", f"Login required for {request.path}",
"cookie", "cookie",
+3 -3
View File
@@ -94,11 +94,11 @@ const settingsMenu = (e: Event) => {
if (store.user.isLoggedIn) { if (store.user.isLoggedIn) {
items.push({ label: '🚪 Logout', onClick: () => store.logout() }) items.push({ label: '🚪 Logout', onClick: () => store.logout() })
} else if (!ssoStore.isExternalAuth) { } else if (store.server.public) {
// Show login in paskia iframe overlay // Show login option only in public mode (non-public modes trigger auth automatically)
items.push({ label: '🔐 Login', onClick: async () => { items.push({ label: '🔐 Login', onClick: async () => {
try { try {
await showAuthIframe('/auth/restricted') await showAuthIframe('/auth/restricted#theme=light')
resumeWatching() resumeWatching()
} catch (e) { } catch (e) {
console.log('Login cancelled') console.log('Login cancelled')