diff --git a/.gitignore b/.gitignore index bb179fe..e3e3d16 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__/ secret_key *.db *.swp +config.py diff --git a/buckler_flask.py b/buckler_flask.py new file mode 100644 index 0000000..0ed5429 --- /dev/null +++ b/buckler_flask.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +""" +Session interface middlewares to integrate the flask app with Buckler. +""" +import json +import urllib.parse +import urllib.request +from datetime import datetime + +from flask.sessions import SessionInterface, SessionMixin +from flask import session, redirect, request + +import config + +class BucklerSessionInterface(SessionInterface): + """ + Queries the Buckler server for session data to the current user and + application. + """ + + def __init__(self): + self.url = config.buckler['url'] + self.app_id = config.buckler['app_id'] + self.app_key = config.buckler['app_key'] + + def open_session(self, app, request): + """Called when a request is initiated.""" + user_id = request.cookies.get('userid') + user_sid = request.cookies.get('session') + + params = { + 'app_id': self.app_id, + 'app_key': self.app_key, + 'userid': user_id, + 'session': user_sid + } + params = urllib.parse.urlencode(params) + req = urllib.request.Request(self.url + f"/get_session?{params}") + res = urllib.request.urlopen(req) + data = json.loads(res.read()) + if data.get('error'): + return None + session = BucklerSession() + session.update(data['session_data']) + session.meta = data['meta'] + session.cookies = request.cookies + return session + + def save_session(self, app, session, response): + """Called at the end of a request.""" + if not session.modified: + return + user_id = session.meta.get('user_id') + user_sid = session.meta.get('user_sid') + + params = { + 'app_id': self.app_id, + 'app_key': self.app_key, + 'userid': user_id, + 'session': user_sid + } + params = urllib.parse.urlencode(params) + data = json.dumps(session) + req = urllib.request.Request( + self.url + f"/set_session?{params}", + data=data.encode('utf8'), + method='POST') + res = urllib.request.urlopen(req) + + last_used = datetime.fromisoformat(session.meta['last_used']) + now = datetime.now(last_used.tzinfo) + delta = now - last_used + if delta.seconds > 600: + response.set_cookie( + 'userid', + session.cookies['userid'], + max_age=30*24*60*60, + secure=True, + httponly=True) + response.set_cookie( + 'session', + session.cookies['session'], + max_age=30*24*60*60, + secure=True, + httponly=True) + + +class BucklerSession(dict, SessionMixin): + """A server side session class based on the Buckler security shield.""" + def __init__(self): + super().__init__() + self.modified = False + + def __setitem__(self, key, value): + super().__setitem__(key, value) + self.modified = True + + +def require_auth(): + """ + Requires the user to be properly authenticated with Buckler before + accessing any views on the application. + """ + if not hasattr(session, 'meta'): + resp = redirect(config.buckler['login_url']) + resp.set_cookie('redirect', request.url) + return resp diff --git a/config.py.template b/config.py.template new file mode 100755 index 0000000..9a7c97f --- /dev/null +++ b/config.py.template @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +""" +Configuration settings for fileHost. +`buckler` specifies settings pertaining to the Buckler server. +""" +buckler = { + 'url': "http://127.0.0.1:5400/buckler", + 'app_id': 1, + 'app_key': """password""", + 'login_url': "/buckler/login", +} diff --git a/fileHost.py b/fileHost.py index 7d4cf92..9354d77 100755 --- a/fileHost.py +++ b/fileHost.py @@ -23,7 +23,11 @@ from flask_paranoid import Paranoid from apscheduler.schedulers.background import BackgroundScheduler import requests +import buckler_flask + app = Flask(__name__) +app.session_interface = buckler_flask.BucklerSessionInterface() +app.before_request(buckler_flask.require_auth) app.config['MAX_CONTENT_LENGTH'] = 128 * 1024 * 1024 app.config["UPLOAD_DIR"] = "/var/www/html/up" app.config["UPLOAD_URL"] = "https://steelbea.me/up/" @@ -163,7 +167,7 @@ def login_required(url=None): def actual_decorator(func): @functools.wraps(func) def _nop(*args, **kwargs): - username = session.get("username") + username = session.meta['username'] if verify_username(username): return func(*args, **kwargs) @@ -186,7 +190,7 @@ def deleteFile(): """ Allows a user to delete a file from the upload directory and the database. """ - username = session.get("username") + username = session.meta['username'] filename = request.form.get("fname") if not verify_username(username): @@ -238,7 +242,7 @@ def logout(): @app.route("/change_password", methods=["POST", "GET"]) -@login_required() +#@login_required() def change_password(): """ Allows the user to change their password. @@ -246,7 +250,7 @@ def change_password(): if request.method == "GET": return render_template("change_password.html") - username = session.get("username") + username = session.meta['username'] current_password = request.form.get("current_password") new_password = request.form.get("new_password") new_password_verify = request.form.get("new_password_verify") @@ -283,12 +287,12 @@ def login(): @app.route("/manage_uploads", methods=["POST", "GET"]) -@login_required() +#@login_required() def manage_uploads(): """ Allows the user to view and/or delete uploads they've made. """ - username = session.get("username") + username = session.meta['username'] if request.method == "GET": uploads = db_execute( "SELECT filename, uploaded_date FROM uploads WHERE uploaded_by = ?", @@ -333,12 +337,12 @@ def gallery(username): @app.route("/upload", methods=["POST"]) -@login_required("login") +#@login_required("login") def upload(): """ Saves the uploaded files and returns URLs pointing to them. """ - username = session.get("username") + username = session.meta['username'] if request.form.get("url"): try: @@ -397,7 +401,7 @@ def upload(): @app.route("/", methods=["GET", "POST"]) -@login_required("login") +#@login_required("login") def index(): """ Saves the uploaded file and returns a URL pointing to it. diff --git a/templates/index.html b/templates/index.html index fadb760..acb930f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4,11 +4,11 @@ Let's uploading boys! - Hello, {{ session.username }}
+ Hello, {{ session.meta['username'] }}
Change password
Logout
Manage Uploads
- Public Gallery
+ Public Gallery

Select file to upload: