From bee4591ac60f8f3bd85c2f2b12dc75e09e2c97d7 Mon Sep 17 00:00:00 2001 From: B Stack Date: Tue, 15 Feb 2022 16:44:06 -0500 Subject: split auth into separate python file Flask can use Blueprint to load paths from two separate source files, so the endpoints and logic can be grouped logically. --- stackbin_auth.py | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 stackbin_auth.py (limited to 'stackbin_auth.py') diff --git a/stackbin_auth.py b/stackbin_auth.py new file mode 100644 index 0000000..5c12311 --- /dev/null +++ b/stackbin_auth.py @@ -0,0 +1,136 @@ +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 -- cgit