from functools import wraps from flask import Blueprint, Flask, Response, request, url_for, redirect, g, render_template, session, abort, current_app auth = Blueprint('auth', __name__) def requires_session(function): ''' Requires a valid session, provided by cookie! ''' @wraps(function) def decorated(*args, **kwargs): if not session: #return Response("requires session",401) return redirect(url_for('auth.login')) else: if 'user' not in session: return Response("User is not in this session.",401) s_user = session['user'] c_user = request.cookies.get('user') #print(f"session user: {s_user}") #print(f"cookie user: {c_user}") if session['user'] != c_user: return Response("Wrong user for this session!.",401) # otherwise, everything is good! return function(s_user, [], *args,**kwargs) # catch-all #return Response("requires session",401) return redirect(url_for('auth.login')) return decorated def _requires_admin_credential(function): """ Requires the user pass the correct admin credential configured in the conf file. """ @wraps(function) def decorated(*args, **kwargs): # formdata is in session if we are coming from login_basic() form = session.get('formdata', None) if form: session.pop('formdata') if 'username' in form: username = form['username'] if 'password' in form: pw = form['password'] else: # then we are coming from the form with POST data if 'username' not in request.form or 'password' not in request.form: return _unauthorized_admin() username = request.form['username'] pw = request.form['password'] if 'ADMIN_USERNAME' in current_app.config and \ 'ADMIN_PASSWORD' in current_app.config and \ username == current_app.config['ADMIN_USERNAME'] and pw == current_app.config['ADMIN_PASSWORD']: return function(username, [], *args, **kwargs) else: return _unauthorized_admin() return decorated def _unauthorized_admin(): return Response(f'Unauthorized! Invalid admin credential... returning to login form', 401) @auth.route("/logout") @auth.route("/logout/") def logout(): resp = Response(f'logged out') # not documented but is found on the Internet in a few random places: session.clear() #resp.set_cookie('user','',expires=0) return resp @auth.route("/login/new") @auth.route("/login/new/") def login_new(): return redirect(url_for("auth.login", new="")) @auth.route("/login/", methods=['POST','GET']) def login(user="None"): if request.method == "GET": if 'user' in session and request.cookies.get('user') == session['user'] and (not 'new' in request.args): return redirect(url_for("admin")) auth_header = request.headers.get("Authorization") # default, show login form return redirect(url_for("auth.login_form")) elif request.method == "POST": if request.authorization: return redirect(url_for("auth.login_basic"),code=307) return redirect(url_for("auth.login_generic")) #return f"Authentication method not supported yet.",400 @auth.route("/login/basic",methods=['POST','GET']) @auth.route("/login/basic/",methods=['POST','GET']) def login_basic(): if not request.authorization: return Response(f"Please provide username and password.",401,{'WWW-Authenticate': 'Basic'}) if 'username' not in request.authorization: return Response(f"No username provided.",401) if 'password' not in request.authorization: return Response(f"No password provided.",401) username = request.authorization.username pw = request.authorization.password form={'username':username,'password':pw} session['formdata'] = form return redirect(url_for("auth.login_generic"),code=307) @auth.route("/login/form", methods=['POST','GET']) @auth.route("/login/form/", methods=['POST','GET']) def login_form(): if request.method == "GET": return render_template("login_form.html", login_url = url_for("auth.login_form") ) else: # assume it is a POST username="" if 'username' in request.form: username = request.form['username'] password="" if 'password' in request.form: password = request.form['password'] form={'username':username,'password':password} session['formdata'] = form return redirect(url_for("auth.login_generic"), code=307) @auth.route("/login/generic", methods=['POST','GET']) @auth.route("/login/generic/", methods=['POST','GET']) @_requires_admin_credential def login_generic(user,groups=[]): resp = Response(f'success') session['user_id'] = "admin" resp = login_success(session,resp,user,groups) return resp def login_success(session,resp,user,groups=[]): resp.set_cookie('user',user) session.permanent = True session['user']=user return resp