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" )