2020-08-12 10:19:38 +03:00
|
|
|
import hashlib
|
|
|
|
import secrets
|
2012-05-14 19:17:49 +04:00
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
import bottle
|
|
|
|
|
|
|
|
from appdirs import AppDirs
|
|
|
|
|
|
|
|
import zerobin
|
2015-05-10 20:19:02 +03:00
|
|
|
from zerobin import default_settings
|
2012-05-14 19:17:49 +04:00
|
|
|
|
2020-08-12 10:19:38 +03:00
|
|
|
|
|
|
|
from runpy import run_path
|
2014-06-22 09:42:21 +04:00
|
|
|
|
2012-05-14 19:17:49 +04:00
|
|
|
|
2015-05-10 20:19:02 +03:00
|
|
|
class SettingsValidationError(Exception):
|
|
|
|
pass
|
2013-04-29 22:17:37 +04:00
|
|
|
|
|
|
|
|
2012-05-14 19:17:49 +04:00
|
|
|
class SettingsContainer(object):
|
|
|
|
"""
|
|
|
|
Singleton containing the settings for the whole app
|
|
|
|
"""
|
|
|
|
|
|
|
|
_instance = None
|
|
|
|
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
|
|
|
|
|
|
if not cls._instance:
|
2020-08-11 17:37:03 +03:00
|
|
|
cls._instance = super(SettingsContainer, cls).__new__(cls, *args, **kwargs)
|
2012-05-14 19:17:49 +04:00
|
|
|
cls._instance.update_with_module(default_settings)
|
|
|
|
return cls._instance
|
|
|
|
|
2020-08-14 17:41:45 +03:00
|
|
|
def update_with_dict(self, mapping):
|
2012-05-14 19:17:49 +04:00
|
|
|
"""
|
2014-06-22 09:42:21 +04:00
|
|
|
Update settings with values from the given mapping object.
|
2012-05-14 19:17:49 +04:00
|
|
|
(Taking only variable with uppercased name)
|
|
|
|
"""
|
2020-08-14 17:41:45 +03:00
|
|
|
for name, value in mapping.items():
|
2012-05-14 19:17:49 +04:00
|
|
|
if name.isupper():
|
|
|
|
setattr(self, name, value)
|
|
|
|
return self
|
|
|
|
|
2014-06-22 09:42:21 +04:00
|
|
|
def update_with_module(self, module):
|
|
|
|
"""
|
|
|
|
Update settings with values from the given module.
|
|
|
|
Uses update_with_dict() behind the scenes.
|
|
|
|
"""
|
|
|
|
return self.update_with_dict(module.__dict__)
|
|
|
|
|
2012-05-14 19:17:49 +04:00
|
|
|
@classmethod
|
|
|
|
def from_module(cls, module):
|
|
|
|
"""
|
|
|
|
Create an instance of SettingsContainer with values based
|
|
|
|
on the one in the passed module.
|
|
|
|
"""
|
2020-08-14 17:41:45 +03:00
|
|
|
params = cls()
|
|
|
|
params.update_with_module(module)
|
|
|
|
return params
|
2012-05-14 19:17:49 +04:00
|
|
|
|
|
|
|
def update_with_file(self, filepath):
|
|
|
|
"""
|
|
|
|
Update settings with values from the given module file.
|
2014-06-22 09:42:21 +04:00
|
|
|
Uses update_with_dict() behind the scenes.
|
2012-05-14 19:17:49 +04:00
|
|
|
"""
|
2020-08-31 18:25:18 +03:00
|
|
|
params = run_path(str(filepath))
|
2020-08-14 17:41:45 +03:00
|
|
|
return self.update_with_dict(params)
|
2012-05-14 19:17:49 +04:00
|
|
|
|
|
|
|
|
|
|
|
settings = SettingsContainer()
|
2015-05-10 20:19:02 +03:00
|
|
|
|
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
def ensure_app_context(data_dir=None, config_dir=None):
|
2020-08-12 10:19:38 +03:00
|
|
|
""" Ensure all the variable things we generate are available.
|
|
|
|
|
|
|
|
This will make sure we have:
|
|
|
|
|
|
|
|
- a var dir
|
|
|
|
- a content dir
|
|
|
|
- a secret key
|
|
|
|
- an admin URL
|
2020-08-13 15:36:42 +03:00
|
|
|
|
|
|
|
This function is idempotent if nothing touch the files it created.
|
2020-08-12 10:19:38 +03:00
|
|
|
"""
|
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
app_dirs = AppDirs("0bin", "tygs")
|
|
|
|
|
|
|
|
settings.DATA_DIR = Path(data_dir or app_dirs.user_data_dir).expanduser()
|
|
|
|
settings.DATA_DIR.mkdir(exist_ok=True, parents=True)
|
|
|
|
|
|
|
|
settings.CONFIG_DIR = Path(config_dir or app_dirs.user_config_dir).expanduser()
|
|
|
|
settings.CONFIG_DIR.mkdir(exist_ok=True, parents=True)
|
|
|
|
|
|
|
|
settings.STATIC_FILES_ROOT = zerobin.ROOT_DIR / "static"
|
|
|
|
|
|
|
|
settings.PASTE_FILES_ROOT = settings.DATA_DIR / "pastes"
|
2020-08-12 10:19:38 +03:00
|
|
|
settings.PASTE_FILES_ROOT.mkdir(exist_ok=True)
|
2020-08-13 15:36:42 +03:00
|
|
|
|
|
|
|
settings.SESSIONS_DIR = settings.DATA_DIR / "sessions"
|
2020-08-12 10:19:38 +03:00
|
|
|
settings.SESSIONS_DIR.mkdir(exist_ok=True)
|
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
bottle.TEMPLATE_PATH.insert(0, zerobin.ROOT_DIR / "views")
|
|
|
|
|
|
|
|
CUSTOM_VIEWS_DIR = settings.CONFIG_DIR / "custom_views"
|
|
|
|
CUSTOM_VIEWS_DIR.mkdir(exist_ok=True)
|
|
|
|
|
|
|
|
bottle.TEMPLATE_PATH.insert(0, CUSTOM_VIEWS_DIR)
|
|
|
|
|
2020-08-14 16:08:55 +03:00
|
|
|
bottle.BaseRequest.MEMFILE_MAX = settings.MAX_SIZE + (1024 * 100)
|
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
secret_key_file = settings.CONFIG_DIR / "secret_key"
|
2020-08-12 10:19:38 +03:00
|
|
|
if not secret_key_file.is_file():
|
|
|
|
secret_key_file.write_text(secrets.token_urlsafe(64))
|
|
|
|
settings.SECRET_KEY = secret_key_file.read_text()
|
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
admin_password_file = settings.CONFIG_DIR / "admin_password"
|
2020-08-12 10:19:38 +03:00
|
|
|
if not secret_key_file.is_file():
|
|
|
|
admin_password_file.write_text(
|
|
|
|
"No password set. Use the set_admin_passord command. Don't write this file by hand."
|
|
|
|
)
|
|
|
|
settings.ADMIN_PASSWORD_FILE = admin_password_file
|
|
|
|
|
|
|
|
payload = ("admin" + settings.SECRET_KEY).encode("ascii")
|
2020-08-12 16:21:49 +03:00
|
|
|
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest() + "/"
|
2020-08-12 10:19:38 +03:00
|
|
|
|
2020-08-13 15:36:42 +03:00
|
|
|
settings_file = settings.CONFIG_DIR / "settings.py"
|
|
|
|
if not settings_file.is_file():
|
|
|
|
default_config = (zerobin.ROOT_DIR / "default_settings.py").read_text()
|
|
|
|
settings_file.write_text(default_config)
|
|
|
|
|
|
|
|
settings.update_with_file(settings_file)
|
|
|
|
|
2020-08-12 10:19:38 +03:00
|
|
|
|
|
|
|
def hash_password(password):
|
|
|
|
return hashlib.scrypt(
|
|
|
|
password.encode("utf8"),
|
|
|
|
salt=settings.SECRET_KEY.encode("ascii"),
|
|
|
|
n=16384,
|
|
|
|
r=8,
|
|
|
|
p=1,
|
|
|
|
dklen=32,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def check_password(password):
|
|
|
|
try:
|
2020-08-12 16:21:49 +03:00
|
|
|
return settings.ADMIN_PASSWORD_FILE.read_bytes() == hash_password(password)
|
2020-08-12 10:19:38 +03:00
|
|
|
except (FileNotFoundError, AttributeError):
|
|
|
|
return False
|
|
|
|
|