From 602127cbdc60ec79263c53ab0833f43da9025cae Mon Sep 17 00:00:00 2001 From: Franziska Kunsmann Date: Tue, 21 Dec 2021 08:30:36 +0100 Subject: [PATCH] initial commit --- .gitignore | 3 + ldap_frontend/__init__.py | 84 ++++++++++++++ ldap_frontend/helpers/ldap.py | 117 ++++++++++++++++++++ ldap_frontend/templates/layout/default.html | 44 ++++++++ ldap_frontend/templates/login.html | 20 ++++ ldap_frontend/templates/selfservice.html | 77 +++++++++++++ requirements.txt | 8 ++ 7 files changed, 353 insertions(+) create mode 100644 .gitignore create mode 100644 ldap_frontend/__init__.py create mode 100644 ldap_frontend/helpers/ldap.py create mode 100644 ldap_frontend/templates/layout/default.html create mode 100644 ldap_frontend/templates/login.html create mode 100644 ldap_frontend/templates/selfservice.html create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b87262 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.venv/ +*.pyc +config.json diff --git a/ldap_frontend/__init__.py b/ldap_frontend/__init__.py new file mode 100644 index 0000000..0e5985a --- /dev/null +++ b/ldap_frontend/__init__.py @@ -0,0 +1,84 @@ +from json import load +from os import environ + +from flask import Flask, flash, redirect, request, session, url_for +from ldap3.core.exceptions import LDAPException + +from .helpers.ldap import login_required, try_auth, get_user, template, update_user + +app = Flask(__name__) +app.secret_key = environ.get("FLASK_SECRET_KEY", default="test") + +with open(environ["APP_CONFIG"]) as f: + APP_CONFIG = load(f) + + +@app.route("/") +def slash(): + return redirect(url_for("login")) + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + session["is_logged_in"] = False + + if request.method == "POST": + if try_auth( + request.form["username"], + request.form["password"], + ): + session["is_logged_in"] = True + session["username"] = request.form["username"] + session["password"] = request.form["password"] + + flash("logged in") + + return redirect(url_for("selfservice")) + else: + flash("username or password is wrong") + + return template(None, "login.html") + + +@app.route("/logout") +def logout(): + session["is_logged_in"] = False + session["username"] = "" + session["password"] = "" + + flash("logged out") + + return redirect(url_for("login")) + + +@app.route("/selfservice", methods=["GET", "POST"]) +@login_required +def selfservice(ldap): + if request.method == "POST": + try: + update_user( + ldap, + session["username"], + { + "givenName": request.form["givenName"], + "sn": request.form["sn"], + "cn": "{} {}".format( + request.form["givenName"], + request.form["sn"], + ), + "mail": request.form["mail"] + }, + ) + flash("data updated") + return redirect(url_for("selfservice")) + except LDAPException as e: + app.logger.error( + "Updating {} failed: {}\n{}".format( + APP_CONFIG["template"]["user_dn"].format(session["username"]), + repr(e), + repr(request.form), + ), + ) + flash(e) + + return template(ldap, "selfservice.html") diff --git a/ldap_frontend/helpers/ldap.py b/ldap_frontend/helpers/ldap.py new file mode 100644 index 0000000..862ff3d --- /dev/null +++ b/ldap_frontend/helpers/ldap.py @@ -0,0 +1,117 @@ +from functools import wraps +from json import load +from os import environ + +from flask import redirect, session, url_for, render_template +from ldap3 import ALL, Connection, Server +from ldap3 import ALL_ATTRIBUTES, MODIFY_REPLACE +from ldap3.core.exceptions import LDAPException + +with open(environ["APP_CONFIG"]) as f: + APP_CONFIG = load(f) + + +def login_required(func): + @wraps(func) + def wrapper(*args, **kwargs): + if session["is_logged_in"]: + if try_auth( + session["username"], + session["password"], + ): + ldap = connect() + + return func(ldap, *args, **kwargs) + else: + return redirect(url_for("login")) + else: + return redirect(url_for("login")) + + return wrapper + + +def admin_required(func): + @wraps(func) + @login_required + def wrapper(*args, **kwargs): + if session["is_logged_in"]: + if try_auth( + session["username"], + session["password"], + ): + ldap = connect() + + return func(ldap, *args, **kwargs) + else: + return redirect(url_for("login")) + else: + return redirect(url_for("login")) + + return wrapper + + +def try_auth(user, password): + try: + connect( + user=APP_CONFIG["template"]["user_dn"].format(user), + password=password, + ) + return True + except LDAPException: + return False + + +def connect(user=None, password=None): + server = Server(APP_CONFIG["ldap"]["server"]) + + if not user and not password: + user = APP_CONFIG["ldap"]["username"] + password = APP_CONFIG["ldap"]["password"] + + conn = Connection( + server, + user=user, + password=password, + ) + conn.bind() + + return conn + + +def get_user(ldap, username): + ldap.search( + APP_CONFIG["ldap"]["user_base"], + APP_CONFIG["template"]["user_search"].format(username), + attributes=ALL_ATTRIBUTES, + ) + if len(ldap.entries) == 1: + return ldap.entries[0] + else: + raise UserNotFoundException(username) + +def update_user(ldap, username, settings): + attrs = {} + for attr, value in settings.items(): + attrs[attr] = [(MODIFY_REPLACE, value)] + + return ldap.modify( + APP_CONFIG["template"]["user_dn"].format(username), + attrs, + ) + + +def template(ldap, name, **kwargs): + user = None + if ldap: + user = get_user(ldap, session["username"]) + + return render_template( + name, + APP_CONFIG=APP_CONFIG, + CURRENT_USER=user, + **kwargs, + ) + + +class UserNotFoundException(Exception): + pass diff --git a/ldap_frontend/templates/layout/default.html b/ldap_frontend/templates/layout/default.html new file mode 100644 index 0000000..9e3d9a8 --- /dev/null +++ b/ldap_frontend/templates/layout/default.html @@ -0,0 +1,44 @@ + + + + {% block title %}LDAP{% endblock %} | {{ APP_CONFIG["title"] }} + + + + + + + + +{% if session.is_logged_in %} + +{% endif %} +
+{% for message in get_flashed_messages() %} + +{% endfor %} +{% block content %} +{% endblock %} +
+ + diff --git a/ldap_frontend/templates/login.html b/ldap_frontend/templates/login.html new file mode 100644 index 0000000..8c4e746 --- /dev/null +++ b/ldap_frontend/templates/login.html @@ -0,0 +1,20 @@ +{% extends "layout/default.html" %} +{% block content %} +
+
+ Login + +
+ + +
+ +
+ + +
+ + +
+
+{% endblock %} diff --git a/ldap_frontend/templates/selfservice.html b/ldap_frontend/templates/selfservice.html new file mode 100644 index 0000000..7fc8e9b --- /dev/null +++ b/ldap_frontend/templates/selfservice.html @@ -0,0 +1,77 @@ +{% extends "layout/default.html" %} +{% block title %}self service{% endblock %} +{% block content %} +
+
+ user data + +
+ +
+ +
contact an administrator if you want to change this
+
+
+ +
+ +
+ +
gets adjusted automatically
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ +
+
+ password + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+{% endblock %} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..46a30a0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +click==8.0.3 +Flask==2.0.2 +itsdangerous==2.0.1 +Jinja2==3.0.3 +ldap3==2.9.1 +MarkupSafe==2.0.1 +pyasn1==0.4.8 +Werkzeug==2.0.2