auth providers

This commit is contained in:
Mateusz Gruszczyński
2026-05-25 09:58:02 +02:00
parent 58d1c7a761
commit 9021b09bc5
4 changed files with 90 additions and 8 deletions

View File

@@ -6,7 +6,7 @@ import secrets
from urllib.parse import urlparse
from flask import abort, g, jsonify, redirect, request, session, url_for
from flask import abort, g, has_request_context, jsonify, redirect, request, session, url_for
from werkzeug.security import check_password_hash, generate_password_hash
from ..config import (
@@ -17,6 +17,7 @@ from ..config import (
AUTH_PROXY_AUTO_CREATE_ROLE,
AUTH_PROXY_USER_HEADER,
API_ALLOWED_ORIGINS,
AUTH_BYPASS_HOSTS,
)
from ..db import connect, default_user_id, utcnow
@@ -67,8 +68,22 @@ def password_hash(password: str) -> str:
return generate_password_hash(password or "")
def _host_matches_bypass(host: str) -> bool:
clean = str(host or "").strip().lower()
if not clean:
return False
return clean in AUTH_BYPASS_HOSTS or clean.split(":", 1)[0] in AUTH_BYPASS_HOSTS
def auth_bypassed_request() -> bool:
# Note: Allows trusted direct-IP access to keep auth enabled for reverse-proxy traffic.
if not enabled() or not AUTH_BYPASS_HOSTS or not has_request_context():
return False
return _host_matches_bypass(request.host)
def current_user_id() -> int:
if not enabled():
if not enabled() or auth_bypassed_request():
return default_user_id()
api_user_id = getattr(g, "api_user_id", None)
if api_user_id:
@@ -361,9 +376,25 @@ def authenticate_external_user() -> dict[str, Any] | None:
if not user or not int(user.get("is_active") or 0):
return None
g.external_user_id = int(user["id"])
session["user_id"] = int(user["id"])
session["username"] = user.get("username")
session["role"] = user.get("role") or "user"
return _public_user(user)
def ensure_request_user() -> int:
# Note: Socket.IO events do not go through Flask before_request like normal REST calls,
# so external proxy auth must be resolved explicitly during the Socket.IO handshake/events.
if not enabled() or auth_bypassed_request():
return default_user_id()
uid = current_user_id()
if uid:
return uid
if uses_external_provider():
authenticate_external_user()
return current_user_id()
def logout_user() -> None:
session.clear()
@@ -595,6 +626,8 @@ def install_guards(app) -> None:
if not enabled():
return None
g.api_token_authenticated = False
if auth_bypassed_request():
return None
if request.path.startswith("/api/"):
token_user = authenticate_api_token(_request_api_token())
if token_user:

View File

@@ -217,7 +217,7 @@ def register_socketio_handlers(socketio):
@socketio.on("connect")
def handle_connect():
ensure_poller_started()
if auth.enabled() and not auth.current_user_id():
if auth.enabled() and not auth.ensure_request_user():
disconnect()
return False
profile = active_profile()
@@ -234,7 +234,7 @@ def register_socketio_handlers(socketio):
@socketio.on("select_profile")
def handle_select_profile(data):
if auth.enabled() and not auth.current_user_id():
if auth.enabled() and not auth.ensure_request_user():
disconnect()
return
old_profile = active_profile()