aboutsummaryrefslogtreecommitdiff
path: root/stackbin_auth.py
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2022-02-15 16:44:06 -0500
committerB Stack <bgstack15@gmail.com>2022-02-15 16:44:06 -0500
commitbee4591ac60f8f3bd85c2f2b12dc75e09e2c97d7 (patch)
tree053806ad9cb454717d362704184c9d6c8cf6ac85 /stackbin_auth.py
parentpw-protect /admin endpoint (diff)
downloadstackbin-bee4591ac60f8f3bd85c2f2b12dc75e09e2c97d7.tar.gz
stackbin-bee4591ac60f8f3bd85c2f2b12dc75e09e2c97d7.tar.bz2
stackbin-bee4591ac60f8f3bd85c2f2b12dc75e09e2c97d7.zip
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.
Diffstat (limited to 'stackbin_auth.py')
-rw-r--r--stackbin_auth.py136
1 files changed, 136 insertions, 0 deletions
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'<meta http-equiv="Refresh" content="4; url={url_for("auth.login")}">Unauthorized! Invalid admin credential... returning to login form', 401)
+
+@auth.route("/logout")
+@auth.route("/logout/")
+def logout():
+ resp = Response(f'<meta http-equiv="Refresh" content="1; url={url_for("new_paste")}">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'<meta http-equiv="Refresh" content="1; url={url_for("admin")}">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
bgstack15