initial
This commit is contained in:
222
docker-pwss/code/share.py
Normal file
222
docker-pwss/code/share.py
Normal file
@@ -0,0 +1,222 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import bcrypt
|
||||
import argparse
|
||||
import sqlite3
|
||||
from werkzeug.utils import secure_filename
|
||||
from datetime import datetime, timedelta
|
||||
from utils import read_config
|
||||
|
||||
ENTRY = {"expires": "never", "password": None}
|
||||
CONFIGS = os.getenv("CONFIG_FOLDER")
|
||||
FOLDERS = os.getenv("STATIC_FOLDER")
|
||||
DATABASE = os.getenv("DATABASE", None)
|
||||
SCHEMA = """CREATE TABLE IF NOT EXISTS sessions(
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
folder TEXT NOT NULL,
|
||||
ip TEXT NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
expire INTEGER NOT NULL
|
||||
);"""
|
||||
|
||||
|
||||
def get_opts():
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
sub_add = subparsers.add_parser("add")
|
||||
sub_remove = subparsers.add_parser("remove")
|
||||
sub_edit = subparsers.add_parser("edit")
|
||||
sub_list = subparsers.add_parser("list")
|
||||
sub_session_list = subparsers.add_parser("sessions-list")
|
||||
sub_session_clean = subparsers.add_parser(
|
||||
"sessions-clean", help="Clean out session DB"
|
||||
)
|
||||
sub_session_remove = subparsers.add_parser(
|
||||
"sessions-remove", help="Terminates all sessions"
|
||||
)
|
||||
|
||||
for p in (sub_add, sub_remove, sub_edit):
|
||||
p.add_argument("folder", action="store", help="Folder name to share")
|
||||
|
||||
for p in (sub_add, sub_edit):
|
||||
|
||||
p.add_argument(
|
||||
"--expires",
|
||||
action="store",
|
||||
help="Share expires in days, or 'never'",
|
||||
default=None,
|
||||
)
|
||||
p.add_argument(
|
||||
"--password", action="store", help="Set password", default=None
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def manager():
|
||||
opts = get_opts()
|
||||
if opts.command == None or opts.command == "list":
|
||||
shares_list()
|
||||
if opts.command == "add":
|
||||
share_add(opts)
|
||||
if opts.command == "remove":
|
||||
share_remove(opts)
|
||||
if opts.command == "edit":
|
||||
share_edit(opts)
|
||||
if opts.command == "sessions-list":
|
||||
session_list()
|
||||
if opts.command == "sessions-remove":
|
||||
session_remove()
|
||||
if opts.command == "sessions-clean":
|
||||
session_clean()
|
||||
|
||||
|
||||
def load_config(name):
|
||||
config = read_config(name)
|
||||
return config
|
||||
|
||||
|
||||
def save_config(path, config):
|
||||
with open(path, "wt") as fp:
|
||||
save_config = ENTRY.copy()
|
||||
for key in save_config:
|
||||
save_config[key] = config[key]
|
||||
return json.dump(save_config, fp, indent=2, sort_keys=True)
|
||||
|
||||
|
||||
def share_oneliner(entry):
|
||||
return f"{(entry['name']+'/').ljust(15)} expires: {entry['expires']}"
|
||||
|
||||
|
||||
def shares_list():
|
||||
|
||||
print("Shared folders:")
|
||||
for c in sorted(os.listdir(CONFIGS)):
|
||||
if c.endswith(".json"):
|
||||
entry = load_config(c[0:-5])
|
||||
print(share_oneliner(entry))
|
||||
|
||||
|
||||
def share_add(opts):
|
||||
|
||||
entry = ENTRY.copy()
|
||||
if opts.folder != secure_filename(opts.folder):
|
||||
raise ValueError(f"Folder '{opts.folder}' is not a safe filename")
|
||||
|
||||
if opts.expires:
|
||||
entry["expires"] = (
|
||||
(datetime.now() + timedelta(days=float(opts.expires)))
|
||||
.replace(microsecond=0)
|
||||
.isoformat()
|
||||
)
|
||||
if not opts.password:
|
||||
raise ValueError("Password required")
|
||||
(pw, _) = hash_password(opts.password)
|
||||
entry["password"] = pw
|
||||
share_folder = os.path.join(FOLDERS, opts.folder)
|
||||
share_config = os.path.join(CONFIGS, f"{opts.folder}.json")
|
||||
if os.path.exists(share_config):
|
||||
raise FileExistsError("Configuration already exists. Edit with `edit`")
|
||||
|
||||
save_config(share_config, entry)
|
||||
if not os.path.exists(share_folder):
|
||||
os.mkdir(share_folder)
|
||||
print(f"Added:\n{share_oneliner(load_config(opts.folder))}")
|
||||
|
||||
|
||||
def share_remove(opts):
|
||||
|
||||
share_folder = os.path.join(FOLDERS, opts.folder)
|
||||
share_config = os.path.join(CONFIGS, f"{opts.folder}.json")
|
||||
if not os.path.exists(share_config):
|
||||
raise FileNotFoundError("Configuration doesn't exist.")
|
||||
else:
|
||||
print(f"Removing configuration {share_config}")
|
||||
os.remove(share_config)
|
||||
if os.path.exists(share_folder):
|
||||
if len(os.listdir(share_folder)) > 0:
|
||||
print(f"Not removing folder {share_folder}, contains data")
|
||||
else:
|
||||
print(f"Removing empty folder {share_folder}")
|
||||
os.rmdir(share_folder)
|
||||
|
||||
|
||||
def share_edit(opts):
|
||||
|
||||
share_config = os.path.join(CONFIGS, f"{opts.folder}.json")
|
||||
if not os.path.exists(share_config):
|
||||
raise FileNotFoundError("Configuration doesn't exist.")
|
||||
entry = load_config(opts.folder)
|
||||
if opts.expires:
|
||||
print(f"Updating expiry date: {opts.expires}")
|
||||
if opts.expires == "never":
|
||||
entry["expires"] = opts.expires
|
||||
else:
|
||||
entry["expires"] = (
|
||||
(datetime.now() + timedelta(days=float(opts.expires)))
|
||||
.replace(microsecond=0)
|
||||
.isoformat()
|
||||
)
|
||||
if opts.password:
|
||||
print("Updating password")
|
||||
(pw, _) = hash_password(opts.password)
|
||||
entry["password"] = pw
|
||||
|
||||
save_config(share_config, entry)
|
||||
print(f"Added:\n{share_oneliner(load_config(opts.folder))}")
|
||||
|
||||
|
||||
def session_get_database():
|
||||
return sqlite3.connect(DATABASE)
|
||||
|
||||
|
||||
def session_create_database():
|
||||
args = tuple()
|
||||
db = session_get_database()
|
||||
cur = db.execute(SCHEMA, args)
|
||||
db.commit()
|
||||
cur.close()
|
||||
|
||||
|
||||
def session_list():
|
||||
query = "SELECT folder, expire, token, ip FROM sessions"
|
||||
args = tuple()
|
||||
cur = session_get_database().execute(query, args)
|
||||
print(f"{'Share'.ljust(15)} {'Expires'.ljust(19)} IP")
|
||||
for session in cur.fetchall():
|
||||
d = datetime.fromtimestamp(session[1])
|
||||
print(f"{session[0].ljust(15)} {d} {session[3]}")
|
||||
cur.close()
|
||||
|
||||
|
||||
def session_remove():
|
||||
query = "DELETE FROM sessions"
|
||||
args = tuple()
|
||||
db = session_get_database()
|
||||
cur = db.execute(query, args)
|
||||
db.commit()
|
||||
cur.close()
|
||||
|
||||
|
||||
def session_clean():
|
||||
query = "DELETE FROM sessions WHERE expire < ?"
|
||||
args = (int(time.time()),)
|
||||
db = session_get_database()
|
||||
cur = db.execute(query, args)
|
||||
db.commit()
|
||||
cur.close()
|
||||
|
||||
|
||||
def hash_password(pw):
|
||||
pw = pw.encode("utf-8")
|
||||
salt = bcrypt.gensalt()
|
||||
hashed = bcrypt.hashpw(pw, salt)
|
||||
return hashed.decode(), salt.decode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
manager()
|
||||
Reference in New Issue
Block a user