use buckler for security

This commit is contained in:
iou1name 2019-09-29 14:49:06 -04:00
parent 318517514c
commit 0df4d14bdf
5 changed files with 134 additions and 11 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ __pycache__/
secret_key secret_key
*.db *.db
*.swp *.swp
config.py

107
buckler_flask.py Normal file
View File

@ -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

11
config.py.template Executable file
View File

@ -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",
}

View File

@ -23,7 +23,11 @@ from flask_paranoid import Paranoid
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
import requests import requests
import buckler_flask
app = Flask(__name__) 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['MAX_CONTENT_LENGTH'] = 128 * 1024 * 1024
app.config["UPLOAD_DIR"] = "/var/www/html/up" app.config["UPLOAD_DIR"] = "/var/www/html/up"
app.config["UPLOAD_URL"] = "https://steelbea.me/up/" app.config["UPLOAD_URL"] = "https://steelbea.me/up/"
@ -163,7 +167,7 @@ def login_required(url=None):
def actual_decorator(func): def actual_decorator(func):
@functools.wraps(func) @functools.wraps(func)
def _nop(*args, **kwargs): def _nop(*args, **kwargs):
username = session.get("username") username = session.meta['username']
if verify_username(username): if verify_username(username):
return func(*args, **kwargs) return func(*args, **kwargs)
@ -186,7 +190,7 @@ def deleteFile():
""" """
Allows a user to delete a file from the upload directory and the database. 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") filename = request.form.get("fname")
if not verify_username(username): if not verify_username(username):
@ -238,7 +242,7 @@ def logout():
@app.route("/change_password", methods=["POST", "GET"]) @app.route("/change_password", methods=["POST", "GET"])
@login_required() #@login_required()
def change_password(): def change_password():
""" """
Allows the user to change their password. Allows the user to change their password.
@ -246,7 +250,7 @@ def change_password():
if request.method == "GET": if request.method == "GET":
return render_template("change_password.html") return render_template("change_password.html")
username = session.get("username") username = session.meta['username']
current_password = request.form.get("current_password") current_password = request.form.get("current_password")
new_password = request.form.get("new_password") new_password = request.form.get("new_password")
new_password_verify = request.form.get("new_password_verify") new_password_verify = request.form.get("new_password_verify")
@ -283,12 +287,12 @@ def login():
@app.route("/manage_uploads", methods=["POST", "GET"]) @app.route("/manage_uploads", methods=["POST", "GET"])
@login_required() #@login_required()
def manage_uploads(): def manage_uploads():
""" """
Allows the user to view and/or delete uploads they've made. Allows the user to view and/or delete uploads they've made.
""" """
username = session.get("username") username = session.meta['username']
if request.method == "GET": if request.method == "GET":
uploads = db_execute( uploads = db_execute(
"SELECT filename, uploaded_date FROM uploads WHERE uploaded_by = ?", "SELECT filename, uploaded_date FROM uploads WHERE uploaded_by = ?",
@ -333,12 +337,12 @@ def gallery(username):
@app.route("/upload", methods=["POST"]) @app.route("/upload", methods=["POST"])
@login_required("login") #@login_required("login")
def upload(): def upload():
""" """
Saves the uploaded files and returns URLs pointing to them. Saves the uploaded files and returns URLs pointing to them.
""" """
username = session.get("username") username = session.meta['username']
if request.form.get("url"): if request.form.get("url"):
try: try:
@ -397,7 +401,7 @@ def upload():
@app.route("/", methods=["GET", "POST"]) @app.route("/", methods=["GET", "POST"])
@login_required("login") #@login_required("login")
def index(): def index():
""" """
Saves the uploaded file and returns a URL pointing to it. Saves the uploaded file and returns a URL pointing to it.

View File

@ -4,11 +4,11 @@
<title>Let's uploading boys!</title> <title>Let's uploading boys!</title>
</head> </head>
<body> <body>
Hello, {{ session.username }}<br> Hello, {{ session.meta['username'] }}<br>
<a href="{{ url_for('change_password') }}">Change password</a><br> <a href="{{ url_for('change_password') }}">Change password</a><br>
<a href="{{ url_for('logout') }}">Logout</a><br> <a href="{{ url_for('logout') }}">Logout</a><br>
<a href="{{ url_for('manage_uploads') }}">Manage Uploads</a><br> <a href="{{ url_for('manage_uploads') }}">Manage Uploads</a><br>
<a href="{{ url_for('gallery', username=session.username) }}">Public Gallery</a><br> <a href="{{ url_for('gallery', username=session.meta['username']) }}">Public Gallery</a><br>
<form method="post" enctype="multipart/form-data" action="{{ url_for('upload') }}"> <form method="post" enctype="multipart/form-data" action="{{ url_for('upload') }}">
<p>Select file to upload: <p>Select file to upload:
<p><input type="file" name="files" multiple><br> <p><input type="file" name="files" multiple><br>