Skip to content
Snippets Groups Projects
app.py 2.98 KiB
Newer Older
from flask import abort, Flask, redirect, request, render_template, url_for
Mark's avatar
Mark committed
from os import urandom, environ
Mark's avatar
Mark committed
from hydra_client import HydraAdmin
Mark's avatar
Mark committed
from flask_login import login_user, logout_user, LoginManager, login_required, current_user
from db import User
Mark's avatar
Mark committed
from forms import LoginForm, LogoutForm
Mark's avatar
Mark committed

HYDRA_ADMIN_URL = environ['HYDRA_ADMIN_URL']
Mark's avatar
Mark committed
hydra = HydraAdmin(HYDRA_ADMIN_URL)
Mark's avatar
Mark committed

app = Flask(__name__)
Mark's avatar
Mark committed
app.config['SECRET_KEY'] = urandom(16)
Mark's avatar
Mark committed
app.debug = True if "DEBUG" in environ and environ["DEBUG"] else False
Mark's avatar
Mark committed

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

@login_manager.user_loader
def user_loader(username):
    user = User(username)
    if not user.active:
        return
    return user
Mark's avatar
Mark committed

Mark's avatar
Mark committed
@app.route('/', methods=['GET'])
Mark's avatar
Mark committed
@login_required
def home():
Mark's avatar
Mark committed
    """Accepts hydra login challenge or renders welcome page

    Connects to the Hydra admin API to accept the login challenge.
    A reference to this challenge object is passed with via args.login_challenge (GET)

    Args:
        login_challenge: A alphanumeric id generated by Hydra, that references a login
            challenge object. The login challenge object can be rejected or accepted via the
            hydra admin API

    Returns:
        Redirect that is saved in the challenge object
        If no challenge reference was passed this function renders a welcome page
    """
Mark's avatar
Mark committed
    logout_form = LogoutForm()
Mark's avatar
Mark committed
    challenge = request.args.get("login_challenge")
    if not challenge:
        return render_template('home.html', email=current_user.email, logout_form=logout_form)
    else:
Mark's avatar
Mark committed
        redirect_to = hydra.login_request(challenge).accept(current_user.username)
Mark's avatar
Mark committed
        return redirect(redirect_to)
Mark's avatar
Mark committed

@app.route('/login', methods=['GET', 'POST'])
def login():
Mark's avatar
Mark committed
    """Provides login form and handles Login attempt

    Args:
        login_form: contains login data submitted by a user (POST)
        next_url: url that this function redirects to after logging in the user

    Returns:
        Error page in case the login was unsuccessful
        Redirect to home page, forwarding the login_challenge in case login was successful
    """
    login_form = LoginForm()
    if login_form.validate_on_submit():
Mark's avatar
Mark committed
        user = User(login_form.username.data)
        if user.authenticate(login_form.password.data):
            login_user(user)
Mark's avatar
Mark committed
        next_url = login_form.next_url.data
        if not is_safe_url(next_url):
            return abort(400)
        return redirect(next_url or url_for('home'))
Mark's avatar
Mark committed
    login_form.next_url.data = request.args.get('next')
    return render_template('login.html', login_form=login_form)

def is_safe_url(url):
Mark's avatar
Mark committed
    safe = True if url == "" else False
    safe = True if url == "/" or safe else False
    safe = True if url[:18] == "/?login_challenge=" \
                   and url[18:].isalnum() or safe else False
    return safe
Mark's avatar
Mark committed
@app.route('/logout', methods=['POST', 'GET'])
Mark's avatar
Mark committed
def logout():
Mark's avatar
Mark committed
    logout_user()
    return redirect(url_for('home'))
Mark's avatar
Mark committed

if __name__ == '__main__':
    app.run()