Files

93 lines
2.9 KiB
Python
Raw Permalink Normal View History

"""
LOTUSGUILD TERMINAL DESIGN SYSTEM Flask Auth Helpers
Provides Authelia SSO integration via remote headers.
Usage:
from auth import require_auth, get_user
@app.route('/my-page')
@require_auth
def my_page():
user = get_user()
return render_template('page.html', user=user)
"""
from functools import wraps
from flask import request, g
def get_user() -> dict:
"""
Parse Authelia SSO headers into a normalised user dict.
Authelia injects these headers after validating the session:
Remote-User username / login name
Remote-Name display name
Remote-Email email address
Remote-Groups comma-separated group list
"""
groups_raw = request.headers.get('Remote-Groups', '')
groups = [g.strip() for g in groups_raw.split(',') if g.strip()]
return {
'username': request.headers.get('Remote-User', ''),
'name': request.headers.get('Remote-Name', ''),
'email': request.headers.get('Remote-Email', ''),
'groups': groups,
'is_admin': 'admin' in groups,
}
def require_auth(f):
"""
Decorator that enforces authentication + group-based authorisation.
Reads allowed_groups from app config (key: 'auth.allowed_groups').
Falls back to ['admin'] if not configured.
Returns 401 if no user header present (request bypassed Authelia).
Returns 403 if user is not in an allowed group.
"""
@wraps(f)
def wrapper(*args, **kwargs):
from flask import current_app
user = get_user()
if not user['username']:
return (
'<h1 style="font-family:monospace;color:#ff4444">401 — Not Authenticated</h1>'
'<p style="font-family:monospace">Access this service through Authelia SSO.</p>',
401,
)
cfg = current_app.config.get('APP_CONFIG', {})
allowed = cfg.get('auth', {}).get('allowed_groups', ['admin'])
if not any(grp in allowed for grp in user['groups']):
return (
f'<h1 style="font-family:monospace;color:#ff4444">403 — Access Denied</h1>'
f'<p style="font-family:monospace">'
f'Account <strong>{user["username"]}</strong> is not in an allowed group '
f'({", ".join(allowed)}).</p>',
403,
)
# Store user on Flask's request context for convenience
g.user = user
return f(*args, **kwargs)
return wrapper
def require_admin(f):
"""
Stricter decorator requires the 'admin' group regardless of config.
"""
@wraps(f)
@require_auth
def wrapper(*args, **kwargs):
user = get_user()
if not user['is_admin']:
return (
'<h1 style="font-family:monospace;color:#ff4444">403 — Admin Required</h1>',
403,
)
return f(*args, **kwargs)
return wrapper