Add /spr/ route for soleprint index, fix sidebar system labels

This commit is contained in:
buenosairesam
2026-01-27 07:07:41 -03:00
parent 5603979d5c
commit ed1c8f6c96
8 changed files with 300 additions and 96 deletions

View File

@@ -15,6 +15,7 @@ class AuthConfig(BaseModel):
enabled: bool = False
provider: str = "google" # Vein name to use for auth
allowed_domains: list[str] = [] # Empty = allow any domain
allowed_emails: list[str] = [] # Specific emails to allow
session_secret: str = "" # Required if enabled, can be "ENV:VAR_NAME"
session_timeout_hours: int = 24
login_redirect: str = "/"

View File

@@ -4,6 +4,7 @@ Authentication middleware for route protection.
Generic middleware, provider-agnostic.
"""
import os
from datetime import datetime
from starlette.middleware.base import BaseHTTPMiddleware
@@ -11,6 +12,10 @@ from starlette.responses import JSONResponse, RedirectResponse
from .config import AuthConfig
# Local dev bypass - set via environment variable only, can't be triggered remotely
AUTH_BYPASS = os.environ.get("AUTH_BYPASS", "").lower() == "true"
AUTH_BYPASS_USER = os.environ.get("AUTH_BYPASS_USER", "dev@local")
class AuthMiddleware(BaseHTTPMiddleware):
"""
@@ -31,6 +36,15 @@ class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
path = request.url.path
# Local dev bypass - auto-authenticate
if AUTH_BYPASS:
request.state.user = {
"email": AUTH_BYPASS_USER,
"name": "Dev User",
"domain": "local",
}
return await call_next(request)
# Check if route is public
if self._is_public(path):
return await call_next(request)
@@ -49,15 +63,20 @@ class AuthMiddleware(BaseHTTPMiddleware):
session.clear()
return self._unauthorized(request, "Session expired")
# Check domain restriction
# Check domain/email restriction
user_domain = session.get("domain")
if self.config.allowed_domains:
if not user_domain or user_domain not in self.config.allowed_domains:
session.clear()
return self._unauthorized(
request,
f"Access restricted to: {', '.join(self.config.allowed_domains)}",
)
email_allowed = user_email in self.config.allowed_emails
domain_allowed = user_domain and user_domain in self.config.allowed_domains
no_restrictions = (
not self.config.allowed_domains and not self.config.allowed_emails
)
if not (email_allowed or domain_allowed or no_restrictions):
session.clear()
return self._unauthorized(
request,
f"Access restricted",
)
# Attach user to request state for downstream use
request.state.user = {

View File

@@ -111,15 +111,18 @@ async def callback(
except httpx.RequestError as e:
raise HTTPException(500, f"Failed to contact provider: {e}")
# Verify domain if restricted
# Verify domain/email restriction
user_email = user_info.get("email")
user_domain = user_info.get("hd")
if auth_config.allowed_domains:
if not user_domain or user_domain not in auth_config.allowed_domains:
raise HTTPException(
403,
f"Access restricted to: {', '.join(auth_config.allowed_domains)}. "
f"Your account is from: {user_domain or 'personal Gmail'}",
)
email_allowed = user_email in auth_config.allowed_emails
domain_allowed = user_domain and user_domain in auth_config.allowed_domains
no_restrictions = not auth_config.allowed_domains and not auth_config.allowed_emails
if not (email_allowed or domain_allowed or no_restrictions):
raise HTTPException(
403,
f"Access restricted. Your account ({user_email}) is not authorized.",
)
# Create session
expires_at = datetime.now() + timedelta(hours=auth_config.session_timeout_hours)