class AWS: """AWS configuration""" # If you all you know is the queue *name* and its AWS region, # make the URL be: # aws://https://sqs.$NAME_OF_REGION.amazonaws.com/$NAME_OF_QUEUE SQS_QUEUE_URL = values.URLValue( 'https://sqs.us-west-2.amazonaws.com/927034868273/buildhub-s3-events') S3_BUCKET_URL = values.URLValue( 'https://s3-us-west-2.amazonaws.com/buildhub-sqs-test') # For more details, see: # http://boto3.readthedocs.io/en/latest/reference/services/sqs.html#SQS.Queue.receive_messages # The duration (in seconds) for which the call waits for a message # to arrive in the queue before returning. SQS_QUEUE_WAIT_TIME_SECONDS = values.IntegerValue(10) # The duration (in seconds) that the received messages are hidden # from subsequent retrieve requests after being retrieved by # a ReceiveMessage request. # Note! This only really matters when multiple concurrent consumers run # daemons that consume the queue. SQS_QUEUE_VISIBILITY_TIMEOUT = values.IntegerValue(5) # The maximum number of messages to return. # Valid values are 1 to 10. Default is 1. SQS_QUEUE_MAX_NUMBER_OF_MESSAGES = values.IntegerValue(1)
class AWS: """AWS configuration""" # If you all you know is the queue *name* and its AWS region, # make the URL be: # aws://https://sqs.$NAME_OF_REGION.amazonaws.com/$NAME_OF_QUEUE SQS_QUEUE_URL = values.URLValue( "https://sqs.us-west-2.amazonaws.com/927034868273/buildhub-s3-events") S3_BUCKET_URL = values.URLValue( "https://s3-us-east-1.amazonaws.com/" "net-mozaws-prod-delivery-inventory-us-east-1") # For more details, see: # http://boto3.readthedocs.io/en/latest/reference/services/sqs.html#SQS.Queue.receive_messages # The duration (in seconds) for which the call waits for a message # to arrive in the queue before returning. SQS_QUEUE_WAIT_TIME_SECONDS = values.IntegerValue(10) # The duration (in seconds) that the received messages are hidden # from subsequent retrieve requests after being retrieved by # a ReceiveMessage request. # Note! This only really matters when multiple concurrent consumers run # daemons that consume the queue. SQS_QUEUE_VISIBILITY_TIMEOUT = values.IntegerValue(5) # The maximum number of messages to return. # Valid values are 1 to 10. Default is 1. SQS_QUEUE_MAX_NUMBER_OF_MESSAGES = values.IntegerValue(1) # When we ingest the SQS queue we get a payload that contains an S3 key and # a S3 bucket name. We then assume that we can use our boto client to connect # to that bucket to read the key to download its file. That S3 bucket name # comes at runtime so it depends on the payloads which aren't know yet. # However, if you *do* know the bucket set this variable in advance so access # to it can be healthchecked. # Note that it's optional! Unset by default. # In real product it should probably be: # https://s3.amazonaws.com/net-mozaws-prod-delivery-firefox SQS_S3_BUCKET_URL = values.URLValue() # If, the S3 bucket that SQS mentioned by name is a public you can connect # to is with an unsigned client. If you don't do this, the request might # fail with: # "An error occurred (403) when calling the HeadObject operation: Forbidden" # If however, like during local development, you use a non-public bucket this # need to be set to false. UNSIGNED_SQS_S3_CLIENT = values.BooleanValue(True)
class DandiMixin(ConfigMixin): WSGI_APPLICATION = 'dandiapi.wsgi.application' ROOT_URLCONF = 'dandiapi.urls' BASE_DIR = Path(__file__).resolve(strict=True).parent.parent REST_FRAMEWORK_EXTENSIONS = {'DEFAULT_PARENT_LOOKUP_KWARG_NAME_PREFIX': ''} ACCOUNT_EMAIL_VERIFICATION = 'none' @staticmethod def before_binding(configuration: Type[ComposedConfiguration]): # Install local apps first, to ensure any overridden resources are found first configuration.INSTALLED_APPS = [ 'dandiapi.api.apps.PublishConfig', ] + configuration.INSTALLED_APPS # Install additional apps configuration.INSTALLED_APPS += [ 'guardian', 'allauth.socialaccount.providers.github', ] configuration.AUTHENTICATION_BACKENDS += [ 'guardian.backends.ObjectPermissionBackend' ] configuration.REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] += [ # TODO remove TokenAuthentication, it is only here to support # the setTokenHack login workaround 'rest_framework.authentication.TokenAuthentication', ] configuration.REST_FRAMEWORK[ 'DEFAULT_PAGINATION_CLASS'] = 'dandiapi.api.views.common.DandiPagination' DANDI_DANDISETS_BUCKET_NAME = values.Value(environ_required=True) DANDI_GIRDER_API_URL = values.URLValue(environ_required=True) DANDI_GIRDER_API_KEY = values.Value(environ_required=True) DANDI_SCHEMA_VERSION = values.Value(environ_required=True) DANDI_DOI_API_URL = values.URLValue(environ=True) DANDI_DOI_API_USER = values.Value(environ=True) DANDI_DOI_API_PASSWORD = values.Value(environ=True) DANDI_DOI_API_PREFIX = values.Value(environ=True) # The CloudAMQP connection was dying, using the heartbeat should keep it alive CELERY_BROKER_HEARTBEAT = 20
def RAVEN_CONFIG(self): return { 'dsn': values.URLValue(None, environ_name='RAVEN_CONFIG_DSN'), 'string_max_length': values.IntegerValue(2000, environ_name='RAVEN_CONFIG_STRING_MAX_LENGTH') }
class Raven(object): """Report uncaught exceptions to the Sentry server.""" INSTALLED_APPS = common.Common.INSTALLED_APPS + ('raven.contrib.django.raven_compat',) RAVEN_CONFIG = { 'dsn': values.URLValue(environ_name='RAVEN_CONFIG_DSN'), 'release': __version__, }
class OIDC: """Settings related to talking to an OIDC provider for authorizing access tokens received from the client.""" # Note! We *could* just have a setting called 'OIDC_DOMAIN' and then use # https://$OIDC_DOMAIN/.well-known/openid-configuration and extract the # 'userinfo_endpoint' value from that during startup or something. # As of May 2018, the likelyhood of this URL changing and the fact that it's # the only URL we need, let's just make the setting the URL that we need # for being able to authorization by access token. OIDC_USER_ENDPOINT = values.URLValue("https://auth.mozilla.auth0.com/userinfo")
class Prod(Base): DEBUG = False INSTALLED_APPS = Base.INSTALLED_APPS + [ 'raven.contrib.django.raven_compat', ] RAVEN_CONFIG = { 'dsn': values.URLValue(environ_name='SENTRY_DSN'), 'release': Base.VERSION, 'environment': 'production', }
class Dev(Base): """Base settings for development.""" DEBUG = True # Required overrides SITE_URL = values.URLValue('http://*****:*****@example.com', 'password': '******' }) # Storage AWS_S3_REGION_NAME = '' AWS_STORAGE_BUCKET_NAME = values.Value('django') AWS_S3_ENDPOINT_URL = values.Value('http://minio:9000') AWS_ACCESS_KEY_ID = values.Value('djangos3') AWS_SECRET_ACCESS_KEY = values.Value('djangos3') AWS_S3_SECURE_URLS = values.BooleanValue(True) @property def AWS_S3_CUSTOM_DOMAIN(self): return values.Value(self.URL.netloc) # Core @property def FRONTEND_URL(self): return values.URLValue(self.SITE_URL) @property def INSTALLED_APPS(self): return super().INSTALLED_APPS + [ 'debug_toolbar', ] # Security CORS_ORIGIN_ALLOW_ALL = True SESSION_COOKIE_SECURE = False CSRF_COOKIE_SECURE = False AWS_AUTO_CREATE_BUCKET = True # Email EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # Services CELERY_BROKER_URL = values.Value('redis://redis', environ_name='CELERY_BROKER_URL') DATABASES = values.DatabaseURLValue( 'postgis://*****:*****@db:5432/django')
def RAVEN_CONFIG(self): version_path = os.path.join(Core.BASE_DIR, '__version__', 'tag') try: with open(version_path) as f: version = f.read().strip() except IOError: version = None return { 'dsn': values.URLValue(None, environ_name='RAVEN_CONFIG_DSN'), 'release': values.Value(version, environ_name='RAVEN_CONFIG_RELEASE'), }
def RAVEN_CONFIG(self): version_path = os.path.join(Core.BASE_DIR, "version.json") try: with open(version_path) as f: build_info = json.loads(f.read()) version = build_info.get("version") except IOError: version = None return { "dsn": values.URLValue(None, environ_name="RAVEN_CONFIG_DSN"), "release": values.Value(version, environ_name="RAVEN_CONFIG_RELEASE"), }
class BaseAuthConfigurationMixin: """ Basic Mixin class which all django projects using this library should include """ MAFIASI_AUTH_APPS = ["django_auth_mafiasi"] "List of apps which need to be added to django's " \ "`INSTALLED_APPS <https://docs.djangoproject.com/en/3.1/ref/settings/#installed-apps>`_" AUTH_GET_USER_FROM_ID_TOKEN_FUNCTION = "django_auth_mafiasi.auth.get_user_from_id_token" "Import path of a function which is used when a user object needs to be derived from an OpenId **ID token**" AUTH_GET_USER_FROM_ACCESS_TOKEN_FUNCTION = "django_auth_mafiasi.auth.get_user_from_access_token" "Import path of a function which is used when a user object needs to be derived from an OpenId **access token**" AUTH_USER_MODEL = "django_auth_mafiasi.MafiasiAuthModelUser" "See `django documentation <https://docs.djangoproject.com/en/3.1/ref/settings/#auth-user-model>`_." \ "If you override this, your new model should inherit from MafiasiAuthModelUser" AUTH_SERVER = values.URLValue(default="https://identity.mafiasi.de/auth/realms/mafiasi") "OpenId Issuer. This defaults to our Mafiasi server but theoretically, any OpenId Issuer can be used" AUTH_CLIENT_ID = values.Value(environ_required=True) "OpenId client id\n\n" \ "It needs to be manually retrieved from the OpenId server and uniquely identifies this application" AUTH_CLIENT_SECRET = values.SecretValue(environ_required=True) "OpenId client secret\n\n" \ "It needs to be manually retrieved from the OpenId server and authenticates this application (not the user)" AUTH_SCOPE = values.ListValue(default=["openid"]) "Scopes to request when logging a user in on the OpenId provider" AUTH_STAFF_GROUPS = values.ListValue(default=["Server-AG"]) "Which groups are considered to be staff (have access to the admin panel)" AUTH_SUPERUSER_GROUPS = values.ListValue(default=["Server-AG"]) "Which groups are considered to be superusers (have access to everything)" REST_FRAMEWORK_REQUIRED_SCOPES = values.ListValue(default=["openid"]) "Scopes to which an access token needs to have access to in order to be allowed access the an API secured by the rest framework authenticator" LOGIN_REDIRECT_URL = "/" "Where to redirect a user after successful login" LOGOUT_REDIRECT_URL = "/" "Where to redirect a user after logout" LOGIN_URL = "django_auth_mafiasi:login"
class DandiConfig(ConfigMixin): WSGI_APPLICATION = 'dandi.wsgi.application' ROOT_URLCONF = 'dandi.urls' BASE_DIR = str(Path(__file__).absolute().parent.parent) REST_FRAMEWORK_EXTENSIONS = {'DEFAULT_PARENT_LOOKUP_KWARG_NAME_PREFIX': ''} @staticmethod def before_binding(configuration: Type[ComposedConfiguration]): configuration.INSTALLED_APPS += ['dandi.publish.apps.PublishConfig'] DANDI_DANDISETS_BUCKET_NAME = values.Value(environ_required=True) DANDI_GIRDER_API_URL = values.URLValue(environ_required=True) DANDI_GIRDER_API_KEY = values.Value(environ_required=True)
class Production(Common): @property def JWT_AUTH(self): return get_jwt_auth() DEBUG = False TEMPLATE_DEBUG = DEBUG PAGE_CACHE_SECONDS = values.IntegerValue(1) # TODO: n a real production server this should have a proper url ALLOWED_HOSTS = values.ListValue(['getfission.com']) # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = values.SecretValue() # ########### Sentry configuration RAVEN_DSN = values.URLValue() RAVEN_CONFIG = {'dsn': os.environ.get('RAVEN_DSN')}
class Base(Core): """Configuration that may change per-environment, some with defaults.""" SECRET_KEY = values.SecretValue() DEBUG = values.BooleanValue(default=False) ALLOWED_HOSTS = values.ListValue([]) #: The URL under which this instance is running SITE_URL = values.URLValue("http://*****:*****@db/postgres") REDIS_URL_DEFAULT = "redis://redis:6379/1" CACHES = EnvironCacheURLValue( REDIS_URL_DEFAULT, environ_prefix=None, environ_name="REDIS_URL" ) # Use redis as the Celery broker. CELERY_BROKER_URL = os.environ.get("REDIS_URL", REDIS_URL_DEFAULT) LOGGING_USE_JSON = values.BooleanValue(False) OIDC_RP_CLIENT_ID = values.Value( environ_name="OIDC_RP_CLIENT_ID", environ_prefix=None ) OIDC_RP_CLIENT_SECRET = values.Value( environ_name="OIDC_RP_CLIENT_SECRET", environ_prefix=None ) OIDC_OP_AUTHORIZATION_ENDPOINT = values.Value( environ_name="OIDC_OP_AUTHORIZATION_ENDPOINT", environ_prefix=None ) OIDC_OP_TOKEN_ENDPOINT = values.Value( environ_name="OIDC_OP_TOKEN_ENDPOINT", environ_prefix=None ) OIDC_OP_USER_ENDPOINT = values.Value( environ_name="OIDC_OP_USER_ENDPOINT", environ_prefix=None ) OIDC_OP_DOMAIN = values.Value(environ_name="OIDC_OP_DOMAIN", environ_prefix=None) def LOGGING(self): return { "version": 1, "disable_existing_loggers": False, "formatters": { "json": { "()": "dockerflow.logging.JsonLogFormatter", "logger_name": "atmo", }, "verbose": {"format": "%(levelname)s %(asctime)s %(name)s %(message)s"}, "django.server": { "()": "django.utils.log.ServerFormatter", "format": "[%(server_time)s] %(message)s", }, }, "handlers": { "console": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "json" if self.LOGGING_USE_JSON else "verbose", }, "sentry": { "level": "ERROR", "class": "raven.contrib.django.raven_compat.handlers.SentryHandler", }, "django.server": { "level": "INFO", "class": "logging.StreamHandler", "formatter": "django.server", }, }, "loggers": { "root": {"level": "INFO", "handlers": ["sentry", "console"]}, "django.db.backends": { "level": "ERROR", "handlers": ["console"], "propagate": False, }, "django.server": { "handlers": ["django.server"], "level": "INFO", "propagate": False, }, "raven": { "level": "DEBUG", "handlers": ["console"], "propagate": False, }, "sentry.errors": { "level": "DEBUG", "handlers": ["console"], "propagate": False, }, "atmo": {"level": "DEBUG", "handlers": ["console"], "propagate": False}, "celery.task": { "level": "DEBUG", "handlers": ["console"], "propagate": False, }, "redbeat.schedulers": { "level": "DEBUG", "handlers": ["console"], "propagate": False, }, "request.summary": { "level": "DEBUG", "handlers": ["console"], "propagate": False, }, "mozilla_django_oidc": { "level": "INFO", "handlers": ["console"], "propagate": False, }, }, }
class Base(Core): """Settings that may change per-environment, some with defaults.""" # Flags that affect other settings, via setting methods below LOGGING_USE_JSON = values.BooleanValue(False) USE_OIDC = values.BooleanValue(False) # General settings DEBUG = values.BooleanValue(False) ADMINS = values.SingleNestedListValue([]) SILENCED_SYSTEM_CHECKS = values.ListValue([ # Check CSRF cookie http only. disabled because we read the # CSRF cookie in JS for forms in React. 'security.W017', ]) # Authentication def AUTHENTICATION_BACKENDS(self): if self.USE_OIDC: return ['normandy.base.auth_backends.LoggingRemoteUserBackend'] else: return ['normandy.base.auth_backends.LoggingModelBackend'] OIDC_REMOTE_AUTH_HEADER = values.Value('HTTP_REMOTE_USER') OIDC_LOGOUT_URL = values.Value(None) # Middleware that _most_ environments will need. Subclasses can override this list. EXTRA_MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ] def MIDDLEWARE(self): """ Determine middleware by combining the core set and per-environment set. """ middleware = Core.MIDDLEWARE + self.EXTRA_MIDDLEWARE if self.USE_OIDC: middleware.append('normandy.base.middleware.ConfigurableRemoteUserMiddleware') return middleware def LOGGING(self): return { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'json': { '()': 'mozilla_cloud_services_logger.formatters.JsonLogFormatter', 'logger_name': 'normandy', }, 'development': { 'format': '%(levelname)s %(asctime)s %(name)s %(message)s', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'json' if self.LOGGING_USE_JSON else 'development', }, }, 'root': { 'handlers': ['console'], 'level': 'WARNING', }, 'loggers': { 'normandy': { 'propagate': False, 'handlers': ['console'], 'level': 'DEBUG', }, 'request.summary': { 'propagate': False, 'handlers': ['console'], 'level': 'DEBUG', }, }, } # Remote services DATABASES = values.DatabaseURLValue('postgres://postgres@localhost/normandy') CONN_MAX_AGE = values.IntegerValue(0) GEOIP2_DATABASE = values.Value(os.path.join(Core.BASE_DIR, 'GeoLite2-Country.mmdb')) # Email settings EMAIL_HOST_USER = values.Value() EMAIL_HOST = values.Value() EMAIL_PORT = values.IntegerValue(587) EMAIL_USE_TLS = values.BooleanValue(True) EMAIL_HOST_PASSWORD = values.Value() EMAIL_BACKEND = values.Value('django.core.mail.backends.smtp.EmailBackend') def RAVEN_CONFIG(self): version_path = os.path.join(Core.BASE_DIR, '__version__', 'tag') try: with open(version_path) as f: version = f.read().strip() except IOError: version = None return { 'dsn': values.URLValue(None, environ_name='RAVEN_CONFIG_DSN'), 'release': values.Value(version, environ_name='RAVEN_CONFIG_RELEASE'), } # statsd STATSD_HOST = values.Value('localhost') STATSD_PORT = values.IntegerValue(8125) STATSD_IPV6 = values.BooleanValue(False) STATSD_PREFIX = values.Value('normandy') STATSD_MAXUDPSIZE = values.IntegerValue(512) # Security settings SECRET_KEY = values.SecretValue() ALLOWED_HOSTS = values.ListValue([]) AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ] PASSWORD_HASHERS = values.ListValue([ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', ]) USE_X_FORWARDED_HOST = values.BooleanValue(False) SECURE_PROXY_SSL_HEADER = values.TupleValue() SECURE_HSTS_SECONDS = values.IntegerValue(3600) SECURE_HSTS_INCLUDE_SUBDOMAINS = values.BooleanValue(True) CSRF_COOKIE_HTTPONLY = values.BooleanValue(False) CSRF_COOKIE_SECURE = values.BooleanValue(True) SECURE_SSL_REDIRECT = values.BooleanValue(True) SECURE_REDIRECT_EXEMPT = values.ListValue([]) SESSION_COOKIE_SECURE = values.BooleanValue(True) SECURE_BROWSER_XSS_FILTER = values.BooleanValue(True) SECURE_CONTENT_TYPE_NOSNIFF = values.BooleanValue(True) X_FRAME_OPTIONS = values.Value('DENY') REQUIRE_RECIPE_AUTH = values.BooleanValue(True) # Media and static settings STATIC_URL = values.Value('/static/') STATIC_ROOT = values.Value(os.path.join(Core.BASE_DIR, 'static')) MEDIA_URL = values.Value('/media/') MEDIA_ROOT = values.Value(os.path.join(Core.BASE_DIR, 'media')) STATICFILES_DIRS = ( os.path.join(Core.BASE_DIR, 'assets'), ) # URL that the CDN exists at to front cached parts of the site, if any. CDN_URL = values.URLValue(None) # URL that bypasses any CDNs APP_SERVER_URL = values.URLValue(None) # URL for the CSP report-uri directive. CSP_REPORT_URI = values.Value('/__cspreport__') # Normandy settings ADMIN_ENABLED = values.BooleanValue(True) ACTION_IMPLEMENTATION_CACHE_TIME = values.IntegerValue(60 * 60 * 24 * 365) NUM_PROXIES = values.IntegerValue(0) API_CACHE_TIME = values.IntegerValue(30) API_CACHE_ENABLED = values.BooleanValue(True) # If true, approvals must come from two separate users. If false, the same # user can approve their own request. PEER_APPROVAL_ENFORCED = values.BooleanValue(True) # Autograph settings AUTOGRAPH_URL = values.Value() AUTOGRAPH_HAWK_ID = values.Value() AUTOGRAPH_HAWK_SECRET_KEY = values.Value() AUTOGRAPH_SIGNATURE_MAX_AGE = values.IntegerValue(60 * 60 * 24 * 7) AUTOGRAPH_X5U_CACHE_BUST = values.Value(None) # How many days before expiration to warn for expired certificates CERTIFICATES_EXPIRE_EARLY_DAYS = values.IntegerValue(None) PROD_DETAILS_DIR = values.Value(os.path.join(Core.BASE_DIR, 'product_details')) # AWS settings AWS_ACCESS_KEY_ID = values.Value() AWS_SECRET_ACCESS_KEY = values.Value() AWS_STORAGE_BUCKET_NAME = values.Value() GITHUB_URL = values.Value('https://github.com/mozilla/normandy')
class DandiMixin(ConfigMixin): WSGI_APPLICATION = 'dandiapi.wsgi.application' ROOT_URLCONF = 'dandiapi.urls' BASE_DIR = Path(__file__).resolve(strict=True).parent.parent REST_FRAMEWORK_EXTENSIONS = {'DEFAULT_PARENT_LOOKUP_KWARG_NAME_PREFIX': ''} ACCOUNT_EMAIL_VERIFICATION = 'none' DANDI_ALLOW_LOCALHOST_URLS = False # Workaround for static file storage to work correctly on Django 4. # Taken from https://github.com/axnsan12/drf-yasg/issues/761#issuecomment-1031381674 STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage' @staticmethod def mutate_configuration(configuration: Type[ComposedConfiguration]): # Install local apps first, to ensure any overridden resources are found first configuration.INSTALLED_APPS = [ 'dandiapi.api.apps.PublishConfig', ] + configuration.INSTALLED_APPS # Install additional apps configuration.INSTALLED_APPS += [ 'guardian', 'allauth.socialaccount.providers.github', ] # Authentication configuration.AUTHENTICATION_BACKENDS += [ 'guardian.backends.ObjectPermissionBackend' ] configuration.REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] += [ # TODO remove TokenAuthentication, it is only here to support # the setTokenHack login workaround 'rest_framework.authentication.TokenAuthentication', ] # Permission configuration.REST_FRAMEWORK['DEFAULT_PERMISSION_CLASSES'] += [ 'dandiapi.api.permissions.IsApprovedOrReadOnly' ] # Pagination configuration.REST_FRAMEWORK[ 'DEFAULT_PAGINATION_CLASS'] = 'dandiapi.api.views.common.DandiPagination' # If this environment variable is set, the pydantic model will allow URLs with localhost # in them. This is important for development and testing environments, where URLs will # frequently point to localhost. if configuration.DANDI_ALLOW_LOCALHOST_URLS: os.environ['DANDI_ALLOW_LOCALHOST_URLS'] = 'True' DANDI_DANDISETS_BUCKET_NAME = values.Value(environ_required=True) DANDI_DANDISETS_BUCKET_PREFIX = values.Value(default='', environ=True) DANDI_DANDISETS_EMBARGO_BUCKET_NAME = values.Value(environ_required=True) DANDI_DANDISETS_EMBARGO_BUCKET_PREFIX = values.Value(default='', environ=True) DANDI_ZARR_PREFIX_NAME = values.Value(default='zarr', environ=True) DANDI_ZARR_CHECKSUM_PREFIX_NAME = values.Value(default='zarr-checksums', environ=True) # Mainly applies to unembargo DANDI_MULTIPART_COPY_MAX_WORKERS = values.IntegerValue(environ=True, default=50) # This is where the schema version should be set. # It can optionally be overwritten with the environment variable, but that should only be # considered a temporary fix. DANDI_SCHEMA_VERSION = values.Value(default='0.6.2', environ=True) DANDI_DOI_API_URL = values.URLValue(environ=True) DANDI_DOI_API_USER = values.Value(environ=True) DANDI_DOI_API_PASSWORD = values.Value(environ=True) DANDI_DOI_API_PREFIX = values.Value(environ=True) DANDI_DOI_PUBLISH = values.BooleanValue(environ=True, default=False) DANDI_WEB_APP_URL = values.URLValue(environ_required=True) DANDI_API_URL = values.URLValue(environ_required=True) DANDI_VALIDATION_JOB_INTERVAL = values.IntegerValue(environ=True, default=60) # The CloudAMQP connection was dying, using the heartbeat should keep it alive CELERY_BROKER_HEARTBEAT = 20 # Clearing out the stock `SWAGGER_SETTINGS` variable causes a Django login # button to appear in Swagger, along with a spurious "authorize" button that # doesn't work. This at least enables us to authorize to the Swagger page on # the spot, which is quite useful. # # When Brian Helba is able to resolve this problem upstream (in # django-composed-configuration) we can remove this setting. SWAGGER_SETTINGS = { 'DEFAULT_AUTO_SCHEMA_CLASS': 'dandiapi.swagger.DANDISwaggerAutoSchema', } # Some tasks working with lots of data need lots of memory, so we need to artificially lower # the number of concurrent tasks (default is 8) to keep memory usage down. CELERY_WORKER_CONCURRENCY = values.IntegerValue(environ=True, default=8) # Automatically approve new users by default AUTO_APPROVE_USERS = True
class Core(AWS, GCP, Celery, S3, GCS, Configuration): """Settings that will never change per-environment.""" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) THIS_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(THIS_DIR) VERSION = get_version(BASE_DIR) # Using the default first site found by django.contrib.sites SITE_ID = 1 INSTALLED_APPS = [ # Django apps "django.contrib.sites", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", # Project specific apps "tecken.apps.TeckenAppConfig", "tecken.symbolicate", "tecken.download", "tecken.upload", "tecken.tokens", "tecken.api", "tecken.useradmin", "tecken.benchmarking", # Third party apps "dockerflow.django", # Third party apps, that need to be listed last "mozilla_django_oidc", ] # June 2017: Notice that we're NOT adding # 'mozilla_django_oidc.middleware.RefreshIDToken'. That's because # most views in this project are expected to be called as AJAX # or from curl. So it doesn't make sense to require every request # to refresh the ID token. # Once there is a way to do OIDC ID token refreshing without needing # the client to redirect, we can enable that. # Note also, the ostensible reason for using 'RefreshIDToken' is # to check that a once-authenticated user is still a valid user. # So if that's "disabled", that's why we have rather short session # cookie age. MIDDLEWARE = ( "dockerflow.django.middleware.DockerflowMiddleware", # 'django.middleware.csrf.CsrfViewMiddleware', "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "tecken.tokens.middleware.APITokenAuthenticationMiddleware", # Important that this comes after APITokenAuthenticationMiddleware "tecken.useradmin.middleware.NotBlockedInAuth0Middleware", "whitenoise.middleware.WhiteNoiseMiddleware", ) ROOT_URLCONF = "tecken.urls" WSGI_APPLICATION = "tecken.wsgi.application" # Add the django-allauth authentication backend. AUTHENTICATION_BACKENDS = ( "django.contrib.auth.backends.ModelBackend", "mozilla_django_oidc.auth.OIDCAuthenticationBackend", ) # Internationalization # https://docs.djangoproject.com/en/1.9/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" USE_I18N = False USE_L10N = False USE_TZ = True DATETIME_FORMAT = "Y-m-d H:i" # simplified ISO format since we assume UTC STATIC_ROOT = values.Value( default=os.path.join(BASE_DIR, "frontend/build")) STATIC_URL = "/" # The default Cache-Control max-age used, WHITENOISE_MAX_AGE = values.IntegerValue(60 * 60) SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" SESSION_CACHE_ALIAS = "default" # System Checks # Override certain builtin Django system checks because we know # with confidence we do these good deeds in Nginx. # https://docs.djangoproject.com/en/1.11/ref/checks/#security SILENCED_SYSTEM_CHECKS = [ "security.W001", # Dealt with using Nginx headers "security.W002", # Dealt with using Nginx headers "security.W003", # CSRF is explicit only on the views that need it # We can't set SECURE_HSTS_INCLUDE_SUBDOMAINS since this runs under a # mozilla.org subdomain "security.W005", "security.W004", # Strict-Transport-Security is set in Nginx ] OIDC_RP_CLIENT_ID = values.SecretValue() OIDC_RP_CLIENT_SECRET = values.SecretValue() OIDC_OP_AUTHORIZATION_ENDPOINT = values.URLValue( "https://auth.mozilla.auth0.com/authorize") OIDC_OP_TOKEN_ENDPOINT = values.URLValue( "https://auth.mozilla.auth0.com/oauth/token") OIDC_OP_USER_ENDPOINT = values.URLValue( "https://auth.mozilla.auth0.com/userinfo") # Feature flag for the Auth0 Management API check that checks if users # are still valid and not blocked in Auth0's user database. ENABLE_AUTH0_BLOCKED_CHECK = values.BooleanValue(True) # There's a middleware that checks if the user is NOT blocked in # Auth0. But we don't want to do it for every single request, since # it's slowish, so we throttle that check with a cache interval. NOT_BLOCKED_IN_AUTH0_INTERVAL_SECONDS = values.IntegerValue(60 * 60 * 24) # Keep it quite short because we don't have a practical way to do # OIDC ID token renewal for this AJAX and curl heavy app. SESSION_COOKIE_AGE = values.IntegerValue(60 * 60 * 24 * 365) # Where users get redirected after successfully signing in LOGIN_REDIRECT_URL = "/?signedin=true" LOGIN_REDIRECT_URL_FAILURE = "/?signin=failed" # API Token authentication is off by default until Tecken has # gone through a security checklist. ENABLE_TOKENS_AUTHENTICATION = values.BooleanValue(True) TOKENS_DEFAULT_EXPIRATION_DAYS = values.IntegerValue(365) # 1 year # Feature flag for enabling or disabling the possible downloading # of missing symbols from Microsoft. ENABLE_DOWNLOAD_FROM_MICROSOFT = values.BooleanValue(False) # When a symbol is tried to be downloaded, and it turns out the symbol # does *not* exist in S3, we write this down so all missing symbols # can be post-processed after. # But we only need to write it down once per symbol. There's a memoizing # guard and this defines how long it should cache that it memoized. MEMOIZE_LOG_MISSING_SYMBOLS_SECONDS = values.IntegerValue(60 * 60 * 24) # Whether or not benchmarking is enabled. It's only useful to have this # enabled in environments dedicated for testing and load testing. BENCHMARKING_ENABLED = values.BooleanValue(False) # When we ask S3 for the size (if it exists) of a symbol already in S3 # this can be cached. This value determines how long we do that caching. MEMOIZE_KEY_EXISTING_SIZE_SECONDS = values.IntegerValue(60 * 60 * 24) # When we upload a .zip file, we iterate over the content and for each # file within (that isn't immediately "ignorable") we kick off a # function which figures out what (and how) to process the file. # That function involves doing a S3/GCS GET (technically ListObjectsV2), # (possible) gzipping the payload and (possibly) a S3/GCS PUT. # All of these function calls get put in a # concurrent.futures.ThreadPoolExecutor pool. This setting is about # how many of these to start, max. UPLOAD_FILE_UPLOAD_MAX_WORKERS = values.IntegerValue(default=None) # Whether to store the missing symbols in Postgres or not. # If you disable this, at the time of writing, missing symbols # will be stored in the Redis default cache. ENABLE_STORE_MISSING_SYMBOLS = values.BooleanValue(True) # The prefix used when generating directories in the temp directory. UPLOAD_TEMPDIR_PREFIX = values.Value("raw-uploads") # When doing local development, especially load testing, it's sometimes # useful to be able to bypass all URL checks for Upload by Download. ALLOW_UPLOAD_BY_ANY_DOMAIN = values.BooleanValue(False) # This is only really meant for the sake of being overrideable by # other setting classes; in particular the 'Test' class. SYNCHRONOUS_UPLOAD_FILE_UPLOAD = False DOWNLOAD_LEGACY_PRODUCTS_PREFIXES = [ "firefox", "seamonkey", "sunbird", "thunderbird", "xulrunner", "fennec", "b2g", ]
class Base(Core): """Configuration that may change per-environment, some with defaults.""" SECRET_KEY = values.SecretValue() DEBUG = values.BooleanValue(default=False) ALLOWED_HOSTS = values.ListValue([]) #: The URL under which this instance is running SITE_URL = values.URLValue('http://*****:*****@db/postgres') REDIS_URL_DEFAULT = 'redis://redis:6379/1' CACHES = values.CacheURLValue( REDIS_URL_DEFAULT, environ_prefix=None, environ_name='REDIS_URL', ) # Use redis as the Celery broker. CELERY_BROKER_URL = os.environ.get('REDIS_URL', REDIS_URL_DEFAULT) LOGGING_USE_JSON = values.BooleanValue(False) def LOGGING(self): return { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'json': { '()': 'dockerflow.logging.JsonLogFormatter', 'logger_name': 'atmo', }, 'verbose': { 'format': '%(levelname)s %(asctime)s %(name)s %(message)s', }, 'django.server': { '()': 'django.utils.log.ServerFormatter', 'format': '[%(server_time)s] %(message)s', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'json' if self.LOGGING_USE_JSON else 'verbose', }, 'sentry': { 'level': 'ERROR', 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', }, 'django.server': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'django.server', }, }, 'loggers': { 'root': { 'level': 'INFO', 'handlers': ['sentry', 'console'], }, 'django.db.backends': { 'level': 'ERROR', 'handlers': ['console'], 'propagate': False, }, 'django.server': { 'handlers': ['django.server'], 'level': 'INFO', 'propagate': False, }, 'raven': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'sentry.errors': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'atmo': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'celery.task': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'redbeat.schedulers': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'request.summary': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, }, }
class Base(Core, CORS, OIDC, Metrics): """Settings that may change per-environment, some with defaults.""" # Flags that affect other settings, via setting methods below LOGGING_USE_JSON = values.BooleanValue(False) USE_OIDC = values.BooleanValue(False) # General settings DEBUG = values.BooleanValue(False) ADMINS = values.SingleNestedListValue([]) SILENCED_SYSTEM_CHECKS = values.ListValue([ # We've subclassed Django's security middleware, so Django's # checks can't tell we are using the middleware. "security.W001", # We have disabled CSRF as we do not use cookie-based authentication. # As a result the CSRF middleware check is not required. "security.W003", ]) # Authentication def AUTHENTICATION_BACKENDS(self): if self.USE_OIDC: return ["normandy.base.auth_backends.EmailOnlyRemoteUserBackend"] else: return ["normandy.base.auth_backends.LoggingModelBackend"] OIDC_REMOTE_AUTH_HEADER = values.Value("HTTP_REMOTE_USER") # Middleware that _most_ environments will need. Subclasses can override this list. EXTRA_MIDDLEWARE = [ "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", ] def MIDDLEWARE(self): """ Determine middleware by combining the core set and per-environment set. """ middleware = Core.MIDDLEWARE + self.EXTRA_MIDDLEWARE if self.USE_OIDC: middleware.append( "normandy.base.middleware.ConfigurableRemoteUserMiddleware") return middleware def LOGGING(self): return { "version": 1, "disable_existing_loggers": False, "formatters": { "json": { "()": "dockerflow.logging.JsonLogFormatter", "logger_name": "normandy" }, "development": { "format": "%(levelname)s %(asctime)s %(name)s %(message)s" }, }, "handlers": { "console": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "json" if self.LOGGING_USE_JSON else "development", } }, "root": { "handlers": ["console"], "level": "WARNING" }, "loggers": { "normandy": { "propagate": False, "handlers": ["console"], "level": "DEBUG" }, "request.summary": { "propagate": False, "handlers": ["console"], "level": "DEBUG" }, }, } # Remote services DATABASES = values.DatabaseURLValue( "postgres://postgres@localhost/normandy") CONN_MAX_AGE = values.IntegerValue(0) GEOIP2_DATABASE = values.Value( os.path.join(Core.BASE_DIR, "GeoLite2-Country.mmdb")) # Email settings EMAIL_HOST_USER = values.Value() EMAIL_HOST = values.Value() EMAIL_PORT = values.IntegerValue(587) EMAIL_USE_TLS = values.BooleanValue(True) EMAIL_HOST_PASSWORD = values.Value() EMAIL_BACKEND = values.Value("django.core.mail.backends.smtp.EmailBackend") def RAVEN_CONFIG(self): version_path = os.path.join(Core.BASE_DIR, "version.json") try: with open(version_path) as f: build_info = json.loads(f.read()) version = build_info.get("version") except IOError: version = None return { "dsn": values.URLValue(None, environ_name="RAVEN_CONFIG_DSN"), "release": values.Value(version, environ_name="RAVEN_CONFIG_RELEASE"), } # statsd STATSD_HOST = values.Value("localhost") STATSD_PORT = values.IntegerValue(8125) STATSD_IPV6 = values.BooleanValue(False) STATSD_PREFIX = values.Value("normandy") STATSD_MAXUDPSIZE = values.IntegerValue(512) # Security settings SECRET_KEY = values.SecretValue() ALLOWED_HOSTS = values.ListValue([]) AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" }, { "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator" }, { "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator" }, { "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator" }, ] PASSWORD_HASHERS = values.ListValue([ "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", ]) USE_X_FORWARDED_HOST = values.BooleanValue(False) SECURE_PROXY_SSL_HEADER = values.TupleValue() SECURE_HSTS_SECONDS = values.IntegerValue(3600) SECURE_HSTS_INCLUDE_SUBDOMAINS = values.BooleanValue(True) CSRF_COOKIE_HTTPONLY = values.BooleanValue(True) CSRF_COOKIE_SECURE = values.BooleanValue(True) SECURE_SSL_REDIRECT = values.BooleanValue(True) SECURE_REDIRECT_EXEMPT = values.ListValue([]) SESSION_COOKIE_SECURE = values.BooleanValue(True) SECURE_BROWSER_XSS_FILTER = values.BooleanValue(True) SECURE_CONTENT_TYPE_NOSNIFF = values.BooleanValue(True) X_FRAME_OPTIONS = values.Value("DENY") REQUIRE_RECIPE_AUTH = values.BooleanValue(True) # Media and static settings STATIC_URL = values.Value("/static/") STATIC_ROOT = values.Value(os.path.join(Core.BASE_DIR, "static")) MEDIA_URL = values.Value("/media/") MEDIA_ROOT = values.Value(os.path.join(Core.BASE_DIR, "media")) STATICFILES_DIRS = (os.path.join(Core.BASE_DIR, "assets"), ) # URL that the CDN exists at to front cached parts of the site, if any. CDN_URL = values.URLValue(None) # URL that bypasses any CDNs APP_SERVER_URL = values.URLValue(None) # URL for the CSP report-uri directive. CSP_REPORT_URI = values.Value("/__cspreport__") # Normandy settings ADMIN_ENABLED = values.BooleanValue(True) IMMUTABLE_CACHE_TIME = values.IntegerValue(60 * 60 * 24 * 365) NUM_PROXIES = values.IntegerValue(0) API_CACHE_TIME = values.IntegerValue(30) API_CACHE_ENABLED = values.BooleanValue(True) PERMANENT_REDIRECT_CACHE_TIME = values.IntegerValue(60 * 60 * 24 * 30) HTTPS_REDIRECT_CACHE_TIME = values.IntegerValue(60 * 60 * 24 * 30) # If true, approvals must come from two separate users. If false, the same # user can approve their own request. PEER_APPROVAL_ENFORCED = values.BooleanValue(True) # Autograph settings AUTOGRAPH_URL = values.Value() AUTOGRAPH_HAWK_ID = values.Value() AUTOGRAPH_HAWK_SECRET_KEY = values.Value() AUTOGRAPH_SIGNATURE_MAX_AGE = values.IntegerValue(60 * 60 * 24 * 7) AUTOGRAPH_X5U_CACHE_BUST = values.Value(None) # Remote Settings connection configuration REMOTE_SETTINGS_URL = values.Value() REMOTE_SETTINGS_USERNAME = values.Value() REMOTE_SETTINGS_PASSWORD = values.Value() REMOTE_SETTINGS_BUCKET_ID = values.Value("main-workspace") REMOTE_SETTINGS_COLLECTION_ID = values.Value("normandy-recipes") REMOTE_SETTINGS_RETRY_REQUESTS = values.IntegerValue(3) # How many days before expiration to warn for expired certificates CERTIFICATES_EXPIRE_EARLY_DAYS = values.IntegerValue(None) CERTIFICATES_CHECK_VALIDITY = values.BooleanValue(True) CERTIFICATES_EXPECTED_ROOT_HASH = values.Value(None) CERTIFICATES_EXPECTED_SUBJECT_CN = values.Value( "normandy.content-signature.mozilla.org") # Storage settings DEFAULT_FILE_STORAGE = values.Value( "normandy.base.storage.NormandyS3Boto3Storage") PROD_DETAILS_DIR = values.Value( os.path.join(Core.BASE_DIR, "product_details")) # AWS settings AWS_ACCESS_KEY_ID = values.Value() AWS_SECRET_ACCESS_KEY = values.Value() AWS_STORAGE_BUCKET_NAME = values.Value() GS_BUCKET_NAME = values.Value() GS_DEFAULT_ACL = values.Value("publicRead") GITHUB_URL = values.Value("https://github.com/mozilla/normandy")
class Base(Configuration): ''' All configurations should sublcass this base class. This contains all that is required, aside from custom endpoints that will vary per deployment and/or environment. Defaults have been set to err on the side of caution, so DEBUG, ADMIN, etc will have to be explictly turned on where necessary. ''' # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # THIS IS JUST A DEFAULT THAT WAS GENERATED FOR LOCAL DEVELOPMENT # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = values.Value('abceasyas123') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = values.BooleanValue(False) ADMIN_ENABLED = values.BooleanValue(False) SESSION_EXPIRE_AT_BROWSER_CLOSE = values.BooleanValue(True) AUTH_USER_MODEL = values.Value('users.User') MEMBERSHIP_ENCODE_KEY = values.Value('') MEMBERSHIP_RENEWAL_URL_BASE = values.URLValue('') SHARED_SESSION_SITES = values.ListValue([]) SESSION_COOKIE_DOMAIN = values.Value() SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') ALLOWED_HOSTS = values.ListValue([]) SITE_ID = values.IntegerValue(1) ADMINS = [('Sir Terence', '*****@*****.**')] SERVER_EMAIL = '*****@*****.**' # SESSION_COOKIE_AGE = 60*60*24 X_FRAME_OPTIONS = 'ALLOW' INSTALLED_APPS = values.ListValue([ # Django packages 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', 'django.contrib.sitemaps', # External packages 'captcha', 'debug_toolbar', 'django_jinja', 'raven.contrib.django.raven_compat', 'rest_framework_swagger', 'rest_framework.authtoken', 'rest_framework', 'rosetta', 'shared_session', 'storages', 'webpack_loader', 'cacheops', 'robots', 'import_export', # Application packages 'clublink.base', 'clublink.certificates', 'clublink.clubs', 'clublink.cms', 'clublink.corp', 'clublink.landings', 'clublink.users', 'clublink.emails', ]) MIDDLEWARE = values.ListValue([ 'debug_toolbar.middleware.DebugToolbarMiddleware', # Custom middleware 'clublink.base.middleware.HostnameRoutingMiddleware', 'clublink.base.middleware.ShortCircuitMiddleware', # Django middleware 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # Sites middleware 'django.contrib.sites.middleware.CurrentSiteMiddleware', # Custom middleware 'clublink.base.middleware.SpoofedUserMiddleware', 'clublink.base.middleware.ScaffoldingMiddleware', 'clublink.base.middleware.LocaleMiddleware' ]) ROOT_URLCONF = values.Value('clublink.urls.common') TEMPLATES = values.ListValue([ { 'BACKEND': 'django_jinja.backend.Jinja2', 'DIRS': [ 'templates', ], 'APP_DIRS': True, 'OPTIONS': { 'match_regex': '.+(\.jinja|\.txt)', 'match_extension': None, 'extensions': DEFAULT_EXTENSIONS + [ 'webpack_loader.contrib.jinja2ext.WebpackExtension', 'jinja2.ext.i18n', 'cacheops.jinja2.cache', 'clublink.base.extensions.SharedSession', ], 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', # Custom context processors 'clublink.base.context_processors.globals' ], } }, { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, } ]) WSGI_APPLICATION = values.Value('clublink.wsgi.application') CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': values.Value('redis://127.0.0.1:6379/1', environ_name='CACHES_DEFAULT_LOCATION'), 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', } } } # Database # https://docs.djangoproject.com/en/1.11/ref/settings/# # As per: https://github.com/kennethreitz/dj-database-url#url-schema ### <-----------------------------------------------------------------> ### ### NOTE!!!! THIS IS THE ONE VALUE THAT IS NOT PREFIXED WITH DJANGO_ ### # DATABASE_DICT = values.DictValue() # LEGACY_DATABASE_DICT = values.DictValue() ### <-----------------------------------------------------------------> ### DATABASE_ENGINE = values.Value("django.db.backends.mysql") DATABASE_NAME = values.Value() DATABASE_USER = values.Value() DATABASE_PASSWORD = values.Value() DATABASE_HOST = values.Value() DATABASE_PORT = values.Value('3306') LEGACY_DATABASE_ENGINE = values.Value("django.db.backends.mysql") LEGACY_DATABASE_NAME = values.Value() LEGACY_DATABASE_USER = values.Value() LEGACY_DATABASE_PASSWORD = values.Value() LEGACY_DATABASE_HOST = values.Value() LEGACY_DATABASE_PORT = values.Value('3306') @property def DATABASES(self): DATABASES = { 'default': { 'ENGINE': self.DATABASE_ENGINE, 'NAME': self.DATABASE_NAME, 'USER': self.DATABASE_USER, 'PASSWORD': self.DATABASE_PASSWORD, 'HOST': self.DATABASE_HOST, 'PORT': self.DATABASE_PORT } } return DATABASES # Password validation # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = values.ListValue([ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]) # Internationalization # https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = values.Value('en') LANGUAGES = values.SingleNestedTupleValue(( ('en', _('English')), ('fr', _('French')), )) LOCALE_PATHS = values.SingleNestedTupleValue( (os.path.join(BASE_DIR, 'locale'), )) TIME_ZONE = values.Value('America/Toronto') USE_I18N = values.BooleanValue(True) USE_L10N = values.BooleanValue(True) USE_TZ = values.BooleanValue(True) # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.11/howto/static-files/ DEFAULT_FILE_STORAGE = values.Value( 'django.contrib.staticfiles.storage.StaticFilesStorage') STATICFILES_DIRS = (os.path.join(BASE_DIR, 'assets'), ) # STATIC # STATIC_URL = values.Value('/static/') STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATICFILES_LOCATION = values.Value('static') STATICFILES_STORAGE = values.Value( 'django.contrib.staticfiles.storage.StaticFilesStorage') # ASSETS # ASSETS_URL = values.Value('/asset_files/') ASSETS_ROOT = os.path.join(BASE_DIR, 'asset_files') ASSETS_LOCATION = values.Value('assets') ASSETS_FILE_STORAGE = values.Value( 'django.contrib.staticfiles.storage.StaticFilesStorage') # MEDIA # MEDIA_URL = values.Value('/media/') MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_LOCATION = values.Value('media') # WEBPACK # WEBPACK_LOADER = values.DictValue({ 'DEFAULT': { 'BUNDLE_DIR_NAME': 'bundles/', 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json') }, }) SWAGGER_SETTINGS = values.DictValue({ 'DOC_EXPANSION': 'list', 'JSON_EDITOR': True, }) CSRF_COOKIE_HTTPONLY = values.BooleanValue(True) SECURE_REDIRECT_EXEMPT = values.ListValue([r'^__health__/$']) GIFT_CERTIFICATE_SITE_URL = values.URLValue() CORP_SITE_URL = values.URLValue() CLUB_SITE_URL = values.Value() ADMIN_SITE_URL = values.URLValue() ADMIN_HOSTNAME = values.RegexValue(r'^admin\.') CORP_HOSTNAME = values.RegexValue(r'^(www\.)?') API_HOSTNAME = values.RegexValue(r'^api\.') GIFT_CERTIFICATE_HOSTNAME = values.RegexValue(r'^giftcertificates\.') GIFT_CARDS_HOSTNAME = values.RegexValue(r'^giftcards\.') def HOSTNAME_URLCONFS(self): return ( (self.ADMIN_HOSTNAME, 'clublink.urls.admin'), (self.CORP_HOSTNAME, 'clublink.urls.corp'), (self.API_HOSTNAME, 'clublink.urls.api'), (self.GIFT_CERTIFICATE_HOSTNAME, 'clublink.urls.gc'), (self.GIFT_CARDS_HOSTNAME, 'clublink.urls.gift_cards'), ) def HOSTNAME_LANGUAGES(self): return ( (self.ADMIN_HOSTNAME, ('en', )), (self.GIFT_CERTIFICATE_HOSTNAME, ('en', )), ) VPN_PROTECTED_VIEWS_ENABLED = values.BooleanValue(True) VPN_IP_ADDRESS = values.Value('10.8.0.1') EMAIL_HOST = values.Value() EMAIL_PORT = values.IntegerValue(587) EMAIL_HOST_USER = values.Value() EMAIL_HOST_PASSWORD = values.Value() EMAIL_USE_TLS = values.BooleanValue(True) DEFAULT_FROM_EMAIL_ADDRESS = values.EmailValue('*****@*****.**') MEMBER_SERVICES_EMAIL_ADDRESS = values.EmailValue( '*****@*****.**') GIFT_CERTIFICATE_EMAIL_ADDRESS = values.EmailValue( '*****@*****.**') CORPORATE_EVENTS_EMAIL_ADDRESS = values.EmailValue( '*****@*****.**') MEMBERSHIP_SALES_EMAIL_ADDRESS = values.EmailValue( '*****@*****.**') EVENTS_EMAIL_ADDRESSES = values.ListValue([ '*****@*****.**', '*****@*****.**', '*****@*****.**', ]) IBS_API_WSDL = values.Value() IBS_API_USER = values.Value() IBS_API_PASSWORD = values.Value() IBS_WEBRES_API_ROOT = values.Value() IBS_WEBRES_API_USER = values.Value() IBS_WEBRES_API_PASSWORD = values.Value() GOOGLE_MAPS_API_KEY = values.Value() GOOGLE_ANALYTICS_TRACKING_ID = values.Value() DEFAULT_CERTIFICATE_EMPLOYEE_NUMBER = values.Value() DEFAULT_CERTIFICATE_MEMBERSHIP_NUMBER = values.Value('') CERTIFICATES_BATCH_LIMIT = values.IntegerValue(150) DATA_UPLOAD_MAX_NUMBER_FIELDS = values.IntegerValue(1000) GIFT_CERTIFICATE_IP_WHITELIST_ENABLED = values.BooleanValue(False) GIFT_CERTIFICATE_IP_WHITELIST = values.ListValue() AES_SHARED_KEY = values.Value() DYNAMICS_HOST = values.Value() DYNAMICS_USER = values.Value() DYNAMICS_PASSWORD = values.Value() DYNAMICS_DATABASE = values.Value() NOCAPTCHA = values.BooleanValue(True) RECAPTCHA_PUBLIC_KEY = values.Value() RECAPTCHA_PRIVATE_KEY = values.Value() PASSWORD_RESET_DEBUG = values.BooleanValue(True) PASSWORD_RESET_DEBUG_EMAIL_ADDRESSES = values.ListValue() ASSETS_FILE_STORAGE = values.Value( 'django.core.files.storage.FileSystemStorage') SEARCH_ENGINE_INDEXING_DISABLED = values.BooleanValue(False) SESSION_EXPIRE_AT_BROWSER_CLOSE = values.BooleanValue(True) REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_RENDERER_CLASSES': ('rest_framework.renderers.JSONRenderer', ), 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 50, 'EXCEPTION_HANDLER': 'clublink.base.api.handlers.logging_exception_handler', } CELERY_BROKER_URL = values.Value() CELERY_RESULT_BACKEND = values.Value()
class Common(Configuration): REPO_ROOT_DIR: str = environ.Path(__file__) - 2 APPS_ROOT_DIR: str = REPO_ROOT_DIR.path('aperte') env = environ.Env() # See https://docs.djangoproject.com/en/2.0/ref/settings/ for a description # of each Django setting. # CORE SETTINGS # -------------------------------------------------------------------------- DEBUG = values.BooleanValue(False) TIME_ZONE = values.Value('UTC') USE_TZ = True if USE_TZ: from django.conf.locale.en import formats formats.DATETIME_FORMAT = values.Value( 'm/d/Y h:i:s T', environ_name='DJANGO_DATETIME_FORMAT') LANGUAGE_CODE = values.Value('en-us') LANGUAGES = (('en', _('English')), ) LOCALE_PATHS = (str(APPS_ROOT_DIR.path('locale')), ) USE_I18N = values.BooleanValue(True) USE_L10N = values.BooleanValue(True) FIXTURE_DIRS = (str(APPS_ROOT_DIR.path('fixtures')), ) WSGI_APPLICATION = 'wsgi.application' # Note: This variable is an empty list by default for security reasons. The # allowed hosts for Django to serve must be specified explicitly in # all environments. ALLOWED_HOSTS = values.ListValue([], environ_required=True) # INSTALLED APPS SETTINGS # -------------------------------------------------------------------------- DJANGO_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django_sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', ] LOCAL_APPS = [ 'aperte.base', 'aperte.users', ] THIRD_PARTY_APPS = [ # TODO: Re-enable this. # 'allauth', # 'allauth.account', # 'allauth.socialaccount', # 'allauth.socialaccount.providers.github', # 'allauth.socialaccount.providers.google', # 'allauth.socialaccount.providers.linkedin', # 'allauth.socialaccount.providers.linkedin_oauth2', 'versatileimagefield', # TODO: Re-enable this. # 'corsheaders', 'raven.contrib.django.raven_compat', 'mail_templated', ] INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS + THIRD_PARTY_APPS # URL SETTINGS # -------------------------------------------------------------------------- ROOT_URLCONF = 'aperte.urls' # ADMIN SETTINGS # -------------------------------------------------------------------------- ADMIN_URL = values.Value('admin') ADMINS = [ ("""Adam Cook""", '*****@*****.**'), ] ADMINS = values.SingleNestedTupleValue( (('SME Virtual Network Admin', '*****@*****.**'))) MANAGERS = ADMINS # AUTHENTICATION AND LOGIN SETTINGS # -------------------------------------------------------------------------- AUTH_USER_MODEL = 'users.User' AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', # TODO: Re-enable this. #'allauth.account.auth_backends.AuthenticationBackend', ] PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', ] AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 6, } }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] LOGIN_REDIRECT_URL = 'users:redirect' LOGIN_URL = 'login' # EMAIL SETTINGS # -------------------------------------------------------------------------- DEFAULT_FROM_EMAIL = values.Value( 'SME Virtual Network <*****@*****.**>') EMAIL_SUBJECT_PREFIX = values.Value('[SME Virtual Network]') EMAIL_USE_TLS = values.BooleanValue(True) SERVER_EMAIL = values.Value(DEFAULT_FROM_EMAIL) # DATABASE SETTINGS # -------------------------------------------------------------------------- DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': values.Value('', environ_name='DATABASE_NAME_DEFAULT', environ_required=True), 'USER': values.Value('', environ_name='DATABASE_USER_DEFAULT', environ_required=True), 'PASSWORD': values.SecretValue(environ_name='DATABASE_PASSWORD_DEFAULT'), 'HOST': values.Value('', environ_name='DATABASE_HOST_DEFAULT', environ_required=True), 'PORT': values.Value('5432', environ_name='DATABASE_PORT_DEFAULT'), 'ATOMIC_REQUESTS': True, 'CONN_MAX_AGE': 10 } } # TEMPLATE SETTINGS # -------------------------------------------------------------------------- TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ str(APPS_ROOT_DIR.path('templates')), ], 'OPTIONS': { 'debug': DEBUG, 'loaders': [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ], 'context_processors': [ 'aperte.base.context_processors.site_settings', 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.request', ], }, }, ] CSRF_FAILURE_VIEW = 'aperte.base.views.render_csrf_failure' # STATIC FILE SETTINGS # -------------------------------------------------------------------------- # STATIC_ROOT - The absolute path to the directory where `collectstatic` # will collect static files for deployment. This is only used during # production (not development). STATIC_ROOT = str(REPO_ROOT_DIR.path('static')) # STATICFILES_DIRS - This setting defines the additional locations the # 'staticfiles' app will traverse if the 'FileSystemFinder' finder is # enabled. STATICFILES_DIRS = (str(APPS_ROOT_DIR.path('dist')), ) STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', ) if env.bool('DJANGO_DISABLE_WHITENOISE', default=False): # Whitenoise is disabled. # This branch is for a local development where `collectstatic` will not # be called any time the styles or scripts are changed. This makes # development faster as the browser can refresh with the latest builds # of the styles and/or scripts without having to call `collectstatic` # first. STATIC_URL = '/static/' else: # Whitenoise is enabled. # This branch is for a local development environment which enables # Whitenoise to test a staging or production environment before pushing # the code to the cloud. INSTALLED_APPS = INSTALLED_APPS + [ 'whitenoise.runserver_nostatic', ] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' STATIC_HOST = values.URLValue('', environ_name='STATIC_HOST', environ_required=True) STATIC_URL = str(STATIC_HOST) + '/static/' # django-sites SETTINGS # -------------------------------------------------------------------------- # Note: Default setting is 'https' for security reasons. For development, # the setting will likely have to be 'http'. SITE_SCHEME = values.Value('https') SITE_DOMAIN = values.Value('', environ_name='SITE_DOMAIN', environ_required=True) SITE_NAME = values.Value('SME Virtual Network') SITES = { 'current': { 'domain': SITE_DOMAIN, 'scheme': SITE_SCHEME, 'name': SITE_NAME }, } SITE_ID = 'current' # MEDIA SETTINGS # -------------------------------------------------------------------------- MEDIA_ROOT = str(REPO_ROOT_DIR.path('.media')) MEDIA_URL = values.Value('{}://{}/media/'.format(SITE_SCHEME, SITE_DOMAIN)) # SECURITY SETTINGS # -------------------------------------------------------------------------- SECRET_KEY = values.SecretValue() CSRF_COOKIE_HTTPONLY = False SESSION_COOKIE_HTTPONLY = True SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_BROWSER_XSS_FILTER = True X_FRAME_OPTIONS = 'DENY' # django-log-request-id SETTINGS # -------------------------------------------------------------------------- REQUEST_ID_RESPONSE_HEADER = 'REQUEST_ID' # LOGGING SETTINGS # -------------------------------------------------------------------------- LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' }, 'request_id': { '()': 'log_request_id.filters.RequestIDFilter' } }, 'formatters': { 'complete': { 'format': '%(asctime)s:[%(levelname)s]:logger=%(name)s:request_id=%(request_id)s message="%(message)s"' }, 'simple': { 'format': '%(levelname)s:%(asctime)s: %(message)s' }, 'null': { 'format': '%(message)s', }, }, 'handlers': { 'null': { 'level': 'DEBUG', 'class': 'logging.NullHandler', }, 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'complete', 'filters': ['request_id'], }, 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' }, 'sentry': { 'level': 'ERROR', 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', 'formatter': 'complete', 'filters': ['request_id'], }, }, 'loggers': { 'django': { 'handlers': ['null'], 'propagate': False, 'level': 'INFO', }, 'django.request': { 'handlers': ['mail_admins', 'console'], 'level': 'ERROR', 'propagate': False, }, 'django.server': { 'handlers': ['console'], 'level': 'INFO', 'propagate': False, }, 'hello_world': { 'handlers': ['console'], 'level': 'INFO', 'propagate': False, }, '': { 'handlers': ['console', 'sentry'], 'level': 'ERROR', 'propagate': True, }, } } # MIDDLEWARE SETTINGS # -------------------------------------------------------------------------- MIDDLEWARE = [ # TODO: Re-enable this. # 'corsheaders.middleware.CorsMiddleware', 'log_request_id.middleware.RequestIDMiddleware', 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] # CACHE SETTINGS # -------------------------------------------------------------------------- # See https://cloud.google.com/appengine/docs/flexible/java/upgrading#memcache_service # See https://cloud.google.com/appengine/docs/flexible/python/using-redislabs-memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': values.Value('', environ_name='CACHE_URL_DEFAULT', environ_required=True), 'OPTIONS': { 'BINARY': True, 'USERNAME': values.Value('', environ_name='CACHE_USERNAME_DEFAULT', environ_required=True), 'PASSWORD': values.SecretValue(environ_name='CACHE_PASSWORD_DEFAULT'), } } } # django-allauth SETTINGS # -------------------------------------------------------------------------- ACCOUNT_ALLOW_REGISTRATION = True ACCOUNT_AUTHENTICATION_METHOD = 'username' ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION = 'mandatory' ACCOUNT_ADAPTER = 'aperte.users.adapters.AccountAdapter' SOCIALACCOUNT_ADAPTER = 'aperte.users.adapters.SocialAccountAdapter' # raven SETTINGS # -------------------------------------------------------------------------- RELEASE_VERSION = get_release() SENTRY_CLIENT = 'raven.contrib.django.raven_compat.DjangoClient' RAVEN_CONFIG = { 'dsn': values.Value('', environ_name='SENTRY_DSN'), 'environment': values.Value('production', environ_name='SENTRY_ENVIRONMENT'), 'release': RELEASE_VERSION, } SITE_INFO = { 'RELEASE_VERSION': RELEASE_VERSION, 'IS_RAVEN_INSTALLED': RAVEN_CONFIG['dsn'] is not '' }
def FRONTEND_URL(self): return values.URLValue(self.SITE_URL)
class Base(Configuration): """Base configuration for our webapps.""" # Set in environment ADMIN_USER: dict = values.DictValue() SECRET_KEY: str = values.SecretValue() # Required to override APP_NAME: str = RequiredValue() SITE_URL: str = RequiredValue() ALLOWED_HOSTS: List[str] = RequiredValue() ADMINS: List[Tuple[str, str]] = RequiredValue() MANAGERS: List[Tuple[str, str]] = RequiredValue() # Services CELERY_BROKER_URL = values.Value('redis://', environ_name='CELERY_BROKER_URL') DATABASES = values.DatabaseURLValue(environ_required=True) # Storage DEFAULT_FILE_STORAGE = 'ionata_settings.storage.MediaS3' STATICFILES_STORAGE = 'ionata_settings.storage.StaticS3' AWS_S3_REGION_NAME = values.Value(environ_required=True) AWS_STORAGE_BUCKET_NAME = values.Value(environ_required=True) AWS_S3_ENDPOINT_URL = values.URLValue(environ_required=True) AWS_ACCESS_KEY_ID = values.SecretValue() AWS_SECRET_ACCESS_KEY = values.SecretValue() # Core DEBUG = False @property def INSTALLED_APPS(self): return [ # Our defaults 'minimal_user', 'corsheaders', 'anymail', 'django_extensions', 'storages', 'django_celery_beat', 'django_celery_results', # Core django apps 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', ] MIDDLEWARE = [ 'debug_toolbar.middleware.DebugToolbarMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', ] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] ROOT_URLCONF = 'webapp.urls' WSGI_APPLICATION = 'webapp.wsgi.application' SITE_ID = 1 # i18n LANGUAGE_CODE = 'en-AU' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Auth ANONYMOUS_USER_ID = -1 AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend'] AUTH_USER_MODEL = 'minimal_user.User' AUTH_PASSWORD_VALIDATORS = [ { 'NAME': f'{_pw_validation}.UserAttributeSimilarityValidator' }, { 'NAME': f'{_pw_validation}.MinimumLengthValidator' }, { 'NAME': f'{_pw_validation}.CommonPasswordValidator' }, { 'NAME': f'{_pw_validation}.NumericPasswordValidator' }, ] # Security INTERNAL_IPS = ['127.0.0.1'] SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SESSION_COOKIE_PATH = '/backend/' CSRF_COOKIE_PATH = '/backend/' SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True AWS_AUTO_CREATE_BUCKET = False AWS_DEFAULT_ACL = 'private' AWS_BUCKET_ACL = 'private' @property def CORS_ORIGIN_WHITELIST(self): return self.ALLOWED_HOSTS @property def CSRF_TRUSTED_ORIGINS(self): return self.CORS_ORIGIN_WHITELIST # URLs MEDIA_URL = '/assets/media/' STATIC_URL = '/assets/static/' # Celery CELERY_BROKER_TRANSPORT_OPTIONS = {'visibility_timeout': 3600} # 1 hour. CELERY_RESULT_BACKEND = 'django-db' CELERY_TIMEZONE = 'UTC' CELERY_ENABLE_UTC = True CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' @property def CELERY_APP_NAME(self): return self.APP_NAME # Email @property def EMAIL_SUBJECT_PREFIX(self): return f'[Django - {self.APP_NAME}] ' @property def DEFAULT_FROM_EMAIL(self): return f'no-reply@{self.URL.hostname}' @property def SERVER_EMAIL(self): return f'no-reply@{self.URL.hostname}' @property def MAILGUN_SENDER_DOMAIN(self): return f'mailgun.{self.URL.hostname}' # Misc properties @property def URL(self): return urlparse(self.SITE_URL) @property def FRONTEND_URL(self): return self.SITE_URL
class BaseConfig(Configuration): DEBUG = values.BooleanValue(False) TEMPLATE_DEBUG = values.BooleanValue(DEBUG) DATABASES = values.DatabaseURLValue( 'postgres://%2Fvar%2Flib%2Fpostgresql/sls_blog') SECRET_KEY = values.SecretValue() CACHES = values.CacheURLValue('locmem://default') # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = (Path(__file__).parent / '../..').resolve(strict=True) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ ALLOWED_HOSTS = [] AUTH_USER_MODEL = 'slsblog_auth.User' # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', 'wagtail.contrib.forms', 'wagtail.contrib.redirects', "wagtail.contrib.search_promotions", "wagtail.contrib.modeladmin", 'wagtail.embeds', 'wagtail.sites', 'wagtail.users', 'wagtail.snippets', 'wagtail.documents', 'wagtail.images', 'wagtail.search', 'wagtail.admin', 'wagtail.core', 'modelcluster', 'taggit', 'wagtail.api.v2', 'rest_framework', "channels", "graphql_ws.django", "graphene_django", "grapple", 'sls_blog.auth', 'sls_blog.cms', 'wagtail_headless_preview', ] REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'], 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication', ) } MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wagtail.contrib.redirects.middleware.RedirectMiddleware', ] ROOT_URLCONF = 'sls_blog.urls' WAGTAIL_SITE_NAME = 'Stevenlsjr Blog' WAGTAIL_I18N_ENABLED = True LANGUAGES = WAGTAIL_CONTENT_LANGUAGES = [ ('en-us', _("English (United States)")), ('en-gb', _("English (United Kingdom)")), ('es-es', _("Spanish (Spain)")), ('es-mx', _("Spanish (Mexico)")), ] CORS_ORIGIN_WHITELIST = values.ListValue(default=[]) CORS_ORIGIN_ALLOW_ALL = values.BooleanValue(default=False) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'sls_blog.wsgi.application' # ASGI_APPLICATION = 'sls_blog.asgi.application' ASGI_APPLICATION = "graphql_ws.django.routing.application" # Password validation # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ DEFAULT_FILE_STORAGE = values.Value( 'django.core.files.storage.FileSystemStorage') STATICFILES_STORAGE = values.Value( 'django.contrib.staticfiles.storage.StaticFilesStorage') AZURE_ACCOUNT_NAME = values.Value(environ_required=False) AZURE_ACCOUNT_KEY = values.Value(environ_required=False) AZURE_CONTAINER = values.Value(environ_required=False) PUBLIC_AZURE_CONTAINER = values.Value(environ_required=False) AZURE_CONNECTION_STRING = values.Value(default=None, environ_required=False) AZURE_CUSTOM_CONNECTION_STRING = values.Value(default=None, environ_required=False) PUBLIC_AZURE_CONTAINER = values.Value(environ_required=False) STATIC_URL = values.Value('/static/') MEDIA_URL = values.Value('/media/') STATIC_ROOT = values.PathValue(BASE_DIR / '.static') MEDIA_ROOT = values.PathValue(BASE_DIR / '.media') def GRAPHENE(self): cfg = { "SCHEMA": "grapple.schema.schema", "MIDDLEWARE": ["grapple.middleware.GrappleMiddleware"], "SUBSCRIPTION_PATH": "/subscriptions" } if self.DEBUG: cfg['MIDDLEWARE'].insert( 0, 'graphene_django.debug.DjangoDebugMiddleware') return cfg GRAPPLE = { 'APPS': { "slsblog_cms": "" }, 'EXPOSE_GRAPHIQL': values.BooleanValue(True) } BASE_URL = values.URLValue('http://localhost:8000') HEADLESS_PREVIEW_CLIENT_URLS = values.DictValue({ 'default': 'http://localhost:3000/preview', }) HEADLESS_PREVIEW_LIVE = values.BooleanValue(True)
class Base(Core): """Settings that may change per-environment, some with defaults.""" # General settings DEBUG = values.BooleanValue(False) ADMINS = values.SingleNestedListValue([]) # Remote services DATABASES = values.DatabaseURLValue( 'postgres://postgres@localhost/normandy') GEOIP2_DATABASE = values.Value( os.path.join(Core.BASE_DIR, 'GeoLite2-Country.mmdb')) # Email settings EMAIL_HOST_USER = values.Value() EMAIL_HOST = values.Value() EMAIL_PORT = values.IntegerValue(587) EMAIL_USE_TLS = values.BooleanValue(True) EMAIL_HOST_PASSWORD = values.Value() EMAIL_BACKEND = values.Value('django.core.mail.backends.smtp.EmailBackend') RAVEN_CONFIG = { 'dsn': values.URLValue(None, environ_name='RAVEN_CONFIG_DSN'), } PROD_DETAILS_STORAGE = values.Value( 'normandy.recipes.storage.ProductDetailsRelationalStorage') # statsd STATSD_HOST = values.Value('localhost') STATSD_PORT = values.IntegerValue(8125) STATSD_IPV6 = values.BooleanValue(False) STATSD_PREFIX = values.Value('normandy') STATSD_MAXUDPSIZE = values.IntegerValue(512) # Security settings SECRET_KEY = values.SecretValue() ALLOWED_HOSTS = values.ListValue([]) AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator' }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator' }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator' }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator' }, ] PASSWORD_HASHERS = values.ListValue([ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', ]) USE_X_FORWARDED_HOST = values.BooleanValue(False) SECURE_PROXY_SSL_HEADER = values.TupleValue() SECURE_HSTS_SECONDS = values.IntegerValue(3600) SECURE_HSTS_INCLUDE_SUBDOMAINS = values.BooleanValue(True) CSRF_COOKIE_HTTPONLY = values.BooleanValue(True) CSRF_COOKIE_SECURE = values.BooleanValue(True) SECURE_SSL_REDIRECT = values.BooleanValue(True) SECURE_REDIRECT_EXEMPT = values.ListValue([]) SESSION_COOKIE_SECURE = values.BooleanValue(True) SECURE_BROWSER_XSS_FILTER = values.BooleanValue(True) SECURE_CONTENT_TYPE_NOSNIFF = values.BooleanValue(True) X_FRAME_OPTIONS = values.Value('DENY') # Media and static settings STATIC_URL = values.Value('/static/') STATIC_ROOT = values.Value(os.path.join(Core.BASE_DIR, 'static')) MEDIA_URL = values.Value('/media/') MEDIA_ROOT = values.Value(os.path.join(Core.BASE_DIR, 'media')) STATICFILES_STORAGE = values.Value( 'normandy.storage.GzipManifestPipelineStorage') # Overwrite old files when uploading media. DEFAULT_FILE_STORAGE = values.Value( 'storages.backends.overwrite.OverwriteStorage') # URL that the CDN exists at to front cached parts of the site, if any. CDN_URL = values.URLValue(None) # Normandy settings CAN_EDIT_ACTIONS_IN_USE = values.BooleanValue(False) ADMIN_ENABLED = values.BooleanValue(True) ACTION_IMPLEMENTATION_CACHE_TIME = values.IntegerValue(60 * 60 * 24 * 365)
class Common(Configuration): BACKEND_DAEMON = values.Value( 'http://back:8000', environ_prefix='ORE') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': values.Value('ore', environ_name='DB_NAME', environ_prefix='ORE'), 'USER': values.Value('ore', environ_name='DB_USER', environ_prefix='ORE'), 'PASSWORD': values.Value('ore', environ_name='DB_PASSWORD', environ_prefix='ORE'), 'HOST': values.Value('localhost', environ_name='DB_HOST', environ_prefix='ORE'), 'PORT': values.Value('', environ_name='DB_PORT', environ_prefix='ORE'), } } # Environment variable "SERVER" contains the host name of the server ALLOWED_HOSTS = ['localhost', '127.0.0.1', values.Value( 'xxx', environ_prefix='ORE', environ_name='SERVER')] # Environment variable "SERVER_URL" contains the full URL for the server SERVER = str(values.Value( 'xxx', environ_prefix='ORE', environ_name='SERVER_URL')) FEEDBACK_PAGE = values.URLValue( 'https://groups.google.com/forum/#!forum/ore-support', environ_prefix='ORE') TERMS_PAGE = values.Value('/about/', environ_prefix='ORE') PRIVACY_PAGE = values.Value('/privacy/', environ_prefix='ORE') AUTH_PROFILE_MODULE = 'ore.UserProfile' CORS_ORIGIN_ALLOW_ALL = True EMAIL_HOST = 'localhost' EMAIL_SUBJECT_PREFIX = '[ORE] ' LANGUAGE_CODE = 'en-en' LOGIN_REDIRECT_URL = '/projects/' LOGIN_URL = '/' REQUIRE_BASE_URL = 'script' REQUIRE_BUILD_PROFILE = '../lib/requirejs/require_build_profile.js' REQUIRE_JS = '../lib/requirejs/require-jquery.js' ROOT_URLCONF = 'ore.urls' MEDIA_ROOT = '' MEDIA_URL = '' USE_X_FORWARDED_HOST = True SEND_BROKEN_LINK_EMAILS = False SERVER_EMAIL = values.Value('NOT_SET', environ_prefix='ORE') SITE_ID = 1 SOCIAL_AUTH_URL_NAMESPACE = 'social' SOCIAL_AUTH_FIELDS_STORED_IN_SESSION = ['next', ] SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_TWITTER_KEY = values.Value('NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_TWITTER_SECRET = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_LIVE_CLIENT_ID = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_LIVE_CLIENT_SECRET = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_YAHOO_OAUTH2_KEY = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_YAHOO_OAUTH2_SECRET = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_GITHUB_KEY = values.Value('NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_GITHUB_SECRET = values.Value( 'NOT_SET', environ_prefix='ORE') SOCIAL_AUTH_PIPELINE = ( 'social.pipeline.social_auth.social_details', 'social.pipeline.social_auth.social_uid', 'social.pipeline.social_auth.auth_allowed', 'social.pipeline.social_auth.social_user', 'social.pipeline.user.get_username', # Transition to 0.7.0 installation for existing users 'social.pipeline.social_auth.associate_by_email', 'social.pipeline.user.create_user', 'social.pipeline.social_auth.associate_user', 'social.pipeline.social_auth.load_extra_data', 'social.pipeline.user.user_details' ) STATICFILES_DIRS = ('ore/static',) STATICFILES_STORAGE = 'require.storage.OptimizedStaticFilesStorage' STATIC_ROOT = 'ore/static-release/' STATIC_URL = '/static/' TEST_RUNNER = 'django.test.runner.DiscoverRunner' TIME_ZONE = 'UTC' # setting this to None doesn't work on fresh Linux systems USE_I18N = False USE_L10N = True USE_TZ = False WSGI_APPLICATION = 'ore.wsgi.application' ROBOTS_USE_SITEMAP = False STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', ) TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ) TEMPLATE_CONTEXT_PROCESSORS = ( 'django.template.context_processors.debug', 'django.core.context_processors.static', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'social.apps.django_app.context_processors.backends', 'social.apps.django_app.context_processors.login_redirect' ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'ore.middleware.HttpErrorMiddleware', ) INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', 'django.contrib.sites', 'robots', 'require', 'social.apps.django_app.default', 'tastypie', 'ore' ) AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'social.backends.google.GoogleOAuth2', 'social.backends.twitter.TwitterOAuth', 'social.backends.yahoo.YahooOAuth2', 'social.backends.open_id.OpenIdAuth', 'social.backends.live.LiveOAuth2', 'social.backends.github.GithubOAuth2' ) LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' }, 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue' }, }, 'formatters': { 'verbose': { 'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s" }, 'simple': { 'format': '%(levelname)s %(message)s' }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler' }, 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'filters': ['require_debug_false'], }, 'False': { 'level': 'DEBUG', 'class': 'logging.NullHandler', }, }, 'loggers': { 'django.request': { 'handlers': ['mail_admins', 'console'], 'level': 'ERROR', 'propagate': True, }, 'ore': { 'handlers': ['console', ], 'level': 'DEBUG', 'propagate': True, }, 'social': { 'handlers': ['console', ], 'level': 'DEBUG', 'propagate': True, }, } }
class BaseSettings(Configuration): """ Django settings for qcat project. For more information on this file, see https://docs.djangoproject.com/en/dev/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/dev/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = join(dirname(dirname(dirname(dirname(__file__))))) # SECURITY WARNING: don't run with debug turned on in production! DEBUG = values.BooleanValue(default=False) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ # Application definition INSTALLED_APPS = ( 'django.contrib.contenttypes', 'grappelli.dashboard', 'grappelli', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.sitemaps', 'django.contrib.staticfiles', 'django.contrib.humanize', 'compressor', 'cookielaw', 'corsheaders', 'django_extensions', 'django_filters', 'easy_thumbnails', 'easy_thumbnails.optimize', 'floppyforms', 'imagekit', 'maintenancemode', 'rest_framework', 'rest_framework_swagger', 'sekizai', 'wkhtmltopdf', # Custom apps 'accounts', 'api', 'approaches', 'configuration', 'qcat', 'questionnaire', 'notifications', 'sample', 'samplemulti', 'samplemodule', 'search', 'summary', 'technologies', 'unccd', 'watershed', 'wocat', 'cca', 'cbp', ) MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.middleware.locale.LocaleMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'maintenancemode.middleware.MaintenanceModeMiddleware', 'qcat.middleware.ProfilerMiddleware', ) ROOT_URLCONF = 'qcat.urls' WSGI_APPLICATION = 'qcat.wsgi.application' # Internationalization # https://docs.djangoproject.com/en/dev/topics/i18n/ LANGUAGE_CODE = 'en' LOCALE_PATHS = ( join(BASE_DIR, 'locale'), ) # The first language is the default language. # Important: If you add languages here, make sure to add a placeholder of # the infographic in src/assets/img/infographics (make a copy of the English # version) LANGUAGES = ( ('en', _('English')), ('fr', _('French')), ('es', _('Spanish')), ('ru', _('Russian')), ('zh', _('Chinese')), ('km', _('Khmer')), ('lo', _('Lao')), ('ar', _('Arabic')), ('pt', _('Portuguese')), ('af', _('Afrikaans')), ('th', _('Thai')), ('mn', _('Mongolian')), ) # languages with extraordinarily long words that need 'forced' line breaks # to remain consistent in the box-layout. WORD_WRAP_LANGUAGES = [ 'km', 'lo', 'th', ] TIME_ZONE = 'Europe/Zurich' USE_I18N = True USE_L10N = True USE_TZ = True BASE_URL = values.Value(environ_prefix='', default='https://qcat.wocat.net') # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/dev/howto/static-files/ STATIC_URL = '/static/' STATIC_ROOT = join(BASE_DIR, '..', 'static') STATICFILES_DIRS = ( join(BASE_DIR, 'static'), ) STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'compressor.finders.CompressorFinder', ) MEDIA_URL = '/upload/' MEDIA_ROOT = join(BASE_DIR, '..', 'upload') UPLOAD_VALID_FILES = { 'image': ( ('image/jpeg', 'jpg'), ('image/png', 'png'), ('image/gif', 'gif'), ), 'document': ( ('application/pdf', 'pdf'), ) } UPLOAD_MAX_FILE_SIZE = 3145728 # 3 MB UPLOAD_IMAGE_THUMBNAIL_FORMATS = ( ('default', (640, 480)), ('small', (1024, 768)), ('medium', (1440, 1080)), # 'large' is the original uploaded image. ) THUMBNAIL_ALIASES = { 'summary': { 'screen': { 'header_image': { 'size': (1542, 767), 'upscale': True, 'crop': True, 'target': (50, 50), }, 'half_height': { 'size': (640, 640), 'upscale': True, 'crop': True, }, 'map': { 'size': (560, 0) }, 'flow_chart': { 'size': (900, 0), 'upscale': False }, 'flow_chart_half_height': { 'size': (640, 640), 'upscale': True, } }, 'print': { 'header_image': { 'size': (6168, 3068), 'crop': True, 'upscale': True, }, 'half_height': { 'size': (2560, 2560), 'upscale': True, 'crop': True, }, 'map': { 'size': (2240, 0) }, 'flow_chart': { 'size': (3600, 0), 'upscale': False }, 'flow_chart_half_height': { 'size': (2560, 2560), 'upscale': True, } } } } SUMMARY_PDF_PATH = join(MEDIA_ROOT, 'summary-pdf') TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ join(BASE_DIR, 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.request', 'sekizai.context_processors.sekizai', 'qcat.context_processors.template_settings' ], } } ] AUTH_USER_MODEL = 'accounts.User' AUTHENTICATION_BACKENDS = ( 'accounts.authentication.WocatCMSAuthenticationBackend', ) REACTIVATE_WOCAT_ACCOUNT_URL = values.URLValue( environ_prefix='', default='https://wocat.net/accounts/reactivate/' ) LOGIN_URL = 'login' GRAPPELLI_ADMIN_TITLE = 'QCAT Administration' GRAPPELLI_INDEX_DASHBOARD = 'qcat.dashboard.CustomIndexDashboard' # Elasticsearch settings ES_HOST = values.Value(default='localhost', environ_prefix='') ES_PORT = values.IntegerValue(default=9200, environ_prefix='') ES_INDEX_PREFIX = values.Value(default='qcat_', environ_prefix='') # For Elasticsearch >= 2.3: https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-2.3.html # noqa ES_NESTED_FIELDS_LIMIT = values.IntegerValue(default=250, environ_prefix='') # For each language (as set in the setting ``LANGUAGES``), a language # analyzer can be specified. This helps to analyze the text in the # corresponding language for better search results. # https://www.elastic.co/guide/en/elasticsearch/reference/1.6/analysis-lang-analyzer.html # noqa ES_ANALYZERS = ( ('en', 'english'), ('es', 'spanish'), ) # https://www.elastic.co/guide/en/elasticsearch/reference/2.0/query-dsl-query-string-query.html#_reserved_characters ES_QUERY_RESERVED_CHARS = ['\\', '+', '-', '=', '&&', '||', '>', '<', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~', '*', '?', ':', '/'] MESSAGE_TAGS = { messages.INFO: 'secondary', } # Allow various formats to communicate with the API. REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework_xml.parsers.XMLParser', ), 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework_xml.renderers.XMLRenderer', 'rest_framework_csv.renderers.CSVRenderer', ), 'DEFAULT_THROTTLE_RATES': { 'anon': '10/day', 'user': '******', }, 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 25, } SWAGGER_SETTINGS = { 'DOC_EXPANSION': 'list', 'JSON_EDITOR': True, 'VALIDATOR_URL': None, } API_PAGE_SIZE = values.IntegerValue(default=25, environ_prefix='') CORS_ORIGIN_WHITELIST = values.ListValue(environ_prefix='', default=[]) DATABASES = values.DatabaseURLValue(environ_required=True) ALLOWED_HOSTS = values.ListValue(default=['localhost', '127.0.0.1']) SECRET_KEY = values.SecretValue(environ_required=True) # The base URL of the REST API used for authentication AUTH_API_URL = values.Value(environ_prefix='', default='https://www.wocat.net/api/v1/') # The username used for API login AUTH_API_USER = values.Value(environ_prefix='') # The key used for API login AUTH_API_KEY = values.Value(environ_prefix='') AUTH_API_TOKEN = values.Value(environ_prefix='') # The URL of the WOCAT authentication form. Used to handle both login # and logout AUTH_LOGIN_FORM = values.Value( environ_prefix='', default='https://dev.wocat.net/en/sitefunctions/login.html' ) AUTH_COOKIE_NAME = values.Value(default='fe_typo_user', environ_prefix='') # Specify specific editions to be used in the filter. If not specified, # always the latest edition of a configuration is used. Specify specific # editions as a dict in the env. # Example to use edition 2015 to filter Technologies: # {"technologies": "2015"} FILTER_EDITIONS = values.DictValue(default={}, environ_prefix='') # https://raw.githubusercontent.com/SeleniumHQ/selenium/master/py/CHANGES # for the latest supported firefox version. TESTING_FIREFOX_PATH = values.Value(environ_prefix='') TESTING_CHROMEDRIVER_PATH = values.Value( environ_prefix='', default='/usr/local/bin/chromedriver') TESTING_POP_BROWSER = values.BooleanValue(environ_prefix='', default=False) # Flag for caching of the whole configuration object. Sections are always cached. USE_CACHING = values.BooleanValue(default=True) # django-cache-url doesn't support the redis package of our choice, set the redis location as # common environment (dict)value. CACHES = values.DictValue(environ_prefix='') KEY_PREFIX = values.Value(environ_prefix='', default='') # If set to true, the template 503.html is displayed. MAINTENANCE_MODE = values.BooleanValue(environ_prefix='', default=False) MAINTENANCE_LOCKFILE_PATH = join(BASE_DIR, 'maintenance.lock') # "Feature toggles" IS_ACTIVE_FEATURE_MODULE = values.BooleanValue( environ_prefix='', default=True ) IS_ACTIVE_FEATURE_WATERSHED = values.BooleanValue( environ_prefix='', default=False ) IS_ACTIVE_FEATURE_MEMORY_PROFILER = values.BooleanValue( environ_prefix='', default=False ) HOST_STRING_DEV = values.Value(environ_prefix='') HOST_STRING_DEMO = values.Value(environ_prefix='') HOST_STRING_LIVE = values.Value(environ_prefix='') # touch file to reload uwsgi TOUCH_FILE_DEV = values.Value(environ_prefix='') TOUCH_FILE_DEMO = values.Value(environ_prefix='') TOUCH_FILE_LIVE = values.Value(environ_prefix='') SENTRY_DSN = values.Value(environ_prefix='') SLACK_TOKEN = values.Value(environ_prefix='') WARN_HEADER = values.Value(environ_prefix='') NEXT_MAINTENANCE = join(BASE_DIR, 'envs/NEXT_MAINTENANCE') DEPLOY_TIMEOUT = values.Value(environ_prefix='', default=900) # Settings for piwik integration. Tracking happens in the frontend # (base template) and backend (API) PIWIK_SITE_ID = values.IntegerValue(environ_prefix='', default=None) PIWIK_URL = values.Value(environ_prefix='', default='https://webstats.wocat.net/') PIWIK_AUTH_TOKEN = values.Value(environ_prefix='') PIWIK_API_VERSION = values.IntegerValue(environ_prefix='', default=1) # google webdeveloper verification GOOGLE_WEBMASTER_TOOLS_KEY = values.Value(environ_prefix='') # Google Maps Javascript API key GOOGLE_MAPS_JAVASCRIPT_API_KEY = values.Value(environ_prefix='') # Mail settings (notification mails) DEFAULT_FROM_EMAIL = '*****@*****.**' DO_SEND_EMAILS = values.BooleanValue(environ_prefix='', default=True) DO_SEND_STAFF_ONLY = values.BooleanValue(environ_prefix='', default=False) WOCAT_IMPORT_DATABASE_URL = values.Value(environ_prefix='') WOCAT_IMPORT_DATABASE_URL_LOCAL = values.Value(environ_prefix='') WOCAT_CONTACT_PAGE = values.Value( environ_prefix='', default='https://www.wocat.net/about/wocat-secretariat' ) WOCAT_MAILBOX_USER_ID = values.IntegerValue(environ_prefix='') # TODO: Temporary test of UNCCD flagging. TEMP_UNCCD_TEST = values.ListValue(environ_prefix='') CDE_SUBNET_ADDR = values.Value(environ_prefix='', default='0.0.0.') # Prevent error when submitting very large forms. Default is 1000. # (https://docs.djangoproject.com/en/2.0/ref/settings/#data-upload-max-number-fields) DATA_UPLOAD_MAX_NUMBER_FIELDS = 2000
class Base(Configuration, Celery): # Web Settings SECRET_KEY = values.SecretValue() # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'ugrryo)w9y0(*i^-zjq+%)=o^g*-0l%l*7!5qzrg3j$y3mtp*$' # Password validation # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators @property def CACHES(self): return { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': self.REDIS_URL, 'OPTIONS': { 'COMPRESSOR': 'django_redis.compressors.zlib.ZlibCompressor', # noqa 'SERIALIZER': 'django_redis.serializers.json.JSONSerializer', # noqa }, }, } AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Internationalization # https://docs.djangoproject.com/en/1.10/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = values.Value('UTC', environ_prefix=None) USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ STATIC_URL = '/static/' CONN_MAX_AGE = values.IntegerValue(60, environ_prefix=None) ALLOWED_HOSTS = values.ListValue([], environ_prefix=None) CORS_ORIGIN = values.Value(environ_prefix=None) USE_X_FORWARDED_HOST = values.BooleanValue(False, environ_prefix=None) # Application definition INSTALLED_APPS = [ 'relops_hardware_controller.apps.RelopsHardwareControllerAppConfig', 'relops_hardware_controller.api', 'django_celery_results', 'rest_framework', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'relops_hardware_controller.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'relops_hardware_controller.wsgi.application' SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_CACHE_ALIAS = 'default' REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': (), 'DEFAULT_RENDERER_CLASSES': ('rest_framework.renderers.JSONRenderer', ), 'UNAUTHENTICATED_USER': '******', } TASKCLUSTER_CLIENT_ID = values.Value(environ_prefix=None) TASKCLUSTER_ACCESS_TOKEN = values.SecretValue(environ_prefix=None) TASK_NAMES = values.ListValue([ 'ping', 'status', 'reboot', 'ipmi_off', 'ipmi_on', 'ipmi_list', 'ipmi_cycle', 'ipmi_reset', 'reimage', 'loan', 'return_loan', ], environ_prefix=None) REQUIRED_TASKCLUSTER_SCOPE_SETS = values.SingleNestedListValue( [['project:releng:roller:{}'.format(task_name)] for task_name in TASK_NAMES.value], seq_separator=',', environ_prefix=None) VALID_WORKER_ID_REGEX = values.Value('^.*', environ_prefix=None) # Worker Settings NOTIFY_EMAIL = values.Value('*****@*****.**', environ_prefix=None) NOTIFY_IRC_CHANNEL = values.Value('#roller', environ_prefix=None) BUGZILLA_URL = values.URLValue('https://bugzilla.mozilla.org', environ_prefix=None) BUGZILLA_API_KEY = values.SecretValue(environ_prefix=None) BUGZILLA_REOPEN_STATE = values.Value('REOPENED', environ_prefix=None) BUGZILLA_REBOOT_TEMPLATE = values.Value(json.dumps( dict( api_key='${api_key}', product='Infrastructure & Operations', component='DCOps', cc='${cc}', summary='${hostname} is unreachable', version='unspecified', description= 'Reboot ${hostname} ${ip}\nRequested by ${client_id}\nRelops controller action failed:${log}', blocks='${blocks}', )), environ_prefix=None) BUGZILLA_WORKER_TRACKER_TEMPLATE = values.Value(json.dumps( dict( api_key='${api_key}', product='Infrastructure & Operations', component='CIDuty', summary='[${DC}] ${alias} problem tracking', version='unspecified', alias='${alias}', )), environ_prefix=None) XEN_URL = values.URLValue('', environ_prefix=None) XEN_USERNAME = values.Value('', environ_prefix=None) XEN_PASSWORD = values.Value('', environ_prefix=None) ILO_USERNAME = values.Value('', environ_prefix=None) ILO_PASSWORD = values.Value('', environ_prefix=None) WORKER_CONFIG = JSONFileValue('', environ_prefix=None, environ_name='WORKER_CONFIG_PATH') # how many seconds to wait for a machine to go down and come back up DOWN_TIMEOUT = values.IntegerValue(60, environ_prefix=None) UP_TIMEOUT = values.IntegerValue(300, environ_prefix=None) REBOOT_METHODS = values.ListValue( [ 'ssh_reboot', 'ipmi_reset', 'ipmi_cycle', 'snmp_reboot', # snmp pdu for mac minis 'file_bugzilla_bug', # give up and file a bug ], environ_prefix=None)
class Localdev(Base): """Configuration to be used during local development and base class for testing""" # Override some useful developer settings to be True by default. ENABLE_TOKENS_AUTHENTICATION = values.BooleanValue(True) DEBUG = values.BooleanValue(default=True) DEBUG_PROPAGATE_EXCEPTIONS = values.BooleanValue(default=True) # When doing localdev, these defaults will suffice. The minio # one forces you to use/test boto3 and the old public symbols URL # forces you to use/test the symbol downloader based on requests.get(). SYMBOL_URLS = values.ListValue(["http://minio:9000/testbucket"]) # By default, upload all symbols to this when in local dev. UPLOAD_DEFAULT_URL = values.Value("http://minio:9000/testbucket") # Note! By default the value for 'UPLOAD_TRY_SYMBOLS_URL' becomes # the value of 'UPLOAD_DEFAULT_URL' but with a '/try' prefix added. # Run this much sooner in local development. UPLOAD_REATTEMPT_LIMIT_SECONDS = values.IntegerValue(60) @classmethod def post_setup(cls): super().post_setup() # in case we don't find these AWS config variables in the environment # we load them from the .env file for param in ("ACCESS_KEY_ID", "SECRET_ACCESS_KEY", "DEFAULT_REGION"): if param not in os.environ: os.environ[param] = values.Value( default="", environ_name=param, environ_prefix="AWS" ) @property def VERSION(self): # this was breaking in ci return {} output = subprocess.check_output( # Use the absolute path of 'git' here to avoid 'git' # not being the git we expect in Docker. ["/usr/bin/git", "describe", "--tags", "--always", "--abbrev=0"] ) # nosec if output: return {"version": output.decode().strip()} else: return {} MARKUS_BACKENDS = [ # Commented out, but uncomment if you want to see all the # metrics sent to markus. # { # 'class': 'markus.backends.logging.LoggingMetrics', # }, { "class": "markus.backends.datadog.DatadogMetrics", "options": { "statsd_host": "statsd", "statsd_port": 8125, "statsd_namespace": "", }, }, # {"class": "tecken.markus_extra.LogAllMetricsKeys"}, # { # 'class': 'markus.backends.logging.LoggingRollupMetrics', # 'options': { # 'logger_name': 'markus', # 'leader': 'ROLLUP', # 'flush_interval': 60 # } # }, ] # Set these to smaller numbers for the sake of more easily testing # pagination in local development. API_UPLOADS_BATCH_SIZE = 10 API_FILES_BATCH_SIZE = 20 # # Default to the test oidcprovider container for Open ID Connect # # Client ID and secret must match oidcprovider database OIDC_RP_CLIENT_ID = values.IntegerValue(1) OIDC_RP_CLIENT_SECRET = values.Value("bd01adf93cfb") # Load oidcprovider on public port 8081, without /etc/hosts changes OIDC_OP_AUTHORIZATION_ENDPOINT = values.URLValue( "http://oidc.127.0.0.1.nip.io:8081/openid/authorize" ) # The backend connects to oidcprovider on docker port 8080 # Django's URL validator, used in URLValue, doesn't like docker hostnames OIDC_OP_TOKEN_ENDPOINT = values.Value("http://oidcprovider:8080/openid/token") # Same as token switch from URLValue to Value OIDC_OP_USER_ENDPOINT = values.Value("http://oidcprovider:8080/openid/userinfo") # Allow non-SSL connection to oidcprovider OIDC_VERIFY_SSL = values.BooleanValue(False) # Disable NotBlockedInAuth0Middleware ENABLE_AUTH0_BLOCKED_CHECK = values.BooleanValue(False)
class Base(Core): """Settings that may change per-environment, some with defaults.""" SECRET_KEY = values.SecretValue() DEBUG = values.BooleanValue(default=False) DEBUG_PROPAGATE_EXCEPTIONS = values.BooleanValue(default=False) ALLOWED_HOSTS = values.ListValue([]) # The URL under which this instance is running SITE_URL = values.URLValue('http://*****:*****@db/postgres') CACHES = { 'default': django_cache_url.config( default='redis://redis-cache:6379/0', env='REDIS_URL', ), 'store': django_cache_url.config( default='redis://redis-store:6379/0', env='REDIS_STORE_URL', ) } LOGGING_USE_JSON = values.BooleanValue(False) def LOGGING(self): return { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'json': { '()': 'dockerflow.logging.JsonLogFormatter', 'logger_name': 'tecken', }, 'verbose': { 'format': '%(levelname)s %(asctime)s %(name)s %(message)s', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': ('json' if self.LOGGING_USE_JSON else 'verbose'), }, 'sentry': { 'level': 'ERROR', 'class': ('raven.contrib.django.raven_compat.handlers' '.SentryHandler'), }, }, 'loggers': { 'root': { 'level': 'INFO', 'handlers': ['sentry', 'console'], }, 'django.db.backends': { 'level': 'ERROR', 'handlers': ['console'], 'propagate': False, }, 'raven': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'sentry.errors': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'tecken': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False, }, 'request.summary': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, }, }, } SYMBOL_URLS = values.ListValue([ 'https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.' 'symbols-public/v1/', ]) # Number of seconds to wait for a symbol download. If this # trips, no error will be raised and we'll just skip using it # as a known symbol file. # The value gets cached as an empty dict for one hour. SYMBOLS_GET_TIMEOUT = values.Value(5)