FLASK mit LDAP Authentication gegen Active Directory und Gruppen Autorisierung für einzelne Seiten

Source: https://gist.github.com/lanbugs/40f94d6a7b849dcd0803179e0a7bb137

Dies ist ein Beispiel um die Anmeldung an einer FLASK Webseite gegen Active Directory zu machen mit LDAP. Zusätzlich gibt es einen Decorator der es ermöglicht einzelne Unterseiten nur für einzelne oder mehrere AD Gruppen zu berechtigen.

Für das Beispiel werden folgende PIP Pakete benötigt: flask, flask-login, ldap3

Die Funktionen etc. sind im Code dokumentiert.

Code Beispiel

#!/usr/bin/env python3

# FLASK with LDAP3 authentication against active directory and authorization check for group membership
# Written by Maximilian Thoma 2023
# Visit: https://lanbugs.de for more ...

from functools import wraps
from flask import Flask, request, redirect, url_for, render_template, abort
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from ldap3 import Server, Connection, SUBTREE, SIMPLE

# LDAP Settings
LDAP_USER = "CN=LDAP Bind,CN=Users,DC=ad,DC=local"
LDAP_PASS = "SuperSecret12345567"
LDAP_SERVER = "ldap://ad01.ad.local"
AD_DOMAIN = "ADLOCAL"
SEARCH_BASE = "CN=Users,DC=ad,DC=local"

# Init Flask
app = Flask(__name__)
app.secret_key = "ThisSecretIsVeryWeakDoItBetter"

# Init LoginManager
login_manager = LoginManager()
login_manager.login_view = "login"
login_manager.init_app(app)


class User(UserMixin):
    """
    The user model
    """
    def __init__(self, username):
        self.id = username
        self.groups = []


def authenticate_ldap(username, password):
    """
    Check authentication of user against AD with LDAP
    :param username: Username
    :param password: Password
    :return: True is authentication is successful, else False
    """
    server = Server(LDAP_SERVER, use_ssl=True)

    try:
        with Connection(server,
                        user=f'{AD_DOMAIN}\\{username}',
                        password=password,
                        authentication=SIMPLE,
                        check_names=True,
                        raise_exceptions=True) as conn:
            if conn.bind():
                print("Authentication successful")
                return True
    except Exception as e:
        print(f"LDAP authentication failed: {e}")
    return False


def get_user_groups(username):
    """
    Connect to LDAP and query for all groups
    :param username: Username
    :return: List of group names
    """
    server = Server(LDAP_SERVER, use_ssl=True)

    with Connection(server,
                    user=LDAP_USER,
                    password=LDAP_PASS,
                    auto_bind=True) as conn:
        search_filter = f'(sAMAccountName={username})'
        conn.search(search_base=SEARCH_BASE,
                    search_filter=search_filter,
                    attributes=['memberOf'],
                    search_scope=SUBTREE)

        if conn.entries:
            user_entry = conn.entries[0]
            group_dns = user_entry.memberOf

            group_names = [group.split(',')[0].split('=')[1] for group in group_dns]

            return group_names

    return []


@login_manager.user_loader
def load_user(user_id):
    """
    The user_loader of flask-login, this will load Usermodel and the groups from AD
    :param user_id: Username
    :return: user object
    """
    user = User(user_id)
    user.groups = get_user_groups(user_id)
    return user


def group_required(groups):
    """
    Decorator to check group membership
    :param groups: list of groups which are allowed to see the site
    """
    def decorator(func):
        @wraps(func)
        def decorated_function(*args, **kwargs):

            for g in groups:
                if current_user.is_authenticated and g in current_user.groups:
                    return func(*args, **kwargs)

            abort(403)
        return decorated_function
    return decorator


@app.route('/login', methods=['GET', 'POST'])
def login():
    """
    Login page
    """
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        if authenticate_ldap(username, password):
            user = User(username)
            login_user(user)
            return redirect(url_for('user_panel'))

    return render_template('login.html')


@app.route('/logout')
@login_required
def logout():
    """
    Logout page
    """
    logout_user()
    return redirect(url_for('login'))


@app.route('/admin')
@login_required
@group_required(["p_admin"])
def admin_panel():
    """
    Protected admin panel, only users of group p_admin are allowed to see the page 
    """
    return 'Admin Panel'


@app.route('/user')
@login_required
@group_required(["p_user", "p_admin"])
def user_panel():
    """
    Protected user panel, only users of group p_user and p_admin are allowed to see the page 
    """
    return 'User Panel'


if __name__ == "__main__":
    app.run(debug=True)

Viel Spaß 🙂

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.