Skip to content
Snippets Groups Projects
Commit 8ab6459a authored by jan.bednarik's avatar jan.bednarik
Browse files

majak: Refactoring settings

parent 5f0728bc
No related branches found
No related tags found
1 merge request!4Settings
...@@ -16,13 +16,18 @@ COPY . . ...@@ -16,13 +16,18 @@ COPY . .
RUN bash -c 'adduser --disabled-login --quiet --gecos app app && \ RUN bash -c 'adduser --disabled-login --quiet --gecos app app && \
chmod -R o+r /app/ && \ chmod -R o+r /app/ && \
mkdir /app/media_files && \ mkdir /app/media_files && \
mkdir /app/static_files && \
chown -R app:app /app/media_files && \ chown -R app:app /app/media_files && \
chown -R app:app /app/static_files && \
chmod o+x /app/run.sh' chmod o+x /app/run.sh'
USER app USER app
# TODO HACK! ENV DJANGO_SETTINGS_MODULE "majak.settings.production"
# ENV DJANGO_SETTINGS_MODULE "majak.settings.production"
ENV DJANGO_SETTINGS_MODULE "majak.settings.dev" # fake values for required env variables used to run collectstatic during build
RUN DJANGO_SECRET_KEY=x DATABASE_URL=postgres://x/x DJANGO_ALLOWED_HOSTS=x \
OIDC_RP_CLIENT_ID=x OIDC_RP_CLIENT_SECRET=x OIDC_RP_REALM_URL=x \
python manage.py collectstatic
EXPOSE 8000 EXPOSE 8000
......
...@@ -6,7 +6,24 @@ Maják je CMS pro Pirátské weby. Postavený je na [Wagtail](https://wagtail.io ...@@ -6,7 +6,24 @@ Maják je CMS pro Pirátské weby. Postavený je na [Wagtail](https://wagtail.io
[![powered by: Wagtail](https://img.shields.io/badge/powered%20by-Wagtail-43b1b0)](https://wagtail.io) [![powered by: Wagtail](https://img.shields.io/badge/powered%20by-Wagtail-43b1b0)](https://wagtail.io)
[![powered by: Django](https://img.shields.io/badge/powered%20by-Django-0C4B33)](https://www.djangoproject.com) [![powered by: Django](https://img.shields.io/badge/powered%20by-Django-0C4B33)](https://www.djangoproject.com)
## Konfigurace ## Pod pokličkou
[Wagtail](https://wagtail.io) a [Django](https://www.djangoproject.com) jsou poměrně
vyspělé frameworky. Vždy mysli na to, že problém co řešíš, už pravděpodobně řešil
někdo před tebou. A obvykle existuje elegantní řešení.
Pár užitečných odkazů:
* [docs Wagtail](https://docs.wagtail.io/)
* [docs Django](https://docs.djangoproject.com/)
* [docs Wagtailmenus](https://wagtailmenus.readthedocs.io/)
A za zmínku stojí [Awesome Wagtail](https://github.com/springload/awesome-wagtail)
jako přehled pluginů a rozšíření pro Wagtail.
## Deployment
### Konfigurace
Je třeba nastavit environment proměnné: Je třeba nastavit environment proměnné:
...@@ -14,29 +31,24 @@ Je třeba nastavit environment proměnné: ...@@ -14,29 +31,24 @@ Je třeba nastavit environment proměnné:
| --- | --- | --- | | --- | --- | --- |
| `DATABASE_URL` | | DSN k databázi (např. `postgres://user:pass@localhost:5342/majak`) | | `DATABASE_URL` | | DSN k databázi (např. `postgres://user:pass@localhost:5342/majak`) |
| `OIDC_RP_REALM_URL` | | OpenID server realm URL (např. `http://localhost:8080/auth/realms/master/`) | | `OIDC_RP_REALM_URL` | | OpenID server realm URL (např. `http://localhost:8080/auth/realms/master/`) |
| `OIDC_RP_CLIENT_ID` | | OpenID Client ID | | `OIDC_RP_CLIENT_ID` | | OpenID Client ID |
| `OIDC_RP_CLIENT_SECRET` | | OpenID Client Secret | | `OIDC_RP_CLIENT_SECRET` | | OpenID Client Secret |
| `BASE_URL` | https://majak.pirati.cz | základní URL pro notifikační emaily apod. |
V produkci musí být navíc nastaveno: V produkci musí být navíc nastaveno:
| proměnná | default | popis | | proměnná | default | popis |
| --- | --- | --- | | --- | --- | --- |
| `DJANGO_SECRET_KEY` | | tajný šifrovací klíč | | `DJANGO_SECRET_KEY` | | tajný šifrovací klíč |
| `DJANGO_ALLOWED_HOSTS` | | allowed hosts (více hodnot odděleno čárkami) |
## Pod pokličkou ### Přidání nového webu
[Wagtail](https://wagtail.io) a [Django](https://www.djangoproject.com) jsou poměrně
vyspělé frameworky. Vždy mysli na to, že problém co řešíš, už pravděpodobně řešil
někdo před tebou. A obvykle existuje elegantní řešení.
Pár užitečných odkazů:
* [docs Wagtail](https://docs.wagtail.io/) Doména či subdoména se musí nakonfigurovat v:
* [docs Django](https://docs.djangoproject.com/)
* [docs Wagtailmenus](https://wagtailmenus.readthedocs.io/)
A za zmínku stojí [Awesome Wagtail](https://github.com/springload/awesome-wagtail) * environment proměnné `DJANGO_ALLOWED_HOSTS`
jako přehled pluginů a rozšíření pro Wagtail. * proxy před Majákem
* SSO Client redirect URIs
## Vývoj ## Vývoj
......
...@@ -9,8 +9,27 @@ PROJECT_DIR = ROOT_DIR / "majak" ...@@ -9,8 +9,27 @@ PROJECT_DIR = ROOT_DIR / "majak"
env = environ.Env() env = environ.Env()
environ.Env.read_env(str(ROOT_DIR / ".env")) environ.Env.read_env(str(ROOT_DIR / ".env"))
# Application definition # GENERAL
# ------------------------------------------------------------------------------
DEBUG = env.bool("DJANGO_DEBUG", False)
ROOT_URLCONF = "majak.urls"
WSGI_APPLICATION = "majak.wsgi.application"
# I18N and L10N
# ------------------------------------------------------------------------------
TIME_ZONE = "Europe/Prague"
LANGUAGE_CODE = "cs"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# DATABASES
# ------------------------------------------------------------------------------
DATABASES = {"default": env.db("DATABASE_URL")}
DATABASES["default"]["ATOMIC_REQUESTS"] = True
# APPS
# ------------------------------------------------------------------------------
INSTALLED_APPS = [ INSTALLED_APPS = [
"search", "search",
"senator", "senator",
...@@ -41,6 +60,25 @@ INSTALLED_APPS = [ ...@@ -41,6 +60,25 @@ INSTALLED_APPS = [
"django.contrib.staticfiles", "django.contrib.staticfiles",
] ]
# AUTHENTICATION
# ------------------------------------------------------------------------------
AUTHENTICATION_BACKENDS = ["pirates.auth.PiratesOIDCAuthenticationBackend"]
AUTH_USER_MODEL = "users.User"
LOGIN_REDIRECT_URL = "/admin"
LOGOUT_REDIRECT_URL = "/admin"
LOGIN_URL = "/admin"
OIDC_RP_CLIENT_ID = env.str("OIDC_RP_CLIENT_ID")
OIDC_RP_CLIENT_SECRET = env.str("OIDC_RP_CLIENT_SECRET")
OIDC_RP_REALM_URL = env.str("OIDC_RP_REALM_URL")
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_OP_JWKS_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/certs")
OIDC_OP_AUTHORIZATION_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/auth")
OIDC_OP_TOKEN_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/token")
OIDC_OP_USER_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/userinfo")
# MIDDLEWARE
# ------------------------------------------------------------------------------
MIDDLEWARE = [ MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
...@@ -52,14 +90,33 @@ MIDDLEWARE = [ ...@@ -52,14 +90,33 @@ MIDDLEWARE = [
"wagtail.contrib.redirects.middleware.RedirectMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware",
] ]
ROOT_URLCONF = "majak.urls" # STATIC
# ------------------------------------------------------------------------------
STATIC_ROOT = str(ROOT_DIR / "static_files")
STATIC_URL = "/static/"
STATICFILES_DIRS = [str(PROJECT_DIR / "static")]
STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
]
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
# MEDIA
# ------------------------------------------------------------------------------
MEDIA_URL = "/media/"
MEDIA_ROOT = str(ROOT_DIR / "media_files")
# TEMPLATES
# ------------------------------------------------------------------------------
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [str(PROJECT_DIR / "templates")], "DIRS": [str(PROJECT_DIR / "templates")],
"APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"loaders": [
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
],
"context_processors": [ "context_processors": [
"django.template.context_processors.debug", "django.template.context_processors.debug",
"django.template.context_processors.request", "django.template.context_processors.request",
...@@ -72,75 +129,52 @@ TEMPLATES = [ ...@@ -72,75 +129,52 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = "majak.wsgi.application" # SECURITY
# ------------------------------------------------------------------------------
SESSION_COOKIE_HTTPONLY = True
# Database CSRF_COOKIE_HTTPONLY = True
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = "DENY"
DATABASES = {"default": env.db("DATABASE_URL")}
DATABASES["default"]["ATOMIC_REQUESTS"] = True # EMAIL
# ------------------------------------------------------------------------------
EMAIL_BACKEND = env(
# Password validation "DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.dummy.EmailBackend"
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators )
AUTH_PASSWORD_VALIDATORS = [ # LOGGING
{ # ------------------------------------------------------------------------------
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"
}
}, },
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",}, "handlers": {
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",}, "console": {
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",}, "level": "DEBUG",
] "class": "logging.StreamHandler",
"formatter": "verbose",
}
# Internationalization },
# https://docs.djangoproject.com/en/3.0/topics/i18n/ "root": {"level": "INFO", "handlers": ["console"]},
}
LANGUAGE_CODE = "cs"
TIME_ZONE = "Europe/Prague"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
]
STATICFILES_DIRS = [str(PROJECT_DIR / "static")]
# ManifestStaticFilesStorage is recommended in production, to prevent outdated
# Javascript / CSS assets being served from cache (e.g. after a Wagtail upgrade).
# See https://docs.djangoproject.com/en/3.0/ref/contrib/staticfiles/#manifeststaticfilesstorage
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
STATIC_ROOT = str(ROOT_DIR / "static_files")
STATIC_URL = "/static/"
MEDIA_ROOT = str(ROOT_DIR / "media_files")
MEDIA_URL = "/media/"
# Wagtail settings
# WAGTAIL SETTINGS
# ------------------------------------------------------------------------------
WAGTAIL_SITE_NAME = "Maják" WAGTAIL_SITE_NAME = "Maják"
# Base URL to use when referring to full URLs within the Wagtail admin backend -
# e.g. in notification emails. Don't include '/admin' or a trailing slash
BASE_URL = "http://example.com"
WAGTAIL_ALLOW_UNICODE_SLUGS = False WAGTAIL_ALLOW_UNICODE_SLUGS = False
TAGGIT_CASE_INSENSITIVE = True TAGGIT_CASE_INSENSITIVE = True
AUTH_USER_MODEL = "users.User" WAGTAIL_USER_TIME_ZONES = ["Europe/Prague"]
WAGTAILADMIN_NOTIFICATION_INCLUDE_SUPERUSERS = False
# disable editing of user details synced from SSO
WAGTAIL_USER_EDIT_FORM = "users.forms.CustomUserEditForm" WAGTAIL_USER_EDIT_FORM = "users.forms.CustomUserEditForm"
WAGTAIL_USER_CREATION_FORM = "users.forms.CustomUserCreationForm" WAGTAIL_USER_CREATION_FORM = "users.forms.CustomUserCreationForm"
WAGTAIL_PASSWORD_MANAGEMENT_ENABLED = False WAGTAIL_PASSWORD_MANAGEMENT_ENABLED = False
...@@ -149,17 +183,6 @@ WAGTAILUSERS_PASSWORD_ENABLED = False ...@@ -149,17 +183,6 @@ WAGTAILUSERS_PASSWORD_ENABLED = False
WAGTAILUSERS_PASSWORD_REQUIRED = False WAGTAILUSERS_PASSWORD_REQUIRED = False
WAGTAIL_EMAIL_MANAGEMENT_ENABLED = False WAGTAIL_EMAIL_MANAGEMENT_ENABLED = False
AUTHENTICATION_BACKENDS = ["pirates.auth.PiratesOIDCAuthenticationBackend"] # Base URL to use when referring to full URLs within the Wagtail admin backend -
# e.g. in notification emails. Don't include '/admin' or a trailing slash
OIDC_RP_CLIENT_ID = env.str("OIDC_RP_CLIENT_ID") BASE_URL = env.str("BASE_URL", default="https://majak.pirati.cz")
OIDC_RP_CLIENT_SECRET = env.str("OIDC_RP_CLIENT_SECRET")
OIDC_RP_REALM_URL = env.str("OIDC_RP_REALM_URL")
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_OP_JWKS_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/certs")
OIDC_OP_AUTHORIZATION_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/auth")
OIDC_OP_TOKEN_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/token")
OIDC_OP_USER_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/userinfo")
LOGIN_REDIRECT_URL = "/admin"
LOGOUT_REDIRECT_URL = "/admin"
LOGIN_URL = "/admin"
from .base import * from .base import *
from .base import env
# SECURITY WARNING: don't run with debug turned on in production! # GENERAL
DEBUG = True # ------------------------------------------------------------------------------
DEBUG = env.bool("DJANGO_DEBUG", default=True)
# SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = env("DJANGO_SECRET_KEY", default="58asda4d6nasd*jkj!dbska83asd54")
SECRET_KEY = "1vu8ve^2vdu%k00s7&)l+^a@b5aht53el96evte(7d)0@!dyk1" ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["*"])
# SECURITY WARNING: define the correct hosts in production!
ALLOWED_HOSTS = ["*"]
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
try:
from .local import *
except ImportError:
pass
from .base import * from .base import *
from .base import env
DEBUG = False # DATABASES
# ------------------------------------------------------------------------------
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60)
try: # SECURITY
from .local import * # ------------------------------------------------------------------------------
except ImportError: ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS")
pass SECRET_KEY = env("DJANGO_SECRET_KEY")
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# set this to 60 seconds first and then to 518400 once you prove the former works
SECURE_HSTS_SECONDS = 518400
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
# TEMPLATES
# ------------------------------------------------------------------------------
TEMPLATES[0]["OPTIONS"]["loaders"] = [
(
"django.template.loaders.cached.Loader",
[
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
],
)
]
# STATIC
# ------------------------------------------------------------------------------
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware")
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
# LOGGING
# ------------------------------------------------------------------------------
LOGGING["filters"] = {
"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}
}
...@@ -4,3 +4,4 @@ django-environ ...@@ -4,3 +4,4 @@ django-environ
django-extensions django-extensions
psycopg2-binary psycopg2-binary
pirates<=0.4 pirates<=0.4
whitenoise
...@@ -42,6 +42,7 @@ urllib3==1.25.9 # via requests ...@@ -42,6 +42,7 @@ urllib3==1.25.9 # via requests
wagtail==2.9 # via -r base.in wagtail==2.9 # via -r base.in
wagtailmenus==3.0.1 # via -r base.in wagtailmenus==3.0.1 # via -r base.in
webencodings==0.5.1 # via html5lib webencodings==0.5.1 # via html5lib
whitenoise==5.0.1 # via -r base.in
willow==1.3 # via wagtail willow==1.3 # via wagtail
xlsxwriter==1.2.8 # via wagtail xlsxwriter==1.2.8 # via wagtail
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment