Skip to content
Snippets Groups Projects
auth.py 3.03 KiB
Newer Older
Ivan Procaccini's avatar
Ivan Procaccini committed
from urllib.parse import urljoin, urlparse
Ivan Procaccini's avatar
Ivan Procaccini committed

Ivan Procaccini's avatar
Ivan Procaccini committed
from flask import Blueprint, flash, g, render_template, request, url_for
from flask_login import current_user, login_user, logout_user
Ivan Procaccini's avatar
Ivan Procaccini committed
from werkzeug.utils import redirect

Ivan Procaccini's avatar
Ivan Procaccini committed
from .. import ldap_service, login_manager, messages
Ivan Procaccini's avatar
Ivan Procaccini committed
from ..database import db
Ivan Procaccini's avatar
Ivan Procaccini committed
from ..forms.login import LoginForm
from ..ldap_authentication.authenticator import NAME, SURNAME, TITLE
Ivan Procaccini's avatar
Ivan Procaccini committed
from ..models.administration import AuthenticatedUser
Ivan Procaccini's avatar
Ivan Procaccini committed

bp = Blueprint("auth", __name__)


@bp.before_request
def get_current_user():
    g.user = current_user


@login_manager.user_loader
def load_user(user_id):
    return AuthenticatedUser.query.get(user_id)
Ivan Procaccini's avatar
Ivan Procaccini committed


@bp.route("/login", methods=("GET", "POST"))
@bp.route("/", methods=("GET", "POST"))
def login():
    next_pg = request.args.get("next")

    # Redirect to correct home page (according to role)
    if request.method == "GET" and current_user.is_authenticated:
        return redirect(to_next_page_or_home(next_pg))

    # Perform login
    form = LoginForm(request.form)
    if request.method == "POST" and form.validate():
        username, password = (
            request.form.get("username").lower(),
            request.form.get("password"),
        )
        attributes = ldap_service.authenticate(username, password)
        if attributes is None:
            flash(messages.LOGIN_UNSUCCESSFUL_ERROR)
            return render_template("pages/login.html", form=form)
        username = attributes["name"]
        user = AuthenticatedUser.query.filter_by(username=username).first()
Ivan Procaccini's avatar
Ivan Procaccini committed
        if not user:
            user = AuthenticatedUser(
Ivan Procaccini's avatar
Ivan Procaccini committed
                username=username,
                firstname=attributes.get(NAME, username.upper()),
                surname=attributes.get(SURNAME, ""),
                role=normalize_role(attributes.get(TITLE)),
Ivan Procaccini's avatar
Ivan Procaccini committed
            )
            db.session.add(user)
            db.session.commit()
        login_user(user)
        return redirect(to_next_page_or_home(next_pg))

    if form.errors:
        flash(messages.LOGIN_UNSUCCESSFUL_ERROR)

    # User not authenticated tries to 'GET' or 'POST' invalid form
    return render_template("pages/login.html", form=form)


@bp.route("/logout")
def logout():
    if not current_user.is_anonymous:
        db.session.delete(AuthenticatedUser.query.get(current_user.username))
Ivan Procaccini's avatar
Ivan Procaccini committed
        db.session.commit()
    logout_user()
    return redirect(url_for("auth.login"))


##################################################################
# U T I L I T I E S
##################################################################


def normalize_role(ldap_role):
    return "student" if ldap_role in {"Student", "PGT", "Casual"} else "staff"


Ivan Procaccini's avatar
Ivan Procaccini committed
def to_next_page_or_home(next_pg):
    if next_pg and is_safe_url(request.host_url, next_pg):
        return next_pg
    else:
        return url_for(f"{current_user.role}.projects")
Ivan Procaccini's avatar
Ivan Procaccini committed


def is_safe_url(request_host_url, target):
    ref_url = urlparse(request_host_url)
    test_url = urlparse(urljoin(request_host_url, target))
    return test_url.scheme in ("http", "https") and ref_url.netloc == test_url.netloc