beautify, hashed passwords and session ids

This commit is contained in:
Denis Lehmann 2022-04-14 00:14:37 +02:00
parent fbe71b389e
commit 7e4a7ad38f
5 changed files with 122 additions and 27 deletions

View file

@ -1,7 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
import crypt
import toml import toml
import uuid
import werkzeug import werkzeug
from datetime import datetime, timedelta
from hmac import compare_digest as compare_hash
from flask import ( from flask import (
Flask, Flask,
render_template, render_template,
@ -16,14 +20,46 @@ from werkzeug.utils import secure_filename
base_path = Path("public") base_path = Path("public")
cloud_name = "raincloud" 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 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): def get_config(directory):
"""Load a 'mincloud.conf' file from given directory. """Load a 'rc.toml' file from given directory.
Parameters: Parameters:
directory - basepath of 'mincloud.conf' directory - basepath of 'mincloud.conf'
@ -37,8 +73,10 @@ def get_config(directory):
config["directory"] = directory config["directory"] = directory
parsed_config = toml.load(path) parsed_config = toml.load(path)
config["password"] = ( config["hashed_password"] = (
parsed_config["password"] if "password" in parsed_config else None parsed_config["hashed_password"]
if "hashed_password" in parsed_config
else None
) )
config["download"] = ( config["download"] = (
parsed_config["download"] if "download" in parsed_config else False parsed_config["download"] if "download" in parsed_config else False
@ -49,7 +87,7 @@ def get_config(directory):
return config return config
else: else:
raise MincloudIOException("No raincloud directory") raise RaincloudIOException("No raincloud directory")
def get_files(directory): def get_files(directory):
@ -68,16 +106,37 @@ app = Flask(__name__)
@app.route("/<directory>", methods=["GET", "POST"]) @app.route("/<directory>", methods=["GET", "POST"])
@app.route("/<directory>/<path:filename>", methods=["GET"]) @app.route("/<directory>/<path:filename>", methods=["GET"])
def directory(directory, filename=None): def directory(directory, filename=None):
global sessions
try: 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) config = get_config(directory)
if config["password"]: if config["hashed_password"]:
authenticated = True if directory in session and session[directory] == config["password"] else False authenticated = (
True
if directory in session
and validate_session(directory, session[directory])
else False
)
if not authenticated: if not authenticated:
if request.method == "POST": if request.method == "POST":
if request.form["password"] == config["password"]: if compare_hash(
session[directory] = config["password"] 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)) return redirect(url_for("directory", directory=directory))
else: else:
return render_template( return render_template(
@ -112,7 +171,7 @@ def directory(directory, filename=None):
else: else:
return "No upload allowed" return "No upload allowed"
except MincloudIOException as e: except RaincloudIOException as e:
print(e) print(e)
return "No 404 file" return "No 404 file"

View file

@ -9,16 +9,18 @@ body {
} }
#header { #header {
padding: 15px 30px; padding: 0px 30px;
border-bottom: 1px solid lightgray; border-bottom: 1px solid lightgray;
font-size: 20px; font-size: 20px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
min-height: 60px;
} }
#cloud-name { #cloud-name {
color: #28bcff;
margin-right: 30px;
} }
#directory-name { #directory-name {
@ -26,17 +28,23 @@ body {
} }
#content { #content {
margin: 50px 150px; margin: 50px 0px;
text-align: center;
} }
.file { .file {
border-bottom: 1px solid lightgrey; border-bottom: 1px solid lightgrey;
padding: 5px 15px; padding: 0px 30px;
min-height: 60px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
form {
display: inline-block;
}
.button { .button {
background-color: #28bcff; background-color: #28bcff;
border: none; border: none;
@ -55,14 +63,36 @@ body {
background-color: #0cb3ff; background-color: #0cb3ff;
} }
.upload {
background-color: #ff814e;
}
.upload:hover {
background-color: #ff7740;
}
.logout {
background-color: #ff4e62;
}
.logout:hover {
background-color: #ff3e54;
}
input[type="file"] { input[type="file"] {
display: none; display: none;
} }
.upload { input[type="password"] {
background-color: #4bcd8f; border: 1px solid lightgrey;
padding: 11px 11px;
font-size: 16px;
border-radius: 5px;
width: 300px;
} }
.upload:hover { input[type="password"]:focus {
background-color: #3bc885; outline: none;
border: 2px solid #ff814e;
padding: 10px 10px;
} }

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<form action="/{{ config["directory"] }}" method="post"> <form action="/{{ config["directory"] }}" method="post">
<input type="password" name="password"> <input type="password" name="password" placeholder="Password">
<input type="submit"> <input type="submit" class="button" value="Authenticate">
</form> </form>
{% endblock %} {% endblock %}

View file

@ -2,11 +2,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}"> <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}">
<title>{{ cloud_name }} :: {{ config["directory"] }}</title> <title>{{ config["directory"] }} | {{ cloud_name }}</title>
<div id="container"> <div id="container">
<div id="header"> <div id="header">
<div> <div>
<span id="cloud-name">{{ cloud_name }} :: </span><span id="directory-name">{{ config["directory"] }}</span> <span id="cloud-name">{{ cloud_name }}</span><span id="directory-name">{{ config["directory"] }}</span>
</div> </div>
{% block nav_content %}{% endblock %} {% block nav_content %}{% endblock %}
</div> </div>

View file

@ -1,15 +1,21 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block nav_content %} {% block nav_content %}
{% if config["upload"] %} <div>
<div> {% if config["upload"] %}
<form action="/{{ config["directory"] }}" enctype="multipart/form-data" method="post"> <form action="/{{ config["directory"] }}" enctype="multipart/form-data" method="post">
<label class="button upload"> <label class="button upload">
<input type="file" name="file" onchange="this.form.submit()"> <input type="file" name="file" onchange="this.form.submit()">
Upload Upload
</label> </label>
</form> </form>
</div> {% endif %}
{% endif %} {% if config["hashed_password"] %}
<form action="/{{ config["directory"] }}" method="post">
<input hidden type="text" name="logout">
<input type="submit" class="button logout" value="Logout">
</form>
{% endif %}
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% for f in files %} {% for f in files %}