#!/usr/bin/python # -*- coding: UTF-8 -*- from datetime import timedelta from uuid import uuid4 import os from flask import Flask from flask import jsonify from flask import render_template, send_from_directory from flask import request from flask import session from flask import url_for from flask_compress import Compress from flask_session import Session from werkzeug import redirect import pyotp from functions import pbkdf2 import config ################################################################################ app = Flask( __name__ ) app.config.from_pyfile( 'config.py' ) Compress( app ) Session( app ) debug = os.environ.get( "DEBUG", False ) baseurl = os.environ.get( "BASEURL", "" ) ################################################################################ # Generic routing @app.route( baseurl + '/ping' ) def ping(): return "pong" ################################################################################ # App serving @app.route( baseurl + '/app/<path>' ) def send_app_files( path ): return send_from_directory( 'app', path ) ################################################################################ # Sessions @app.before_request def renew_session(): session.permanent = True app.permanent_session_lifetime = timedelta( seconds = config.session_timeout ) @app.route( baseurl + '/logout' ) def logout(): session.clear() return redirect( url_for( 'home' ) ) @app.route( baseurl + '/login' ) def login(): session.clear() session[ 'stage' ] = 'password' return render_template( "login.html", baseurl = baseurl, js = config.cdnjs, css = config.cdncss ) @app.route( baseurl + '/do_login', methods = [ 'POST' ] ) def do_login(): if session[ 'stage' ] == 'password' or not 'stage' in session: q = config.db.query( 'SELECT * FROM users WHERE username = %s', ( request.form.get( "username" ), ) ) user = q.fetchone() if user == None: return jsonify( { 'error': False, 'logged': False } ) form_password = request.form.get( "password", None ) if form_password == None or not pbkdf2( form_password, user[ 'password' ] ): return jsonify( { 'error': False, 'logged': False, } ) elif not user[ 'active' ]: return jsonify( { 'error': False, 'logged': False, 'message': 'Your account is not activated. Please contact an administrator.' } ) else: session[ 'session_id' ] = str( uuid4() ) session[ 'username' ] = user[ 'username' ] session[ 'password_check' ] = 'ok' if user[ 'must_use_totp' ]: session[ 'stage' ] = 'totp' return jsonify( { 'error': False, 'must_use_totp': True } ) else: session[ 'logged' ] = True elif session[ 'stage' ] == 'totp': q = config.db.query( 'SELECT username, totp FROM users WHERE username = %s', ( session[ 'username' ], ) ) user = q.fetchone() if not pyotp.TOTP( user[ 'totp' ] ).verify( request.form[ "totp" ], valid_window = 1 ): return jsonify( { 'error': False, 'logged': False, 'message': 'Wrong TOTP' } ) else: session[ 'logged' ] = True ############################################################################ if session.get( 'logged', False ) and session.get( 'username', False ): return jsonify( { 'error': False, 'logged': True, } ) else: session.clear() return jsonify( { 'error': False, 'logged': False, } ) ################################################################################ # Home page @app.route( baseurl + '/' ) def home(): if not session.get( "logged", False ): return redirect( url_for( 'login' ) ) else: return render_template( "index.html", baseurl = baseurl, js = config.cdnjs, css = config.cdncss, session_timeout = config.session_timeout ) ################################################################################ # Main startup if __name__ == '__main__': app.run( debug = debug, host = "0.0.0.0", threaded = True )