71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
import functools
|
|
import ldap
|
|
import flask
|
|
import urllib
|
|
|
|
from webapp import app, context, config, ldaputils
|
|
|
|
bp = flask.Blueprint('auth', __name__)
|
|
|
|
def login_required(f):
|
|
@functools.wraps(f)
|
|
def func(*a, **kw):
|
|
conn = context.get_connection()
|
|
if not conn:
|
|
flask.session.clear()
|
|
flask.flash('You must log in to continue', category='warning')
|
|
return flask.redirect('/login?' + urllib.parse.urlencode({'goto': flask.request.path}))
|
|
return f(*a, **kw)
|
|
return func
|
|
|
|
def req_to_ctx():
|
|
return dict(flask.request.form.items())
|
|
|
|
@bp.route('/login', methods=["GET"])
|
|
def login_form():
|
|
return flask.render_template('login.html', **req_to_ctx())
|
|
|
|
def _connect_to_ldap(dn, password):
|
|
try:
|
|
return app.connections.bind(dn, password)
|
|
except ldap.LDAPError as error_message:
|
|
print("Could not connect to server:", error_message)
|
|
return None
|
|
|
|
@bp.route('/login', methods=["POST"])
|
|
def login_action():
|
|
# LDAP usernames/DNs are case-insensitive, so we normalize them just in
|
|
# case,
|
|
username = flask.request.form.get("username", "").lower()
|
|
password = flask.request.form.get("password", "")
|
|
goto = flask.request.values.get("goto", "/")
|
|
dn = ldaputils.user_dn(username)
|
|
|
|
conn = _connect_to_ldap(dn, password)
|
|
if conn:
|
|
# Now that we have logged in, we can retrieve the 'real' username (which
|
|
# might be cased differently from the login name).
|
|
res = conn.search_s(dn, ldap.SCOPE_SUBTREE)
|
|
for (k, vs) in res[0][1].items():
|
|
if k == 'uid':
|
|
username = vs[0].decode()
|
|
|
|
# Check if user belongs to admin group
|
|
is_admin = bool(conn.search_s(dn, ldap.SCOPE_SUBTREE, ldaputils.member_of_any(config.ldap_admin_groups)))
|
|
|
|
flask.session["username"] = username
|
|
flask.session['dn'] = dn
|
|
flask.session['is_admin'] = is_admin
|
|
context.refresh_profile()
|
|
return flask.redirect(goto)
|
|
else:
|
|
flask.flash("Invalid credentials.", category='danger')
|
|
return login_form()
|
|
|
|
@bp.route('/logout')
|
|
@login_required
|
|
def logout_action():
|
|
app.connections.unbind(flask.session['dn'])
|
|
flask.session.clear()
|
|
return flask.redirect('/')
|