Skip to content

Commit 6913689

Browse files
authored
Support for authentication using external proxy (#33)
* add options for HTTP header authentication to config * add template for handling error 401: Unauthorized * support external authentication Expects authentication to be done using an external tool (such as Apache), that fills the users UUID to a HTTP header and acts as a proxy.
1 parent 9af2ec9 commit 6913689

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

config.example.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ class Config():
99
TESTING = False
1010
# SSO auth enabled
1111
SSO_AUTH = False
12+
# Authentication is done outside the app, use HTTP header to get the user uuid.
13+
# If SSO_AUTH is set to True, this option is ignored and SSO auth is used.
14+
HEADER_AUTH = True
15+
# Name of HTTP header containing the UUID of authenticated user.
16+
# Only used when HEADER_AUTH is set to True
17+
AUTH_HEADER_NAME = 'X-Authenticated-User'
1218
# SSO LOGOUT
1319
LOGOUT_URL = "https://flowspec.example.com/Shibboleth.sso/Logout"
1420
# SQL Alchemy config

flowapp/__init__.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import babel
33

4-
from flask import Flask, redirect, render_template, session, url_for
4+
from flask import Flask, redirect, render_template, session, url_for, request
55
from flask_sso import SSO
66
from flask_sqlalchemy import SQLAlchemy
77
from flask_wtf.csrf import CSRFProtect
@@ -72,21 +72,9 @@ def login(user_info):
7272
else:
7373
user = db.session.query(models.User).filter_by(uuid=uuid).first()
7474
try:
75-
session["user_uuid"] = user.uuid
76-
session["user_email"] = user.uuid
77-
session["user_name"] = user.name
78-
session["user_id"] = user.id
79-
session["user_roles"] = [role.name for role in user.role.all()]
80-
session["user_orgs"] = ", ".join(
81-
org.name for org in user.organization.all()
82-
)
83-
session["user_role_ids"] = [role.id for role in user.role.all()]
84-
session["user_org_ids"] = [org.id for org in user.organization.all()]
85-
roles = [i > 1 for i in session["user_role_ids"]]
86-
session["can_edit"] = True if all(roles) and roles else []
75+
_register_user_to_session(uuid)
8776
except AttributeError:
88-
return redirect("/")
89-
77+
pass
9078
return redirect("/")
9179

9280
@app.route("/logout")
@@ -96,6 +84,19 @@ def logout():
9684
session.clear()
9785
return redirect(app.config.get("LOGOUT_URL"))
9886

87+
@app.route("/ext-login")
88+
def ext_login():
89+
header_name = app.config.get("AUTH_HEADER_NAME", 'X-Authenticated-User')
90+
if header_name not in request.headers:
91+
return render_template("errors/401.j2")
92+
uuid = request.headers.get(header_name)
93+
if uuid:
94+
try:
95+
_register_user_to_session(uuid)
96+
except AttributeError:
97+
return render_template("errors/401.j2")
98+
return redirect("/")
99+
99100
@app.route("/")
100101
@auth_required
101102
def index():
@@ -177,4 +178,20 @@ def format_datetime(value):
177178

178179
return babel.dates.format_datetime(value, format)
179180

181+
def _register_user_to_session(uuid: str):
182+
user = db.session.query(models.User).filter_by(uuid=uuid).first()
183+
session["user_uuid"] = user.uuid
184+
session["user_email"] = user.uuid
185+
session["user_name"] = user.name
186+
session["user_id"] = user.id
187+
session["user_roles"] = [role.name for role in user.role.all()]
188+
session["user_orgs"] = ", ".join(
189+
org.name for org in user.organization.all()
190+
)
191+
session["user_role_ids"] = [role.id for role in user.role.all()]
192+
session["user_org_ids"] = [org.id for org in user.organization.all()]
193+
roles = [i > 1 for i in session["user_role_ids"]]
194+
session["can_edit"] = True if all(roles) and roles else []
195+
180196
return app
197+

flowapp/auth.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ def auth_required(f):
1414
@wraps(f)
1515
def decorated(*args, **kwargs):
1616
if not check_auth(get_user()):
17-
return redirect("/login")
17+
if current_app.config.get("SSO_AUTH"):
18+
return redirect("/login")
19+
elif current_app.config.get("HEADER_AUTH", False):
20+
return redirect("/ext-login")
1821
return f(*args, **kwargs)
1922

2023
return decorated
@@ -99,6 +102,12 @@ def check_auth(uuid):
99102
if uuid:
100103
exist = db.session.query(User).filter_by(uuid=uuid).first()
101104
return exist
105+
elif current_app.config.get("HEADER_AUTH", False):
106+
# External auth (for example apache)
107+
header_name = current_app.config.get("AUTH_HEADER_NAME", 'X-Authenticated-User')
108+
if header_name not in request.headers or not session.get("user_uuid"):
109+
return False
110+
return db.session.query(User).filter_by(uuid=request.headers.get(header_name))
102111
else:
103112
# Localhost login / no check
104113
session["user_email"] = current_app.config["LOCAL_USER_UUID"]

flowapp/templates/errors/401.j2

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% extends 'layouts/default.j2' %}
2+
{% block content %}
3+
<h1>Could not log you in.</h1>
4+
<p class="form-text">401: Unauthorized</p>
5+
<p>Please log out and try logging in again.</p>
6+
<p><a href="{{url_for('logout')}}">Log out</a></p>
7+
{% endblock %}

0 commit comments

Comments
 (0)