moderator/settings.py (215 lines of code) (raw):
# This is your project's main settings file that can be committed to your
# repo. If you need to override a setting locally, use local.py
import os
import dj_database_url
import pymysql
from decouple import Csv, config
from django_jinja.builtins import DEFAULT_EXTENSIONS
ROOT = os.path.dirname(os.path.dirname(__file__))
path = lambda *a: os.path.abspath(os.path.join(ROOT, *a)) # noqa
SECRET_KEY = config("SECRET_KEY")
DEBUG = config("DEBUG", default=False, cast=bool)
DEV = config("DEV", default=False, cast=bool)
SITE_URL = config("SITE_URL")
ALLOWED_HOSTS = config("ALLOWED_HOSTS", cast=Csv())
# Defines the views served for root URLs.
ROOT_URLCONF = "moderator.urls"
INSTALLED_APPS = [
# Django apps
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.messages",
"django.contrib.staticfiles",
"dal",
"dal_select2",
"django.contrib.admin",
# Third party apps
# "axes",
"mozilla_django_oidc",
# Project specific apps
"moderator.moderate",
]
if DEV:
INSTALLED_APPS += ["django_extensions"]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.middleware.common.CommonMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"csp.middleware.CSPMiddleware",
# "axes.middleware.AxesMiddleware",
]
# List of finder classes that know how to find static files in
# various locations.
if not DEV:
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = "moderator.wsgi.application"
CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
)
TEMPLATES = [
{
"BACKEND": "django_jinja.backend.Jinja2",
"NAME": "jinja2",
"APP_DIRS": True,
"OPTIONS": {
"match_extension": ".jinja",
"newstyle_gettext": True,
"context_processors": CONTEXT_PROCESSORS,
"undefined": "jinja2.Undefined",
"extensions": DEFAULT_EXTENSIONS,
},
},
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"APP_DIRS": True,
"OPTIONS": {"context_processors": CONTEXT_PROCESSORS},
},
]
def COMPRESS_JINJA2_GET_ENVIRONMENT():
from django.template import engines
return engines["jinja2"].env
SITE_ID = 1
# Auth
# The first hasher in this list will be used for new passwords.
# Any other hasher in the list can be used for existing passwords.
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
"django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
"django.contrib.auth.hashers.BCryptPasswordHasher",
]
AUTHENTICATION_BACKENDS = [
# "axes.backends.AxesBackend",
"moderator.moderate.auth.ModeratorAuthBackend",
"django.contrib.auth.backends.ModelBackend",
]
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/var/www/example.com/media/"
MEDIA_ROOT = path("media")
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://example.com/media/", "http://media.example.com/"
MEDIA_URL = "/media/"
# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
STATIC_ROOT = path("static")
STATIC_URL = "/static/"
# Internationalization
TIME_ZONE = config("TIME_ZONE", default="UTC")
USE_I18N = config("USE_I18N", default=False, cast=bool)
USE_L10N = config("USE_L10N", default=False, cast=bool)
USE_TZ = config("USE_TZ", default=True, cast=bool)
# Path to redirect to on successful login.
LOGIN_REDIRECT_URL = config("LOGIN_REDIRECT_URL", default="/")
# Path to redirect to on unsuccessful login attempt.
LOGIN_REDIRECT_URL_FAILURE = config("LOGIN_REDIRECT_URL_FAILURE", default="/")
LOGOUT_REDIRECT_URL = config("LOGOUT_REDIRECT_URL", default="/")
# Paginator items per page
ITEMS_PER_PAGE = config("ITEMS_PER_PAGE", default=10, cast=int)
# Sessions
SESSION_COOKIE_SECURE = config("SESSION_COOKIE_SECURE", default=True, cast=bool)
SESSION_COOKIE_HTTPONLY = config("SESSION_COOKIE_HTTPONLY", default=True, cast=bool)
CSRF_USE_SESSIONS = True
CSRF_COOKIE_SECURE = config("CSRF_COOKIE_SECURE", default=True, cast=bool)
# Security Middleware
SECURE_CONTENT_TYPE_NOSNIFF = config(
"SECURE_CONTENT_TYPE_NOSNIFF", default=True, cast=bool
)
SECURE_BROWSER_XSS_FILTER = config("SECURE_BROWSER_XSS_FILTER", default=True, cast=bool)
SECURE_HSTS_SECONDS = config("SECURE_HSTS_SECONDS", default=15768000, cast=int)
SECURE_HSTS_INCLUDE_SUBDOMAINS = config(
"SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True, cast=int
)
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Database
DATABASES = {"default": config("DATABASE_URL", cast=dj_database_url.parse)}
pymysql.install_as_MySQLdb()
# Enable debugging only if in dev env
if DEBUG:
for backend in TEMPLATES:
backend["OPTIONS"]["debug"] = DEBUG
# Sentry support
if SENTRY_DSN := config("SENTRY_DSN", None):
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[DjangoIntegration()],
sample_rate=config("SENTRY_SAMPLE_RATE", 1.0),
traces_sample_rate=config("SENTRY_TRACES_SAMPLE_RATE", 0.01),
)
# Django-CSP
CSP_DEFAULT_SRC = (
"'self'",
"https://*.mozilla.org",
"https://*.mozilla.net",
)
CSP_IMG_SRC = (
"'self'",
"https://*.google-analytics.com",
"https://*.gravatar.com",
"https://*.wp.com",
"https://people.mozilla.org",
"https://*.mozaws.net",
)
CSP_SCRIPT_SRC = (
"'self'",
"https://*.google-analytics.com",
)
# Django OIDC
def _username_algo(email):
import base64
import hashlib
try:
from django.utils.encoding import smart_bytes
except ImportError:
from django.utils.encoding import smart_str as smart_bytes
return base64.urlsafe_b64encode(hashlib.sha1(smart_bytes(email)).digest()).rstrip(
b"="
)
OIDC_OP_AUTHORIZATION_ENDPOINT = config("OIDC_OP_AUTHORIZATION_ENDPOINT", default="")
OIDC_OP_TOKEN_ENDPOINT = config("OIDC_OP_TOKEN_ENDPOINT", default="")
OIDC_OP_USER_ENDPOINT = config("OIDC_OP_USER_ENDPOINT", default="")
OIDC_RP_CLIENT_ID = config("OIDC_RP_CLIENT_ID", default="")
OIDC_RP_OIDC_RP_CLIENT_ID = config("OIDC_RP_CLIENT_ID", default="")
OIDC_RP_CLIENT_ID = config("OIDC_RP_CLIENT_ID", default="")
OIDC_RP_CLIENT_SECRET = config("OIDC_RP_CLIENT_SECRET", default="")
OIDC_RP_CLIENT_SECRET_ENCODED = config(
"OIDC_RP_CLIENT_SECRET_ENCODED", default=True, cast=bool
)
OIDC_CALLBACK_CLASS = "moderator.moderate.views.OIDCCallbackView"
OIDC_USERNAME_ALGO = _username_algo
OIDC_STORE_ACCESS_TOKEN = config("OIDC_STORE_ACCESS_TOKEN", default=True, cast=bool)
OIDC_RP_SIGN_ALGO = config("OIDC_RP_SIGN_ALGO", default="RS256")
OIDC_OP_JWKS_ENDPOINT = config("OIDC_OP_JWKS_ENDPOINT", default="")
OIDC_RP_SCOPES = "openid email profile"
OIDC_USE_NONCE = config("OIDC_USE_NONCE", default=True, cast=bool)
# Allowed groups a user must have to login
ALLOWED_LOGIN_GROUPS = [
"team_moco",
"team_mofo",
"team_mozillaonline",
"team_pocket",
"team_mzla",
"team_mzvc",
"team_mzai",
"hris_is_staff",
"mozilliansorg_nda",
"mozilliansorg_contingentworkernda",
]
# Django 3.2 Autofield
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
# Enable dev login
ENABLE_DEV_LOGIN = config("ENABLE_DEV_LOGIN", default=False, cast=bool)
# Enable debug toolbar
ENABLE_DEBUG_TOOLBAR = config("ENABLE_DEBUG_TOOLBAR", default=False, cast=bool)
def show_toolbar_callback(*args):
return DEBUG and ENABLE_DEBUG_TOOLBAR
SHOW_DEBUG_TOOLBAR = show_toolbar_callback()
if SHOW_DEBUG_TOOLBAR:
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": "moderator.settings.show_toolbar_callback"
}
INSTALLED_APPS = INSTALLED_APPS + ["debug_toolbar"]
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
# Email setup
FROM_NOREPLY = config(
"FROM_NOREPLY",
default="Mozilla Moderator <no-reply@moderator.mozilla.org>",
)
# Django Axes
# AXES_ENABLED = config("AXES_ENABLED", default=True, cast=bool)
# AXES_IPWARE_PROXY_COUNT = config("AXES_IPWARE_PROXY_COUNT", default=1, cast=int)
# AXES_IPWARE_META_PRECEDENCE_ORDER = [
# "HTTP_X_FORWARDED_FOR",
# "REMOTE_ADDR",
# ]
if DEV and DEBUG:
EMAIL_LOGGING_REAL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "mailcatcher"
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_PORT = 1025
EMAIL_USE_TLS = False
else:
# AWS SES configuration
EMAIL_BACKEND = "django_ses.SESBackend"
AWS_ACCESS_KEY_ID = config("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = config("AWS_SECRET_ACCESS_KEY")
AWS_SES_REGION_NAME = config("AWS_DEFAULT_REGION", default="us-west-2")
AWS_SES_REGION_ENDPOINT = config(
"AWS_REGION_ENDPOINT", default="email.us-west-2.amazonaws.com"
)