""" Configuration overrides for settings.py """ import os import sys from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.contrib.messages import constants as message_constants from ivatar.settings import BASE_DIR from ivatar.settings import MIDDLEWARE from ivatar.settings import INSTALLED_APPS from ivatar.settings import TEMPLATES ADMIN_USERS = [] ALLOWED_HOSTS = ["*"] INSTALLED_APPS.extend( [ "django_extensions", "django_openid_auth", "bootstrap4", "anymail", "ivatar", "ivatar.ivataraccount", "ivatar.tools", ] ) MIDDLEWARE.extend( [ "ivatar.middleware.CustomLocaleMiddleware", ] ) # Add OpenTelemetry middleware only if feature flag is enabled # Note: This will be checked at runtime, not at import time MIDDLEWARE.insert( 0, "ivatar.middleware.MultipleProxyMiddleware", ) AUTHENTICATION_BACKENDS = ( # Enable this to allow LDAP authentication. # See INSTALL for more information. # 'django_auth_ldap.backend.LDAPBackend', "django_openid_auth.auth.OpenIDBackend", "ivatar.ivataraccount.auth.FedoraOpenIdConnect", "django.contrib.auth.backends.ModelBackend", ) TEMPLATES[0]["DIRS"].extend( [ os.path.join(BASE_DIR, "templates"), ] ) TEMPLATES[0]["OPTIONS"]["context_processors"].append( "ivatar.context_processors.basepage", ) OPENID_CREATE_USERS = True OPENID_UPDATE_DETAILS_FROM_SREG = True SOCIAL_AUTH_JSONFIELD_ENABLED = True # Fedora authentication (OIDC). You need to set these two values to use it. SOCIAL_AUTH_FEDORA_KEY = None # Also known as client_id SOCIAL_AUTH_FEDORA_SECRET = None # Also known as client_secret SITE_NAME = os.environ.get("SITE_NAME", "libravatar") IVATAR_VERSION = "1.8.0" SCHEMAROOT = "https://www.libravatar.org/schemas/export/0.2" SECURE_BASE_URL = os.environ.get( "SECURE_BASE_URL", "https://avatars.linux-kernel.at/avatar/" ) BASE_URL = os.environ.get("BASE_URL", "http://avatars.linux-kernel.at/avatar/") LOGIN_REDIRECT_URL = reverse_lazy("profile") MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294 MAX_NUM_PHOTOS = 5 MAX_NUM_UNCONFIRMED_EMAILS = 5 MAX_PHOTO_SIZE = 10485760 # in bytes MAX_PIXELS = 7000 AVATAR_MAX_SIZE = 512 JPEG_QUALITY = 85 # Robohash Performance Optimization # Enable optimized robohash implementation for 6-22x performance improvement ROBOHASH_OPTIMIZATION_ENABLED = True # I'm not 100% sure if single character domains are possible # under any tld... so MIN_LENGTH_EMAIL/_URL, might be +1 MIN_LENGTH_URL = 11 # eg. http://a.io MAX_LENGTH_URL = 255 # MySQL can't handle more than that (LP: 1018682) MIN_LENGTH_EMAIL = 6 # eg. x@x.xx MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294 BOOTSTRAP4 = { "include_jquery": False, "javascript_in_head": False, "css_url": { "href": "/static/css/bootstrap.min.css", "integrity": "sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB", "crossorigin": "anonymous", }, "javascript_url": { "url": "/static/js/bootstrap.min.js", "integrity": "", "crossorigin": "anonymous", }, "popper_url": { "url": "/static/js/popper.min.js", "integrity": "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49", "crossorigin": "anonymous", }, } if "EMAIL_BACKEND" in os.environ: EMAIL_BACKEND = os.environ["EMAIL_BACKEND"] # pragma: no cover else: if "test" in sys.argv or "collectstatic" in sys.argv: EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" else: try: ANYMAIL = { # pragma: no cover "MAILGUN_API_KEY": os.environ["IVATAR_MAILGUN_API_KEY"], "MAILGUN_SENDER_DOMAIN": os.environ["IVATAR_MAILGUN_SENDER_DOMAIN"], } EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" # pragma: no cover except Exception: # pragma: nocover # pylint: disable=broad-except EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" SERVER_EMAIL = os.environ.get("SERVER_EMAIL", "ivatar@mg.linux-kernel.at") DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL", "ivatar@mg.linux-kernel.at") try: from ivatar.settings import DATABASES except ImportError: # pragma: no cover DATABASES = [] # pragma: no cover if "default" not in DATABASES: DATABASES["default"] = { # pragma: no cover "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } if "MYSQL_DATABASE" in os.environ: DATABASES["default"] = { # pragma: no cover "ENGINE": "django.db.backends.mysql", "NAME": os.environ["MYSQL_DATABASE"], "USER": os.environ["MYSQL_USER"], "PASSWORD": os.environ["MYSQL_PASSWORD"], "HOST": "mysql", } if "POSTGRESQL_DATABASE" in os.environ: DATABASES["default"] = { # pragma: no cover "ENGINE": "django.db.backends.postgresql", "NAME": os.environ["POSTGRESQL_DATABASE"], "USER": os.environ["POSTGRESQL_USER"], "PASSWORD": os.environ["POSTGRESQL_PASSWORD"], "HOST": "postgresql", } # CI/CD config has different naming if "POSTGRES_DB" in os.environ: DATABASES["default"] = { # pragma: no cover "ENGINE": "django.db.backends.postgresql", "NAME": os.environ["POSTGRES_DB"], "USER": os.environ["POSTGRES_USER"], "PASSWORD": os.environ["POSTGRES_PASSWORD"], "HOST": os.environ["POSTGRES_HOST"], # Let Django use its default test database naming # "TEST": { # "NAME": os.environ["POSTGRES_DB"], # }, } SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer" USE_X_FORWARDED_HOST = True ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = [ "avatars.linux-kernel.at", "localhost", ] DEFAULT_AVATAR_SIZE = 80 LANGUAGES = ( ("de", _("Deutsch")), ("en", _("English")), ("ca", _("Català")), ("cs", _("Česky")), ("es", _("Español")), ("eu", _("Basque")), ("fr", _("Français")), ("it", _("Italiano")), ("ja", _("日本語")), ("nl", _("Nederlands")), ("pt", _("Português")), ("ru", _("Русский")), ("sq", _("Shqip")), ("tr", _("Türkçe")), ("uk", _("Українська")), ) MESSAGE_TAGS = { message_constants.DEBUG: "debug", message_constants.INFO: "info", message_constants.SUCCESS: "success", message_constants.WARNING: "warning", message_constants.ERROR: "danger", } CACHES = { "default": { "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", "LOCATION": [ "127.0.0.1:11211", ], # "OPTIONS": {"MAX_ENTRIES": 1000000}, }, "filesystem": { "BACKEND": "django.core.cache.backends.filebased.FileBasedCache", "LOCATION": "/var/tmp/ivatar_cache", "TIMEOUT": 900, # 15 minutes "OPTIONS": {"MAX_ENTRIES": 1000000}, }, } # This is 5 minutes caching for generated/resized images, # so the sites don't hit ivatar so much - it's what's set in the HTTP header CACHE_IMAGES_MAX_AGE = 5 * 60 CACHE_RESPONSE = True # Trusted URLs for default redirection TRUSTED_DEFAULT_URLS = [ {"schemes": ["https"], "host_equals": "ui-avatars.com", "path_prefix": "/api/"}, { "schemes": ["http", "https"], "host_equals": "gravatar.com", "path_prefix": "/avatar/", }, { "schemes": ["http", "https"], "host_suffix": ".gravatar.com", "path_prefix": "/avatar/", }, { "schemes": ["http", "https"], "host_equals": "www.gravatar.org", "path_prefix": "/avatar/", }, { "schemes": ["https"], "host_equals": "avatars.dicebear.com", "path_prefix": "/api/", }, { "schemes": ["https"], "host_equals": "api.dicebear.com", "path_prefix": "/", }, { "schemes": ["https"], "host_equals": "badges.fedoraproject.org", "path_prefix": "/static/img/", }, { "schemes": ["http"], "host_equals": "www.planet-libre.org", "path_prefix": "/themes/planetlibre/images/", }, {"schemes": ["https"], "host_equals": "www.azuracast.com", "path_prefix": "/img/"}, { "schemes": ["https"], "host_equals": "reps.mozilla.org", "path_prefix": "/static/base/img/remo/", }, ] URL_TIMEOUT = 10 def map_legacy_config(trusted_url): """ For backward compability with the legacy configuration for trusting URLs. Adapts them to fit the new config. """ if isinstance(trusted_url, str): return {"url_prefix": trusted_url} return trusted_url # Backward compability for legacy behavior TRUSTED_DEFAULT_URLS = list(map(map_legacy_config, TRUSTED_DEFAULT_URLS)) # Bluesky settings BLUESKY_IDENTIFIER = os.environ.get("BLUESKY_IDENTIFIER", None) BLUESKY_APP_PASSWORD = os.environ.get("BLUESKY_APP_PASSWORD", None) # File upload security settings FILE_UPLOAD_MAX_MEMORY_SIZE = 5 * 1024 * 1024 # 5MB DATA_UPLOAD_MAX_MEMORY_SIZE = 5 * 1024 * 1024 # 5MB FILE_UPLOAD_PERMISSIONS = 0o644 # Enhanced file upload security ENABLE_FILE_SECURITY_VALIDATION = True ENABLE_EXIF_SANITIZATION = True ENABLE_MALICIOUS_CONTENT_SCAN = True # Logging configuration - can be overridden in local config # Example: LOGS_DIR = "/var/log/ivatar" # For production deployments # This MUST BE THE LAST! if os.path.isfile(os.path.join(BASE_DIR, "config_local.py")): from config_local import * # noqa # flake8: noqa # NOQA # pragma: no cover