diff --git a/.gitignore b/.gitignore index a7e4691..842f651 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ expephalon/custom_settings.py +settings.ini +log/ +modules/ *.pyc __pycache__/ migrations/ *.pid .vscode/ -venv/ \ No newline at end of file +venv/ diff --git a/.gitmodules b/.gitmodules index 3c3c424..07b80de 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,21 +1,21 @@ [submodule "chat"] - path = chat + path = modules/chat url = git@kumig.it:kumisystems/expephalon-chat.git [submodule "demomodule"] - path = demomodule + path = modules/demomodule url = git@kumig.it:kumisystems/expephalon-demomodule.git [submodule "kumisms"] - path = kumisms + path = modules/kumisms url = git@kumig.it:kumisystems/expephalon-kumisms.git [submodule "playsms"] - path = playsms + path = modules/playsms url = git@kumig.it:kumisystems/expephalon-playsms.git [submodule "smsotp"] - path = smsotp + path = modules/smsotp url = git@kumig.it:kumisystems/expephalon-smsotp.git [submodule "totp"] - path = totp + path = modules/totp url = git@kumig.it:kumisystems/expephalon-totp.git [submodule "ratesapi"] - path = ratesapi + path = modules/ratesapi url = git@kumig.it:kumisystems/expephalon-ratesapi.git diff --git a/debian-packages.txt b/apt-requirements.txt similarity index 100% rename from debian-packages.txt rename to apt-requirements.txt diff --git a/chat b/chat deleted file mode 160000 index 207fba6..0000000 --- a/chat +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 207fba6373de3f28354763f05363f8c201260091 diff --git a/demomodule b/demomodule deleted file mode 160000 index 9709d12..0000000 --- a/demomodule +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9709d12dfceae66ff180230ae36459de50e59371 diff --git a/iam.json b/doc/iam.json similarity index 100% rename from iam.json rename to doc/iam.json diff --git a/expephalon/custom_settings.dist.py b/expephalon/custom_settings.dist.py deleted file mode 100644 index e761730..0000000 --- a/expephalon/custom_settings.dist.py +++ /dev/null @@ -1,58 +0,0 @@ -# Secret Key: Replace this by a long random string. -# You can use django.core.management.utils.get_random_secret_key to generate one. - -SECRET_KEY = "changeme" - -# Database settings -# This application is tested only with MariaDB/MySQL. -# You will have to edit settings.py if you want to use Postgres, SQLite, etc. - -DB_HOST = "localhost" -DB_PORT = 3306 -DB_USER = "expephalon" -DB_PASS = "secret" -DB_NAME = "expephalon" - -# AWS/Minio configuration -# Insert any required parameters as per https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html - -AWS_ACCESS_KEY_ID = None -AWS_SECRET_ACCESS_KEY = None -AWS_STORAGE_BUCKET_NAME = None -AWS_S3_ENDPOINT_URL = None - -# Whether debug messages should be output - set to False in production (exposes sensitive data) - -DEBUG = True - -# Which hostnames may be used to access the system - by default accepts "localhost", add "*" to allow any hostname - -ALLOWED_HOSTS = [] - -# To add frontend or backend templates, move them to the /templates subdirectory, then insert their name (i.e. the directory name) in the -# appropriate field. Move any required statics to the /static subdirectory - -EXPEPHALON_FRONTEND = "frontend" -EXPEPHALON_BACKEND = "backend" - -# To add Expephalon modules, move them to the Expephalon root directory, then add them to this list - -EXPEPHALON_MODULES = [] - -# To use memcached for caching, add IP:PORT or unix:PATH - default setting should be good for an unmodified local setup of memcached - -MEMCACHED_LOCATION = ["127.0.0.1:11211"] - -# RabbitMQ is required for queues to work - default settings should be good for an unmodified local setup of RabbitMQ, -# but you might still want to configure it to use a password - -RABBITMQ_LOCATION = "127.0.0.1:5672" -RABBITMQ_VHOST = "" -RABBITMQ_USER = "guest" -RABBITMQ_PASS = "guest" - -# Logging -# Set log level to something higher in production - debug might leak sensitive information - -LOG_DIRECTORY = "/var/log/expephalon/" -LOGLEVEL = "DEBUG" \ No newline at end of file diff --git a/expephalon/exceptions.py b/expephalon/exceptions.py new file mode 100644 index 0000000..4dae032 --- /dev/null +++ b/expephalon/exceptions.py @@ -0,0 +1,2 @@ +class ConfigError(RuntimeError): + pass diff --git a/expephalon/settings.py b/expephalon/settings.py index 011c9aa..3dda3e7 100644 --- a/expephalon/settings.py +++ b/expephalon/settings.py @@ -1,11 +1,131 @@ import os +from pathlib import Path +from configparser import NoSectionError + from django.urls import reverse_lazy -from expephalon.custom_settings import * # pylint: disable=unused-wildcard-import +from autosecretkey import AutoSecretKey + +from .exceptions import ConfigError + + +# Build paths inside the project like this: BASE_DIR / ... +BASE_DIR = Path(__file__).parent.parent + +ASK_PATH = BASE_DIR / "settings.ini" +ASK = AutoSecretKey(ASK_PATH, template=BASE_DIR / "settings.dist.ini") + +try: + if configpath := ASK.config.get("EXPEPHALON", "ConfigPath", fallback=None): + ASK_PATH = BASE_DIR / "settings.ini" + ASK = AutoSecretKey(ASK_PATH) +except NoSectionError: + raise ConfigError(f"{ASK_PATH} is missing an [EXPEPHALON] section. Have you configured everything?") + +SECRET_KEY = ASK.secret_key +ALLOWED_HOSTS = ["*"] + +DEBUG = ASK.config.getboolean("EXPEPHALON", "Debug", fallback=False) + + +# Database +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases + +if ASK.config.has_section("MariaDB"): + DB_SECTION = "MariaDB" +elif ASK.config.has_section("MySQL"): + DB_SECTION = "MySQL" +else: + raise ConfigError(f"No database configuration section found in {ASK_PATH}") + +if DB_SECTION in ("MariaDB", "MySQL"): + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': ASK.config[DB_SECTION]["Database"], + 'USER': ASK.config[DB_SECTION]["Username"], + 'PASSWORD': ASK.config.get(DB_SECTION, "Password", fallback=""), + 'HOST': ASK.config.get(DB_SECTION, "Host", fallback="localhost"), + 'PORT': ASK.config.getint(DB_SECTION, "Port", fallback=3306), + } + } + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.0/howto/static-files/ + +STATIC_URL = '/static/' + +STATICFILES_DIRS = [ + BASE_DIR / "static", +] + +if ASK.config.has_section("S3"): + DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' + AWS_DEFAULT_ACL = None + + STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' + + AWS_ACCESS_KEY_ID = ASK.config["S3"]["AccessKey"] + AWS_SECRET_ACCESS_KEY = ASK.config["S3"]["SecretKey"] + AWS_STORAGE_BUCKET_NAME = ASK.config["S3"]["Bucket"] + AWS_S3_ENDPOINT_URL = ASK.config["S3"]["Endpoint"] + + +# Expephalon modules + +TEMPLATES_DIRS = [ + BASE_DIR / "templates", +] + +MODULES_DIRS = [ + BASE_DIR / "modules", +] + +EXPEPHALON_FRONTEND = ASK.config.get("EXPEPHALON", "FrontendTemplate", fallback="frontend") +EXPEPHALON_BACKEND = ASK.config.get("EXPEPHALON", "BackendTemplate", fallback="backend") +EXPEPHALON_MAIL = ASK.config.get("EXPEPHALON", "MailTemplate", fallback="mail") + +EXPEPHALON_MODULES = [] + + +# Caching + +if ASK.config.has_section("MEMCACHED"): + CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': [ASK.config.get("MEMCACHED", "Location", fallback="127.0.0.1:11211")], + } + } + + +CELERY_TASK_SERIALIZER = "pickle" +CELERY_RESULT_SERIALIZER = "pickle" +CELERY_ACCEPT_CONTENT = ['pickle'] +CELERY_RESULT_BACKEND = 'django-db' +CELERY_CACHE_BACKEND = 'django-cache' + +if ASK.config.has_section("RABBITMQ"): + RABBITMQ_USER = ASK.config.get("RABBITMQ", "Username", fallback="guest") + RABBITMQ_PASS = ASK.config.get("RABBITMQ", "Password", fallback="guest") + RABBITMQ_HOST = ASK.config.get("RABBITMQ", "Host", fallback="127.0.0.1") + RABBITMQ_PORT = ASK.config.getint("RABBITMQ", "Port", fallback=5672) + RABBITMQ_VHOST = ASK.config.get("RABBITMQ", "VHost", fallback="") + + CELERY_BROKER_URL = f"amqp://{RABBITMQ_USER}:{RABBITMQ_PASS}@{RABBITMQ_HOST}:{RABBITMQ_PORT}/{RABBITMQ_VHOST}" +else: + CELERY_BROKER_URL = "memory://" + +CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" +CELERY_TASK_RESULT_EXPIRES = 12 * 60 * 60 +CELERY_WORKER_HIJACK_ROOT_LOGGER = False + + +LOG_DIRECTORY = Path(ASK.config.get("EXPEPHALON", "LogDirectory", fallback=BASE_DIR / "log")) +LOGLEVEL = ASK.config.get("EXPEPHALON", "LogLevel", fallback="DEBUG") -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Application definition @@ -40,7 +160,7 @@ ROOT_URLCONF = 'expephalon.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ os.path.join(BASE_DIR, "templates") ], + 'DIRS': [ BASE_DIR / "templates" ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -57,20 +177,6 @@ WSGI_APPLICATION = 'expephalon.wsgi.application' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -# Database -# https://docs.djangoproject.com/en/3.0/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': DB_NAME, - 'USER': DB_USER, - 'PASSWORD': DB_PASS, - 'HOST': DB_HOST, - 'PORT': str(DB_PORT), - } -} - # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators @@ -104,17 +210,6 @@ USE_L10N = True USE_TZ = True -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/3.0/howto/static-files/ - -STATIC_URL = '/static/' - -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "static"), -] - -STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' - # Password hasher # https://docs.djangoproject.com/en/3.0/topics/auth/passwords/#how-django-stores-passwords @@ -125,33 +220,6 @@ PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ] -# Media - -DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' -AWS_DEFAULT_ACL = None - -# Caching - -if MEMCACHED_LOCATION: - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': MEMCACHED_LOCATION, - } - } - -# Queue - -CELERY_TASK_SERIALIZER = "pickle" -CELERY_RESULT_SERIALIZER = "pickle" -CELERY_ACCEPT_CONTENT = ['pickle'] -CELERY_RESULT_BACKEND = 'django-db' -CELERY_CACHE_BACKEND = 'django-cache' -CELERY_BROKER_URL = f"amqp://{RABBITMQ_USER}:{RABBITMQ_PASS}@{RABBITMQ_LOCATION}/{RABBITMQ_VHOST}" -CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" -CELERY_TASK_RESULT_EXPIRES = 12 * 60 * 60 -CELERY_WORKER_HIJACK_ROOT_LOGGER = False - # Auth URLs LOGIN_REDIRECT_URL = reverse_lazy('dashboard') @@ -161,7 +229,7 @@ LOGOUT_URL = reverse_lazy('logout') # Logging try: - os.makedirs(LOG_DIRECTORY, exist_ok=True) + LOG_DIRECTORY.mkdir(exist_ok=True) except: raise Exception(f"Could not create log directory {LOG_DIRECTORY}, please create it manually and make sure the Expephalon user account has sufficient privileges to write to it.") diff --git a/kumisms b/kumisms deleted file mode 160000 index 8ee4066..0000000 --- a/kumisms +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8ee4066973d81d3556b021e0b892ae325fc89b48 diff --git a/playsms b/playsms deleted file mode 160000 index 5f97ffb..0000000 --- a/playsms +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5f97ffb347b75678e49128bc7c75181fd2a10254 diff --git a/ratesapi b/ratesapi deleted file mode 160000 index c7226ba..0000000 --- a/ratesapi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c7226ba94d90342db315b541d74e1842dc28eaad diff --git a/requirements.txt b/requirements.txt index 659ca2a..8256990 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,6 @@ GitPython python-dateutil bindglobal pyotp -pyqrcode \ No newline at end of file +pyqrcode +dbsettings +django-autosecretkey diff --git a/smsotp b/smsotp deleted file mode 160000 index 4913f1b..0000000 --- a/smsotp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4913f1bf5bf9a35eef356566bce38a98de91444a diff --git a/totp b/totp deleted file mode 160000 index cd79b1f..0000000 --- a/totp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cd79b1f6bca10c9b8b1aefe59667c90e984d46d6