From 7e4a7ad38f9ce4723a8f01f617711f88665d4446 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Thu, 14 Apr 2022 00:14:37 +0200 Subject: [PATCH] beautify, hashed passwords and session ids --- raincloud.py | 81 ++++++++++++++++++++++++++++++++----- static/style.css | 46 +++++++++++++++++---- templates/authenticate.html | 4 +- templates/base.html | 4 +- templates/directory.html | 14 +++++-- 5 files changed, 122 insertions(+), 27 deletions(-) diff --git a/raincloud.py b/raincloud.py index a01f616..44d6585 100755 --- a/raincloud.py +++ b/raincloud.py @@ -1,7 +1,11 @@ #!/usr/bin/env python +import crypt import toml +import uuid import werkzeug +from datetime import datetime, timedelta +from hmac import compare_digest as compare_hash from flask import ( Flask, render_template, @@ -16,14 +20,46 @@ from werkzeug.utils import secure_filename base_path = Path("public") cloud_name = "raincloud" +sessions = [] + +# mkpass = "$6$7lTWfEYgx.nZdM.C$jb2cQt30FzpnEibp2sN2juGL0sGT2Y9dGlVTQvqxBB579Yy5lfbt3tIHjKYnt/MIcff6I6AFp8Q5k1xjN9C8a0" +# print(compare_hash(mkpass, crypt.crypt("test", mkpass))) +# exit(1) -class MincloudIOException(Exception): +class RaincloudIOException(Exception): pass +def clean_sessions(): + global sessions + sessions = [s for s in sessions if s[0] > datetime.now() - timedelta(days=1)] + + +def validate_session(directory, id_): + global sessions + valid_dates = [s[0] for s in sessions if s[1] == directory and s[2] == id_] + if len(valid_dates) > 0 and valid_dates[0] > datetime.now() - timedelta(days=1): + return True + return False + + +def delete_session(id_): + global sessions + sessions = [s for s in sessions if s[2] != id_] + + +def get_session_id(): + global sessions + ids = [s[2] for s in sessions] + id_ = uuid.uuid4() + while id_ in ids: + id_ = uuid.uuid4() + return id_ + + def get_config(directory): - """Load a 'mincloud.conf' file from given directory. + """Load a 'rc.toml' file from given directory. Parameters: directory - basepath of 'mincloud.conf' @@ -37,8 +73,10 @@ def get_config(directory): config["directory"] = directory parsed_config = toml.load(path) - config["password"] = ( - parsed_config["password"] if "password" in parsed_config else None + config["hashed_password"] = ( + parsed_config["hashed_password"] + if "hashed_password" in parsed_config + else None ) config["download"] = ( parsed_config["download"] if "download" in parsed_config else False @@ -49,7 +87,7 @@ def get_config(directory): return config else: - raise MincloudIOException("No raincloud directory") + raise RaincloudIOException("No raincloud directory") def get_files(directory): @@ -68,16 +106,37 @@ app = Flask(__name__) @app.route("/", methods=["GET", "POST"]) @app.route("//", methods=["GET"]) def directory(directory, filename=None): - + global sessions try: + + # Clean sessions + clean_sessions() + + # Logout + if request.method == "POST" and "logout" in request.form: + delete_session(session[directory]) + return redirect(url_for("directory", directory=directory)) + config = get_config(directory) - if config["password"]: - authenticated = True if directory in session and session[directory] == config["password"] else False + if config["hashed_password"]: + authenticated = ( + True + if directory in session + and validate_session(directory, session[directory]) + else False + ) if not authenticated: if request.method == "POST": - if request.form["password"] == config["password"]: - session[directory] = config["password"] + if compare_hash( + config["hashed_password"], + crypt.crypt( + request.form["password"], config["hashed_password"] + ), + ): + id_ = get_session_id() + session[directory] = id_ + sessions.append((datetime.now(), directory, id_)) return redirect(url_for("directory", directory=directory)) else: return render_template( @@ -112,7 +171,7 @@ def directory(directory, filename=None): else: return "No upload allowed" - except MincloudIOException as e: + except RaincloudIOException as e: print(e) return "No 404 file" diff --git a/static/style.css b/static/style.css index cd962d0..3cfbde0 100644 --- a/static/style.css +++ b/static/style.css @@ -9,16 +9,18 @@ body { } #header { - padding: 15px 30px; + padding: 0px 30px; border-bottom: 1px solid lightgray; font-size: 20px; display: flex; justify-content: space-between; align-items: center; + min-height: 60px; } #cloud-name { - + color: #28bcff; + margin-right: 30px; } #directory-name { @@ -26,17 +28,23 @@ body { } #content { - margin: 50px 150px; + margin: 50px 0px; + text-align: center; } .file { border-bottom: 1px solid lightgrey; - padding: 5px 15px; + padding: 0px 30px; + min-height: 60px; display: flex; justify-content: space-between; align-items: center; } +form { + display: inline-block; +} + .button { background-color: #28bcff; border: none; @@ -55,14 +63,36 @@ body { background-color: #0cb3ff; } +.upload { + background-color: #ff814e; +} + +.upload:hover { + background-color: #ff7740; +} + +.logout { + background-color: #ff4e62; +} + +.logout:hover { + background-color: #ff3e54; +} + input[type="file"] { display: none; } -.upload { - background-color: #4bcd8f; +input[type="password"] { + border: 1px solid lightgrey; + padding: 11px 11px; + font-size: 16px; + border-radius: 5px; + width: 300px; } -.upload:hover { - background-color: #3bc885; +input[type="password"]:focus { + outline: none; + border: 2px solid #ff814e; + padding: 10px 10px; } diff --git a/templates/authenticate.html b/templates/authenticate.html index 66f98c7..bc89042 100644 --- a/templates/authenticate.html +++ b/templates/authenticate.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %}
- - + +
{% endblock %} diff --git a/templates/base.html b/templates/base.html index e55b4de..a528ba4 100644 --- a/templates/base.html +++ b/templates/base.html @@ -2,11 +2,11 @@ -{{ cloud_name }} :: {{ config["directory"] }} +{{ config["directory"] }} | {{ cloud_name }}
diff --git a/templates/directory.html b/templates/directory.html index 4490bda..5b2c985 100644 --- a/templates/directory.html +++ b/templates/directory.html @@ -1,15 +1,21 @@ {% extends "base.html" %} {% block nav_content %} - {% if config["upload"] %} -
+
+ {% if config["upload"] %}
-
- {% endif %} + {% endif %} + {% if config["hashed_password"] %} +
+ + +
+ {% endif %} +
{% endblock %} {% block content %} {% for f in files %}