135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
from flask import Flask, render_template, request, redirect, url_for, session
|
||
from functools import wraps
|
||
from datetime import datetime, date, timedelta
|
||
import io
|
||
import os
|
||
import json
|
||
import qrcode
|
||
import base64
|
||
|
||
folder_config = {}
|
||
|
||
def require_secret(f):
|
||
@wraps(f)
|
||
def decorated_function(*args, **kwargs):
|
||
global folder_config
|
||
if not folder_config:
|
||
with open('folder_config.json') as file:
|
||
folder_config = json.load(file)
|
||
|
||
def is_valid(config_item, provided_secret):
|
||
"""
|
||
Checks if today's date is <= validity date
|
||
AND if the provided secret matches config_item['secret'].
|
||
"""
|
||
folder_validity = config_item['validity']
|
||
# Convert string to a date if necessary:
|
||
if isinstance(folder_validity, str):
|
||
folder_validity = datetime.strptime(folder_validity, '%d.%m.%Y').date()
|
||
|
||
# Return whether it's still valid and secrets match:
|
||
return (
|
||
date.today() <= folder_validity and
|
||
provided_secret == config_item['secret']
|
||
)
|
||
|
||
# 1) Get secret from query params (if any)
|
||
args_secret = request.args.get('secret')
|
||
|
||
# 2) Initialize 'allowed_secrets' in the session if missing
|
||
if 'allowed_secrets' not in session:
|
||
session['allowed_secrets'] = []
|
||
|
||
# 3) If a new secret is provided, check if it’s valid, and add to session if so
|
||
if args_secret:
|
||
for config_item in folder_config:
|
||
if is_valid(config_item, args_secret):
|
||
if args_secret not in session['allowed_secrets']:
|
||
session['allowed_secrets'].append(args_secret)
|
||
session.permanent = True # Make the session permanent
|
||
|
||
# 4) Re-check validity of each secret in session['allowed_secrets']
|
||
# If a secret is no longer valid (or not in config), remove it.
|
||
for secret_in_session in session['allowed_secrets'][:]:
|
||
# Find the current config item with matching secret
|
||
config_item = next(
|
||
(c for c in folder_config if c['secret'] == secret_in_session),
|
||
None
|
||
)
|
||
# If the config item doesn’t exist or is invalid, remove secret
|
||
if config_item is None or not is_valid(config_item, secret_in_session):
|
||
session['allowed_secrets'].remove(secret_in_session)
|
||
|
||
# 5) Build session['folders'] fresh from the valid secrets
|
||
session['folders'] = {}
|
||
for secret_in_session in session.get('allowed_secrets', []):
|
||
config_item = next(
|
||
(c for c in folder_config if c['secret'] == secret_in_session),
|
||
None
|
||
)
|
||
if config_item:
|
||
for folder_info in config_item['folders']:
|
||
session['folders'][folder_info['foldername']] = folder_info['folderpath']
|
||
|
||
# 6) If we have folders, proceed; otherwise show index
|
||
if session['folders']:
|
||
# assume since visitor has a valid secret, they are ok with annonymous tracking
|
||
# this is required to track the devices connecting over the same ip address
|
||
if 'device_id' not in session:
|
||
session['device_id'] = os.urandom(32).hex()
|
||
return f(*args, **kwargs)
|
||
else:
|
||
return render_template('index.html')
|
||
|
||
return decorated_function
|
||
|
||
@require_secret
|
||
def mylinks():
|
||
allowed_secrets = session.get('allowed_secrets', [])
|
||
host = request.host
|
||
|
||
secret_qr_codes = {}
|
||
secret_folders = {}
|
||
secret_valid_to = {}
|
||
|
||
# Build a QR code for each secret (using the URL with the secret as query parameter)
|
||
for secret in allowed_secrets:
|
||
url = f"https://{host}?secret={secret}"
|
||
qr = qrcode.QRCode(version=1, box_size=10, border=4)
|
||
qr.add_data(url)
|
||
qr.make(fit=True)
|
||
img = qr.make_image(fill_color="black", back_color="white")
|
||
buffer = io.BytesIO()
|
||
img.save(buffer, format="PNG")
|
||
buffer.seek(0)
|
||
img_base64 = base64.b64encode(buffer.getvalue()).decode('ascii')
|
||
secret_qr_codes[secret] = img_base64
|
||
|
||
# Lookup folder info and valid-to date for this secret from the global folder_config.
|
||
config_item = next((c for c in folder_config if c['secret'] == secret), None)
|
||
if config_item:
|
||
secret_folders[secret] = config_item['folders']
|
||
secret_valid_to[secret] = config_item.get('validity', 'Unbekannt')
|
||
else:
|
||
secret_folders[secret] = []
|
||
secret_valid_to[secret] = 'Unbekannt'
|
||
|
||
return render_template('mylinks.html',
|
||
allowed_secrets=allowed_secrets,
|
||
secret_qr_codes=secret_qr_codes,
|
||
secret_folders=secret_folders,
|
||
secret_valid_to=secret_valid_to)
|
||
|
||
|
||
def remove_secret():
|
||
secret_to_remove = request.form.get('secret')
|
||
allowed_secrets = session.get('allowed_secrets', [])
|
||
if secret_to_remove in allowed_secrets:
|
||
allowed_secrets.remove(secret_to_remove)
|
||
session['allowed_secrets'] = allowed_secrets
|
||
return redirect(url_for('mylinks'))
|
||
|
||
return render_template('mylinks.html',
|
||
allowed_secrets=allowed_secrets,
|
||
secret_qr_codes=secret_qr_codes,
|
||
secret_folders=secret_folders) |