Exemple #1
0
class Development(Base):
    """Development environment settings.

    We set ``DEBUG`` to ``True`` by default, configure the server to respond to all hosts.
    """

    ALLOWED_HOSTS = ["*"]
    AWS_BASE_NAME = values.Value("development")
    DEBUG = values.BooleanValue(True)
    CLOUDFRONT_SIGNED_URLS_ACTIVE = values.BooleanValue(False)
    CACHES = {
        "default": {
            "BACKEND": "django.core.cache.backends.dummy.DummyCache"
        }
    }

    # Mail
    EMAIL_HOST = values.Value("mailcatcher")
    EMAIL_PORT = values.PositiveIntegerValue(1025)

    # Logging
    LOGGING = values.DictValue({
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "gelf": {
                "()": "logging_gelf.formatters.GELFFormatter",
                "null_character": True,
            },
        },
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
            },
            "gelf": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
                "formatter": "gelf",
            },
        },
        "loggers": {
            "marsha": {
                "handlers": ["console"],
                "level": "DEBUG",
                "propagate": True,
            },
            # This formatter is here as an example to what is possible to do
            # with xapi loogers.
            "xapi": {
                "handlers": ["gelf"],
                "level": "INFO",
                "propagate": True
            },
        },
    })
Exemple #2
0
class CeleryMixin:
    CELERY_ACCEPT_CONTENT = values.ListValue(['application/json'])
    CELERY_ENABLE_UTC = values.BooleanValue(True)
    CELERY_IMPORTS = values.ListValue([])
    CELERY_INCLUDE = values.ListValue([])
    CELERY_TIMEZONE = values.Value('UTC')

    CELERYBEAT_MAX_LOOP_INTERVAL = values.Value(0)
    CELERYBEAT_SCHEDULE = {}
    CELERYBEAT_SCHEDULER = values.Value('celery.beat:PersistentScheduler')
    CELERYBEAT_SCHEDULE_FILENAME = values.Value('celerybeat-schedule')
    CELERYBEAT_SYNC_EVERY = values.PositiveIntegerValue(0)

    BROKER_URL = values.Value(None)
    # BROKER_TRANSPORT
    BROKER_TRANSPORT_OPTIONS = {}
    BROKER_CONNECTION_TIMEOUT = values.FloatValue(4.0)
    BROKER_CONNECTION_RETRY = values.BooleanValue(True)
    BROKER_CONNECTION_MAX_RETRIES = values.PositiveIntegerValue(100)
    BROKER_FAILOVER_STRATEGY = values.Value('round-robin')
    BROKER_HEARTBEAT = values.FloatValue(120.0)
    BROKER_LOGIN_METHOD = values.Value('AMQPLAIN')
    BROKER_POOL_LIMIT = values.PositiveIntegerValue(10)
    BROKER_USE_SSL = values.BooleanValue(False)

    # CELERY_CACHE_BACKEND no longer used
    CELERY_CACHE_BACKEND_OPTIONS = {}

    CASSANDRA_COLUMN_FAMILY = values.Value(None)
    CASSANDRA_ENTRY_TTL = values.PositiveIntegerValue(None)
    CASSANDRA_KEYSPACE = values.Value(None)
    CASSANDRA_PORT = values.PositiveIntegerValue(9042)
    CASSANDRA_READ_CONSISTENCY = values.Value(None)
    CASSANDRA_OPTIONS = {}

    S3_ACCESS_KEY_ID = values.Value(None)
    S3_SECRET_ACCESS_KEY = values.Value(None)
    S3_BUCKET = values.Value(None)
    S3_BASE_PATH = values.Value(None)
    S3_ENDPOINT_URL = values.Value(None)
    S3_REGION = values.Value(None)

    CELERY_COUCHBASE_BACKEND_SETTINGS = {}
    CELERY_ARANGODB_BACKEND_SETTINGS = {}
    CELERY_MONGODB_BACKEND_SETTINGS = {}

    CELERY_EVENT_QUEUE_EXPIRES = values.FloatValue(60.0)
    CELERY_EVENT_QUEUE_TTL = values.FloatValue(5.0)
    CELERY_EVENT_QUEUE_PREFIX = values.Value('celeryev')
    CELERY_EVENT_SERIALIZER = values.Value('json')

    CELERY_REDIS_DB = values.Value(None)
    CELERY_REDIS_HOST = values.Value(None)
    CELERY_REDIS_MAX_CONNECTIONS = values.PositiveIntegerValue(None)
    CELERY_REDIS_PASSWORD = values.Value(None)
    CELERY_REDIS_PORT = values.PositiveIntegerValue(None)
    CELERY_REDIS_BACKEND_USE_SSL = values.BooleanValue(False)

    CELERY_RESULT_BACKEND = values.Value(None)
    CELERY_MAX_CACHED_RESULTS = values.BooleanValue(False)
    CELERY_MESSAGE_COMPRESSION = values.Value(None)

    CELERY_RESULT_EXCHANGE = values.Value(None)
    CELERY_RESULT_EXCHANGE_TYPE = values.Value(None)
    # CELERY_RESULT_EXPIRES timedelta 1 day.
    CELERY_RESULT_PERSISTENT = values.BooleanValue(False)
    CELERY_RESULT_SERIALIZER = values.Value('json')
    CELERY_RESULT_DBURI = values.Value(None)
    CELERY_RESULT_ENGINE_OPTIONS = {}
    # _DB_SHORT_LIVED_SESSIONS
    CELERY_RESULT_DB_TABLE_NAMES = values.ListValue([])
    CELERY_SECURITY_CERTIFICATE = values.Value(None)
    CELERY_SECURITY_CERT_STORE = values.Value(None)
    CELERY_SECURITY_KEY = values.Value(None)

    CELERY_ACKS_LATE = values.BooleanValue(False)
    CELERY_ACKS_ON_FAILURE_OR_TIMEOUT = values.BooleanValue(True)
    CELERY_ALWAYS_EAGER = values.BooleanValue(False)
    CELERY_ANNOTATIONS = None  # dict/list
    CELERY_COMPRESSION = values.Value(None)
    CELERY_CREATE_MISSING_QUEUES = values.BooleanValue(True)
    CELERY_DEFAULT_DELIVERY_MODE = values.Value('persistent')
    # CELERY_DEFAULT_EXCHANGE
    CELERY_DEFAULT_EXCHANGE_TYPE = values.Value('direct')
    CELERY_DEFAULT_QUEUE = values.Value('celery')
    CELERY_DEFAULT_RATE_LIMIT = values.Value(None)
    # CELERY_DEFAULT_ROUTING_KEY str
    CELERY_EAGER_PROPAGATES = values.BooleanValue(False)
    CELERY_IGNORE_RESULT = values.BooleanValue(False)
    CELERY_PUBLISH_RETRY = values.BooleanValue(True)
    # CELERY_PUBLISH_RETRY_POLICY
    CELERY_QUEUES = None
    CELERY_ROUTES = None
    CELERY_SEND_SENT_EVENT = values.BooleanValue(False)
    CELERY_SERIALIZER = values.Value('json')
    CELERYD_SOFT_TIME_LIMIT = values.PositiveIntegerValue(None)
    CELERYD_TIME_LIMIT = values.PositiveIntegerValue(None)
    CELERY_TRACK_STARTED = values.BooleanValue(False)

    CELERYD_AGENT = values.Value(None)
    CELERYD_AUTOSCALER = values.Value('celery.worker.autoscale:Autoscaler')
    CELERYD_CONCURRENCY = values.PositiveIntegerValue(None)
    CELERYD_CONSUMER = values.Value('celery.worker.consumer:Consumer')

    CELERY_WORKER_DIRECT = values.BooleanValue(False)
    CELERY_DISABLE_RATE_LIMITS = values.BooleanValue(False)
    CELERY_ENABLE_REMOTE_CONTROL = values.BooleanValue(True)
    CELERYD_HIJACK_ROOT_LOGGER = values.BooleanValue(True)
    # CELERYD_LOG_COLOR
    CELERYD_LOG_FORMAT = values.Value(
        '[%(asctime)s: %(levelname)s/%(processName)s] %(message)s')
    CELERYD_WORKER_LOST_WAIT = values.FloatValue(10.0)
    CELERYD_MAX_TASKS_PER_CHILD = values.PositiveIntegerValue(None)
    CELERYD_POOL = values.Value('prefork')
    # CELERYD_POOL_PUTLOCKS ?
    CELERYD_POOL_RESTARTS = values.BooleanValue(False)
    CELERYD_PREFETCH_MULTIPLIER = values.PositiveIntegerValue(4)
    CELERYD_REDIRECT_STDOUTS = values.BooleanValue(True)
    CELERYD_REDIRECT_STDOUTS_LEVEL = values.Value('WARNING')
    CELERY_SEND_EVENTS = values.BooleanValue(False)
    CELERYD_STATE_DB = values.Value(None)
    CELERYD_TASK_LOG_FORMAT = values.Value("""[%(asctime)s: %(levelname)s/%(processName)s]
[%(task_name)s(%(task_id)s)] %(message)s""")
    CELERYD_TIMER = values.Value(None)
    CELERYD_TIMER_PRECISION = values.FloatValue(1.0)
Exemple #3
0
class Base(Configuration):
    ########################################################################
    #
    # General django settings
    # https://docs.djangoproject.com/en/3.1/topics/settings/
    #
    ########################################################################
    INSTALLED_APPS = [
        'imagetagger.annotations',
        'imagetagger.base',
        'imagetagger.images',
        'imagetagger.users',
        'imagetagger.tools',
        'imagetagger.administration',
        'django.contrib.admin',
        'imagetagger.tagger_messages',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'widget_tweaks',
        'friendlytagloader',
    ]

    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',
        'django.middleware.locale.LocaleMiddleware',
    ]

    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',
                    'imagetagger.base.context_processors.base_data',
                ],
            },
        },
    ]

    ROOT_URLCONF = 'imagetagger.urls'
    WSGI_APPLICATION = 'imagetagger.wsgi.application'

    FILE_UPLOAD_HANDLERS = [
        "django.core.files.uploadhandler.MemoryFileUploadHandler",
        "django.core.files.uploadhandler.TemporaryFileUploadHandler",
    ]

    # Password validation
    # https://docs.djangoproject.com/en/1.10/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/1.10/topics/i18n/

    USE_I18N = True

    USE_L10N = True

    USE_TZ = True

    AUTH_USER_MODEL = 'users.User'

    PROBLEMS_URL = 'https://github.com/bit-bots/imagetagger/issues'
    PROBLEMS_TEXT = ''

    LOGIN_URL = '/user/login/'
    LOGIN_REDIRECT_URL = '/images/'

    # Flash Messages
    # https://docs.djangoproject.com/en/3.1/ref/contrib/messages/

    MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
    MESSAGE_TAGS = {
        messages.INFO: 'info',
        messages.ERROR: 'danger',
        messages.WARNING: 'warning',
        messages.SUCCESS: 'success',
    }

    # Sets the default expire time for new messages in days
    DEFAULT_EXPIRE_TIME = 7

    # Sets the default number of messages per page
    MESSAGES_PER_PAGE = 10

    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.10/howto/static-files/

    STATIC_URL = '/static/'

    EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

    ########################################################################
    #
    # Computed properties and hooks
    #
    ########################################################################
    @property
    def DATABASES(self):
        """https://docs.djangoproject.com/en/1.10/ref/settings/#databases"""
        return {
            'default': {
                'ENGINE': 'django.db.backends.postgresql_psycopg2',
                'HOST': self.DB_HOST,
                'PORT': self.DB_PORT,
                'NAME': self.DB_NAME,
                'USER': self.DB_USER,
                'PASSWORD': self.DB_PASSWORD
            }
        }

    @classmethod
    def post_setup(cls):
        super().post_setup()

        if cls.SENTRY_REPORTING_ENABLED:
            try:
                import sentry_sdk
                from sentry_sdk.integrations.django import DjangoIntegration
                sentry_sdk.init(
                    dsn=cls.SENTRY_DSN,
                    integrations=[DjangoIntegration()],
                    # If you wish to associate users to errors you may enable sending PII data.
                    send_default_pii=cls.SENTRY_SEND_DEFAULT_PII)
            except ImportError:
                raise ImproperlyConfigured(
                    "Could not import sentry although the server is configured to use it"
                )

    ########################################################################
    #
    # User-adaptable settings
    #
    ########################################################################
    DEBUG = values.BooleanValue(environ_prefix='IT', default=False)
    SECRET_KEY = values.SecretValue(environ_prefix='IT')
    DB_HOST = values.Value(environ_prefix='IT', environ_required=True)
    DB_PORT = values.PositiveIntegerValue(environ_prefix='IT', default=5432)
    DB_NAME = values.Value(environ_prefix='IT', default='imagetagger')
    DB_USER = values.Value(environ_prefix='IT', default=DB_NAME)
    DB_PASSWORD = values.Value(environ_prefix='IT', environ_required=True)
    ALLOWED_HOSTS = values.ListValue(environ_prefix='IT',
                                     environ_required=True)
    LANGUAGE_CODE = values.Value(environ_prefix='IT', default='en-us')
    TIME_ZONE = values.Value(environ_prefix='IT', default='Europe/Berlin')
    STATIC_ROOT = values.Value(environ_prefix='IT',
                               default=path_join(BASE_DIR, 'static'))
    USE_IMPRINT = values.BooleanValue(environ_prefix='IT', default=False)
    IMPRINT_NAME = values.Value(environ_prefix='IT')
    IMPRINT_URL = values.Value(environ_prefix='IT')
    # the URL where the ImageTagger is hosted e.g. https://imagetagger.bit-bots.de
    DOWNLOAD_BASE_URL = values.Value(environ_prefix='IT',
                                     environ_required=True)
    TOOLS_ENABLED = values.BooleanValue(environ_prefix='IT', default=True)
    TOOL_UPLOAD_NOTICE = values.Value(environ_prefix='IT', default='')
    ENABLE_ZIP_DOWNLOAD = values.BooleanValue(environ_prefix='IT',
                                              default=is_in_docker())
    USE_NGINX_IMAGE_PROVISION = values.BooleanValue(environ_prefix='IT',
                                                    default=is_in_docker())
    FS_URL = values.Value(environ_prefix='IT',
                          default=path_join(BASE_DIR, 'data'))
    TMP_FS_URL = values.Value(environ_prefix='IT',
                              default='temp://imagetagger')
    UPLOAD_NOTICE = values.Value(
        environ_prefix='IT',
        default='By uploading images to this tool you accept that '
        'the images get published under creative commons license '
        'and confirm that you have the permission to do so.')
    EXPORT_SEPARATOR = values.Value(environ_prefix='IT', default='|')
    # the path to the folder with the imagesets relative to the filesystem root (see FS_URL)
    IMAGE_PATH = values.Value(environ_prefix='IT', default='images')
    # the path to use for temporary image files relative to the temp filesystem (see TMP_FS_URL)
    TMP_IMAGE_PATH = values.Value(environ_prefix='IT', default='images')
    # the path to the folder with the tools relative to the filesystem root (see FS_URL)
    TOOLS_PATH = values.Value(environ_prefix='IT', default='tools')
    # filename extension of accepted imagefiles
    IMAGE_EXTENSION = values.ListValue(environ_prefix='IT',
                                       default=['png', 'jpeg'])
    ACCOUNT_ACTIVATION_DAYS = values.PositiveIntegerValue(environ_prefix='IT',
                                                          default=7)

    SENTRY_REPORTING_ENABLED = values.BooleanValue(environ_prefix='IT',
                                                   default=False)
    SENTRY_DSN = values.Value(environ_prefix='IT',
                              environ_required=SENTRY_REPORTING_ENABLED)
    SENTRY_SEND_DEFAULT_PII = values.BooleanValue(environ_prefix='IT',
                                                  default=False)
Exemple #4
0
class Base(Configuration):
    """Base configuration every configuration (aka environment) should inherit from.

    It depends on an environment variable that SHOULD be defined:
    - DJANGO_SECRET_KEY

    You may also want to override default configuration by setting the following
    environment variables:
    - DJANGO_DEBUG
    """

    DATA_DIR = values.Value(os.path.join("/", "data"))

    # Static files (CSS, JavaScript, Images)
    STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"), )
    STATIC_URL = "/static/"
    MEDIA_URL = "/media/"
    # Allow to configure location of static/media files for non-Docker installation
    MEDIA_ROOT = values.Value(os.path.join(str(DATA_DIR), "media"))
    STATIC_ROOT = values.Value(os.path.join(str(DATA_DIR), "static"))

    SECRET_KEY = values.SecretValue()

    DEBUG = values.BooleanValue(False)

    DATABASES = {
        "default": {
            "ENGINE":
            values.Value(
                "django.db.backends.postgresql",
                environ_name="DATABASE_ENGINE",
                environ_prefix=None,
            ),
            "NAME":
            values.Value("marsha",
                         environ_name="POSTGRES_DB",
                         environ_prefix=None),
            "USER":
            values.Value("marsha_user",
                         environ_name="POSTGRES_USER",
                         environ_prefix=None),
            "PASSWORD":
            values.Value("pass",
                         environ_name="POSTGRES_PASSWORD",
                         environ_prefix=None),
            "HOST":
            values.Value("localhost",
                         environ_name="POSTGRES_HOST",
                         environ_prefix=None),
            "PORT":
            values.Value(5432,
                         environ_name="POSTGRES_PORT",
                         environ_prefix=None),
        }
    }

    ALLOWED_HOSTS = []

    SITE_ID = 1

    SECURE_BROWSER_XSS_FILTER = True
    SECURE_CONTENT_TYPE_NOSNIFF = True
    X_FRAME_OPTIONS = "DENY"
    SECURE_REFERRER_POLICY = "same-origin"
    SILENCED_SYSTEM_CHECKS = values.ListValue([])
    CSRF_TRUSTED_ORIGINS = values.ListValue([])

    # Application definition

    INSTALLED_APPS = [
        "django.contrib.admin.apps.SimpleAdminConfig",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.sites",
        "django.contrib.staticfiles",
        "django_extensions",
        "dockerflow.django",
        "waffle",
        "rest_framework",
        "corsheaders",
        "marsha.core.apps.CoreConfig",
        "marsha.bbb.apps.BbbConfig",
        "marsha.websocket.apps.WebsocketConfig",
        "channels",
    ]

    MIDDLEWARE = [
        "corsheaders.middleware.CorsMiddleware",
        "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",
        "dockerflow.django.middleware.DockerflowMiddleware",
        "waffle.middleware.WaffleMiddleware",
    ]

    ROOT_URLCONF = "marsha.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",
            ]
        },
    }]

    AUTH_USER_MODEL = "core.User"

    ASGI_APPLICATION = "marsha.asgi.application"

    # For sentinels:
    # CHANNEL_LAYERS = {
    #     "default": {
    #         "BACKEND": "marsha.websocket.layers.JsonRedisChannelLayer",
    #         "CONFIG": {
    #             "hosts": [{
    #                 "sentinels": [(SENTINEL_HOST, SENTINEL_PORT)],
    #                 "master_name": SENTINEL_MASTER,
    #             }],
    #         },
    #     },
    # }
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "marsha.websocket.layers.JsonRedisChannelLayer",
            "CONFIG": {
                "hosts":
                values.ListValue([("redis", 6379)],
                                 environ_name="REDIS_HOST",
                                 environ_prefix=None),
            },
        },
    }

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":
        ("rest_framework_simplejwt.authentication.JWTTokenUserAuthentication",
         ),
        "EXCEPTION_HANDLER":
        "marsha.core.views.exception_handler",
        "DEFAULT_PAGINATION_CLASS":
        "rest_framework.pagination.LimitOffsetPagination",
        "PAGE_SIZE":
        50,
        "DEFAULT_THROTTLE_RATES": {
            "anon": "3/minute",
            "live_registration": "3/minute",
        },
    }

    # WAFFLE
    WAFFLE_CREATE_MISSING_SWITCHES = True

    # Password validation
    # https://docs.djangoproject.com/en/2.0/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"
        },
    ]

    JWT_SIGNING_KEY = values.Value(SECRET_KEY)

    # Internationalization
    # https://docs.djangoproject.com/en/2.0/topics/i18n/

    # Django sets `LANGUAGES` by default with all supported languages. Let's save it to a
    # different setting before overriding it with the languages active in Marsha. We can use it
    # for example for the choice of time text tracks languages which should not be limited to
    # the few languages active in Marsha.
    # pylint: disable=no-member
    ALL_LANGUAGES = Configuration.LANGUAGES

    LANGUAGE_CODE = "en-us"

    # Careful! Languages should be ordered by priority, as this tuple is used to get
    # fallback/default languages throughout the app.
    # Use "en" as default as it is the language that is most likely to be spoken by any visitor
    # when their preferred language, whatever it is, is unavailable
    LANGUAGES = [("en", _("english")), ("fr", _("french"))]
    LANGUAGES_DICT = dict(LANGUAGES)

    # Internationalization
    TIME_ZONE = "UTC"
    USE_I18N = True
    USE_L10N = True
    USE_TZ = True

    REACT_LOCALES = values.ListValue(["en_US", "es_ES", "fr_FR", "fr_CA"])
    DEFAULT_LTI_LAUNCH_PRESENTATION_LOCALE = values.Value("en")

    VIDEO_RESOLUTIONS = [144, 240, 480, 720, 1080]
    STORAGE_BACKEND = values.Value("marsha.core.storage.s3")

    # Logging
    LOGGING = values.DictValue({
        "version": 1,
        "disable_existing_loggers": False,
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
            },
        },
        "loggers": {
            "marsha": {
                "handlers": ["console"],
                "level": "INFO",
                "propagate": True
            },
        },
    })

    # AWS
    AWS_ACCESS_KEY_ID = values.SecretValue()
    AWS_SECRET_ACCESS_KEY = values.SecretValue()
    AWS_S3_REGION_NAME = values.Value("eu-west-1")
    AWS_S3_URL_PROTOCOL = values.Value("https")
    AWS_BASE_NAME = values.Value()
    UPDATE_STATE_SHARED_SECRETS = values.ListValue()
    AWS_UPLOAD_EXPIRATION_DELAY = values.Value(24 * 60 * 60)  # 24h
    AWS_MEDIALIVE_ROLE_ARN = values.SecretValue()
    AWS_MEDIAPACKAGE_HARVEST_JOB_ARN = values.SecretValue()

    # BBB
    BBB_ENABLED = values.BooleanValue(False)
    BBB_API_ENDPOINT = values.Value()
    BBB_API_SECRET = values.Value(None)

    # LIVE_RAW
    LIVE_RAW_ENABLED = values.BooleanValue(False)

    # Cloud Front key pair for signed urls
    CLOUDFRONT_ACCESS_KEY_ID = values.Value(None)
    CLOUDFRONT_PRIVATE_KEY_PATH = values.Value(
        os.path.join(BASE_DIR, "..", ".ssh", "cloudfront_private_key"))
    CLOUDFRONT_SIGNED_URLS_ACTIVE = values.BooleanValue(True)
    CLOUDFRONT_SIGNED_URLS_VALIDITY = 2 * 60 * 60  # 2 hours

    CLOUDFRONT_DOMAIN = values.Value(None)

    BYPASS_LTI_VERIFICATION = values.BooleanValue(False)

    # Cache
    APP_DATA_CACHE_DURATION = values.Value(60)  # 60 secondes

    SENTRY_DSN = values.Value(None)

    # Resource max file size
    DOCUMENT_SOURCE_MAX_SIZE = values.Value(2**30)  # 1GB
    VIDEO_SOURCE_MAX_SIZE = values.Value(2**30)  # 1GB
    SUBTITLE_SOURCE_MAX_SIZE = values.Value(2**20)  # 1MB
    THUMBNAIL_SOURCE_MAX_SIZE = values.Value(10 * (2**20))  # 10MB
    SHARED_LIVE_MEDIA_SOURCE_MAX_SIZE = values.Value(300 * (2**20))  # 300MB

    EXTERNAL_JAVASCRIPT_SCRIPTS = values.ListValue([])

    VIDEO_PLAYER = values.Value("videojs")
    FRONT_UPLOAD_POLL_INTERVAL = values.Value("60")

    MAINTENANCE_MODE = values.BooleanValue(False)

    # XMPP Settings
    LIVE_CHAT_ENABLED = values.BooleanValue(False)
    XMPP_BOSH_URL = values.Value(None)
    XMPP_CONVERSE_PERSISTENT_STORE = values.Value("localStorage")
    XMPP_WEBSOCKET_URL = values.Value(None)
    XMPP_CONFERENCE_DOMAIN = values.Value(None)
    XMPP_PRIVATE_ADMIN_JID = values.Value(None)
    XMPP_PRIVATE_SERVER_PORT = values.Value(5222)
    XMPP_PRIVATE_SERVER_PASSWORD = values.Value(None)
    XMPP_JWT_SHARED_SECRET = values.Value(None)
    XMPP_JWT_ISSUER = values.Value("marsha")
    XMPP_JWT_AUDIENCE = values.Value("marsha")
    XMPP_DOMAIN = values.Value(None)

    # LIVE SETTINGS
    NB_DAYS_BEFORE_DELETING_LIVE_RECORDINGS = values.Value(14)
    NB_SECONDS_LIVING_DEV_STACK = values.PositiveIntegerValue(600)
    LIVE_PLAYLIST_WINDOW_SECONDS = values.PositiveIntegerValue(10)
    LIVE_SEGMENT_DURATION_SECONDS = values.PositiveIntegerValue(4)
    LIVE_FRAMERATE_NUMERATOR = values.PositiveIntegerValue(24000)
    LIVE_FRAMERATE_DENOMINATOR = values.PositiveIntegerValue(1000)
    LIVE_GOP_SIZE = values.FloatValue(4)

    # JITSI SETTINGS
    JITSI_EXTERNAL_API_URL = values.Value(
        "https://meet.jit.si/external_api.js")
    JITSI_DOMAIN = values.Value("meet.jit.si")
    JITSI_CONFIG_OVERWRITE = values.DictValue({})
    JITSI_INTERFACE_CONFIG_OVERWRITE = values.DictValue({})

    # LIVE PAIRING
    LIVE_PAIRING_EXPIRATION_SECONDS = 60

    # SHARED LIVE MEDIA SETTINGS
    ALLOWED_SHARED_LIVE_MEDIA_MIME_TYPES = values.ListValue(
        ["application/pdf"])

    # Cors
    CORS_ALLOW_ALL_ORIGINS = values.BooleanValue(False)
    CORS_ALLOWED_ORIGINS = values.ListValue([])
    CORS_ALLOWED_ORIGIN_REGEXES = values.ListValue([])
    CORS_URLS_REGEX = values.Value(r"^/api/pairing-challenge$")
    CORS_ALLOW_METHODS = values.ListValue(["POST", "OPTIONS"])
    CORS_ALLOW_HEADERS = values.ListValue(list(default_headers))

    # Mail
    EMAIL_BACKEND = values.Value("django.core.mail.backends.smtp.EmailBackend")
    EMAIL_HOST = values.Value(None)
    EMAIL_HOST_USER = values.Value(None)
    EMAIL_HOST_PASSWORD = values.Value(None)
    EMAIL_PORT = values.PositiveIntegerValue(None)
    EMAIL_USE_TLS = values.BooleanValue(False)
    EMAIL_FROM = values.Value("*****@*****.**")

    # REMINDERS SENT for scheduled webinars
    REMINDER_1, REMINDER_2, REMINDER_3, REMINDER_IS_STARTED, REMINDER_ERROR = (
        "REMINDER_1",
        "REMINDER_2",
        "REMINDER_3",
        "REMINDER_IS_STARTED",
        "REMINDER_ERROR",
    )
    # keys for REMINDERS_STEP
    (
        REMINDER_ELAPSED_LABEL,
        REGISTER_EXCLUDE_STEP,
        REMINDER_KEY_REGISTER_BEFORE_S,
        REMINDER_KEY_STARTS_IN_S,
        REMINDER_OBJECT_MAIL,
    ) = (
        "REMINDER_ELAPSED_LABEL",
        "REGISTER_EXCLUDE_STEP",
        "STARTS_IN_S",
        "REGISTER_BEFORE_S",
        "REMINDER_OBJECT_MAIL",
    )
    REMINDERS_STEP = values.DictValue({
        REMINDER_1: {
            REMINDER_ELAPSED_LABEL: _("5 minutes"),
            REGISTER_EXCLUDE_STEP: [],
            REMINDER_KEY_STARTS_IN_S:
            300,  # webinar starts in less than 5 minutes
            REMINDER_KEY_REGISTER_BEFORE_S: 10800,  # three hours before
            REMINDER_OBJECT_MAIL: _("Live starts in less than 5 minutes"),
        },
        REMINDER_2: {
            REMINDER_ELAPSED_LABEL: _("3 hours"),
            REGISTER_EXCLUDE_STEP:
            [REMINDER_1],  # if step REMINDER_1 is done it will cancel this one
            REMINDER_KEY_REGISTER_BEFORE_S: 86400,  # 1 day before in seconds
            REMINDER_KEY_STARTS_IN_S:
            10800,  # webinar starts in less than 3 hours
            REMINDER_OBJECT_MAIL: _("Live starts in less than 3 hours"),
        },
        REMINDER_3: {
            REMINDER_ELAPSED_LABEL: _("3 days"),
            REGISTER_EXCLUDE_STEP: [REMINDER_1, REMINDER_2],
            REMINDER_KEY_REGISTER_BEFORE_S: 30 * 86400,  # thirty days before
            REMINDER_KEY_STARTS_IN_S:
            3 * 86400,  # webinar starts in less than 3 days
            REMINDER_OBJECT_MAIL: _("Live starts in less than 3 days"),
        },
    })
    REMINDERS_SECRET = values.Value()

    # pylint: disable=invalid-name
    @property
    def AWS_SOURCE_BUCKET_NAME(self):
        """Source bucket name.

        If this setting is set in an environment variable we use it. Otherwise
        the value is computed with the AWS_BASE_NAME value.
        """
        return os.environ.get("DJANGO_AWS_SOURCE_BUCKET_NAME",
                              f"{self.AWS_BASE_NAME}-marsha-source")

    # pylint: disable=invalid-name
    @property
    def AWS_DESTINATION_BUCKET_NAME(self):
        """Destination bucket name.

        If this setting is set in an environment variable we use it. Otherwise
        the value is computed with the AWS_BASE_NAME value.
        """
        return os.environ.get(
            "DJANGO_AWS_DESTINATION_BUCKET_NAME",
            f"{self.AWS_BASE_NAME}-marsha-destination",
        )

    # pylint: disable=invalid-name
    @property
    def SIMPLE_JWT(self):
        """Define settings for `djangorestframework_simplejwt`.

        The JWT_SIGNING_KEY must be evaluated late as the jwt library check for string type.
        """
        return {
            "ACCESS_TOKEN_LIFETIME":
            timedelta(days=1),
            "ALGORITHM":
            "HS256",
            "SIGNING_KEY":
            str(self.JWT_SIGNING_KEY),
            "USER_ID_CLAIM":
            "resource_id",
            "AUTH_TOKEN_CLASSES":
            ("rest_framework_simplejwt.tokens.AccessToken", ),
        }

    # pylint: disable=invalid-name
    @property
    def RELEASE(self):
        """
        Return the release information.

        Delegate to the module function to enable easier testing.
        """
        return get_release()

    @classmethod
    def _get_environment(cls):
        """Environment in which the application is launched."""
        return cls.__name__.lower()

    # pylint: disable=invalid-name
    @property
    def ENVIRONMENT(self):
        """Environment in which the application is launched."""
        return self._get_environment()

    @classmethod
    def post_setup(cls):
        """Post setup configuration.

        This is the place where you can configure settings that require other
        settings to be loaded.
        """
        super().post_setup()

        # The DJANGO_SENTRY_DSN environment variable should be set to activate
        # sentry for an environment
        if cls.SENTRY_DSN is not None:
            sentry_sdk.init(
                dsn=cls.SENTRY_DSN,
                environment=cls._get_environment(),
                release=get_release(),
                integrations=[DjangoIntegration()],
            )
            with sentry_sdk.configure_scope() as scope:
                scope.set_extra("application", "backend")
Exemple #5
0
class Base(Configuration):
    """
    This is the base configuration every configuration (aka environnement) should inherit from. It
    is recommended to configure third-party applications by creating a configuration mixins in
    ./configurations and compose the Base configuration with those mixins.
    It depends on an environment variable that SHOULD be defined:
    * DJANGO_SECRET_KEY
    You may also want to override default configuration by setting the following environment
    variables:
    * DJANGO_SENTRY_DSN
    * DB_NAME
    * DB_HOST
    * DB_PASSWORD
    * DB_USER
    """

    DEBUG = False

    # Security
    ALLOWED_HOSTS = []
    SECRET_KEY = values.Value(None)

    AUTH_USER_MODEL = "ashley.User"

    # SECURE_PROXY_SSL_HEADER allows to fix the scheme in Django's HttpRequest
    # object when you application is behind a reverse proxy.
    #
    # Keep this SECURE_PROXY_SSL_HEADER configuration only if :
    # - your Django app is behind a proxy.
    # - your proxy strips the X-Forwarded-Proto header from all incoming requests
    # - Your proxy sets the X-Forwarded-Proto header and sends it to Django
    #
    # In other cases, you should comment the following line to avoid security issues.
    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

    # Disable Samesite flag in session and csrf cookies, because ashley is meant to
    # run in an iframe on external websites.
    # Note : The better solution is to send a flag Samesite=none, because
    # modern browsers are considering Samesite=Lax by default when the flag is
    # not specified.
    CSRF_COOKIE_SAMESITE = "None"
    SESSION_COOKIE_SAMESITE = "None"

    # Modern browsers require to have the `secure` attribute on cookies with `Samesite=none`
    CSRF_COOKIE_SECURE = True
    SESSION_COOKIE_SECURE = True

    # Privacy
    SECURE_REFERRER_POLICY = "same-origin"

    # Application definition
    ROOT_URLCONF = "urls"
    WSGI_APPLICATION = "wsgi.application"

    # Database
    DATABASES = {
        "default": {
            "ENGINE":
            values.Value(
                "django.db.backends.postgresql_psycopg2",
                environ_name="DB_ENGINE",
                environ_prefix=None,
            ),
            "NAME":
            values.Value("ashley", environ_name="DB_NAME",
                         environ_prefix=None),
            "USER":
            values.Value("fun", environ_name="DB_USER", environ_prefix=None),
            "PASSWORD":
            values.Value("pass",
                         environ_name="DB_PASSWORD",
                         environ_prefix=None),
            "HOST":
            values.Value("localhost",
                         environ_name="DB_HOST",
                         environ_prefix=None),
            "PORT":
            values.Value(5432, environ_name="DB_PORT", environ_prefix=None),
        }
    }

    # Static files (CSS, JavaScript, Images)
    STATIC_URL = "/static/"
    STATIC_ROOT = os.path.join(DATA_DIR, "static")
    MEDIA_URL = "/media/"
    MEDIA_ROOT = os.path.join(DATA_DIR, "media")
    STATICFILES_DIRS = (MACHINA_MAIN_STATIC_DIR, )

    # AWS
    AWS_ACCESS_KEY_ID = values.SecretValue()
    AWS_LOCATION = values.Value("media/")
    AWS_S3_CUSTOM_DOMAIN = values.Value()
    AWS_S3_REGION_NAME = values.Value()
    AWS_S3_URL_PROTOCOL = values.Value("https")
    AWS_SECRET_ACCESS_KEY = values.SecretValue()
    AWS_STORAGE_BUCKET_NAME = values.Value()
    AWS_QUERYSTRING_AUTH = False
    DEFAULT_FILE_STORAGE = values.Value(
        "storages.backends.s3boto3.S3Boto3Storage")

    # Internationalization
    TIME_ZONE = "UTC"
    USE_I18N = True
    USE_L10N = True
    USE_TZ = True

    # Templates
    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "DIRS": [ASHLEY_MAIN_TEMPLATE_DIR, MACHINA_MAIN_TEMPLATE_DIR],
            "OPTIONS": {
                "context_processors": [
                    "django.contrib.auth.context_processors.auth",
                    "django.contrib.messages.context_processors.messages",
                    "django.template.context_processors.csrf",
                    "django.template.context_processors.debug",
                    "django.template.context_processors.i18n",
                    "django.template.context_processors.media",
                    "django.template.context_processors.request",
                    "django.template.context_processors.tz",
                    "ashley.context_processors.site_metas",
                    "machina.core.context_processors.metadata",
                ],
                "loaders": [
                    "django.template.loaders.filesystem.Loader",
                    "django.template.loaders.app_directories.Loader",
                ],
            },
        },
    ]

    MIDDLEWARE = [
        "django.middleware.security.SecurityMiddleware",
        "django.contrib.sessions.middleware.SessionMiddleware",
        "django.middleware.locale.LocaleMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.middleware.csrf.CsrfViewMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "dockerflow.django.middleware.DockerflowMiddleware",
        "ashley.machina_extensions.forum_permission.middleware.ForumPermissionMiddleware",
    ]

    AUTHENTICATION_BACKENDS = [
        "ashley.auth.backend.LTIBackend",
        "django.contrib.auth.backends.ModelBackend",
    ]

    # Django applications from the highest priority to the lowest
    INSTALLED_APPS = [
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        # Django machina dependencies
        "mptt",
        "haystack",
        "widget_tweaks",
        # Ashley
        "ashley",
        "ashley.machina_extensions.forum",
        "ashley.machina_extensions.forum_conversation",
        "ashley.machina_extensions.forum_moderation",
        "ashley.machina_extensions.forum_permission",
        "ashley.machina_extensions.forum_search",
        # Django LTI Toolbox
        "lti_toolbox",
        # Third party apps
        "dockerflow.django",
        # Django machina
        "machina",
        "machina.apps.forum_conversation.forum_attachments",
        "machina.apps.forum_conversation.forum_polls",
        "machina.apps.forum_feeds",
        "machina.apps.forum_member",
        "machina.apps.forum_tracking",
        "rest_framework",
    ]

    # Languages
    LANGUAGE_CODE = "en-us"

    # Cache
    CACHES = {
        "default": {
            "BACKEND": "django.core.cache.backends.locmem.LocMemCache"
        },
        "machina_attachments": {
            "BACKEND":
            "django.core.cache.backends.filebased.FileBasedCache",
            "LOCATION":
            values.Value(
                tempfile.gettempdir(),
                environ_name="TMP_ATTACHMENT_DIR",
                environ_prefix=None,
            ),
        },
    }

    # Search engine
    HAYSTACK_CONNECTIONS = {
        "default": {
            "ENGINE":
            "haystack.backends.elasticsearch5_backend.Elasticsearch5SearchEngine",
            "URL":
            "{:s}:{!s}".format(
                values.Value(
                    "elasticsearch",
                    environ_name="ELASTICSEARCH_HOST",
                    environ_prefix=None,
                ),
                values.Value(9200,
                             environ_name="ELASTICSEARCH_PORT",
                             environ_prefix=None),
            ),
            "INDEX_NAME":
            values.Value("ashley",
                         environ_name="ELASTICSEARCH_INDEX_NAME",
                         environ_prefix=None),
        },
    }

    # Machina
    MACHINA_MARKUP_LANGUAGE = ("ashley.editor.draftjs_renderer", {})
    MACHINA_MARKUP_MAX_LENGTH_VALIDATOR = (
        "ashley.validators.MarkupNullableMaxLengthValidator")
    MACHINA_MARKUP_WIDGET = "ashley.editor.widgets.DraftEditor"
    MACHINA_PROFILE_AVATARS_ENABLED = False
    MACHINA_USER_DISPLAY_NAME_METHOD = "get_public_username_with_default"

    MAX_UPLOAD_FILE_MB = values.PositiveIntegerValue(5)
    IMAGE_TYPE_ALLOWED = values.ListValue(["gif", "jpeg", "jpg", "png", "svg"])

    # Sentry
    SENTRY_DSN = values.Value(None, environ_name="SENTRY_DSN")

    REST_FRAMEWORK = {
        "ALLOWED_VERSIONS": ("1.0", ),
        "DEFAULT_AUTHENTICATION_CLASSES":
        ("rest_framework.authentication.SessionAuthentication", ),
        "DEFAULT_VERSION":
        "1.0",
        "DEFAULT_VERSIONING_CLASS":
        "rest_framework.versioning.URLPathVersioning",
    }

    @classmethod
    def post_setup(cls):
        """Post setup configuration.
        This is the place where you can configure settings that require other
        settings to be loaded.
        """
        super().post_setup()

        # The SENTRY_DSN setting should be available to activate sentry for an environment
        if cls.SENTRY_DSN is not None:
            sentry_sdk.init(
                dsn=cls.SENTRY_DSN,
                environment=cls.__name__.lower(),
                release=get_release(),
                integrations=[DjangoIntegration()],
            )
            with sentry_sdk.configure_scope() as scope:
                scope.set_extra("application", "backend")
Exemple #6
0
class Development(Base):
    """Development environment settings.

    We set ``DEBUG`` to ``True`` by default, configure the server to respond to all hosts.
    """

    ALLOWED_HOSTS = ["*"]
    AWS_BASE_NAME = values.Value("development")
    DEBUG = values.BooleanValue(True)
    CLOUDFRONT_SIGNED_URLS_ACTIVE = values.BooleanValue(False)
    CACHES = {
        "default": {
            "BACKEND": "django.core.cache.backends.dummy.DummyCache"
        }
    }
    STAT_BACKEND = values.Value("marsha.core.stats.dummy_backend")

    # Mail
    EMAIL_HOST = values.Value("mailcatcher")
    EMAIL_PORT = values.PositiveIntegerValue(1025)

    # Logging
    LOGGING = values.DictValue({
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "gelf": {
                "()": "logging_gelf.formatters.GELFFormatter",
                "null_character": True,
            },
        },
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
            },
            "gelf": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
                "formatter": "gelf",
            },
        },
        "loggers": {
            "marsha": {
                "handlers": ["console"],
                "level": "DEBUG",
                "propagate": True,
            },
            # This formatter is here as an example to what is possible to do
            # with xapi loogers.
            "xapi": {
                "handlers": ["gelf"],
                "level": "INFO",
                "propagate": True
            },
        },
    })

    @classmethod
    def setup(cls):
        # Add settings from the Renater FER SAML testing suite
        # pylint: disable=import-outside-toplevel
        from social_edu_federation.testing.settings import saml_fer_settings

        # pylint: enable=import-outside-toplevel

        for setting_name, setting_value in saml_fer_settings.items():
            setattr(cls, setting_name, values.Value(setting_value))

        cls.SOCIAL_AUTH_SAML_FER_IDP_FAKER = values.Value(True)
        cls.SOCIAL_AUTH_SAML_FER_FEDERATION_SAML_METADATA_URL = values.Value(
            # Metadata is fetched from inside the docker, hence the 8000 port
            "http://localhost:8000/account/saml/idp/metadata/")

        # Call setup afterward
        super().setup()
Exemple #7
0
class Base(Configuration):
    """Base configuration every configuration (aka environment) should inherit from.

    It depends on an environment variable that SHOULD be defined:
    - DJANGO_SECRET_KEY

    You may also want to override default configuration by setting the following
    environment variables:
    - DJANGO_DEBUG
    """

    DATA_DIR = values.Value(os.path.join("/", "data"))

    # Static files (CSS, JavaScript, Images)
    STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"), )
    STATIC_URL = "/static/"
    MEDIA_URL = "/media/"
    # Allow to configure location of static/media files for non-Docker installation
    MEDIA_ROOT = values.Value(os.path.join(str(DATA_DIR), "media"))
    STATIC_ROOT = values.Value(os.path.join(str(DATA_DIR), "static"))

    SECRET_KEY = values.SecretValue()

    DEBUG = values.BooleanValue(False)

    DATABASES = {
        "default": {
            "ENGINE":
            values.Value(
                "django.db.backends.postgresql",
                environ_name="DATABASE_ENGINE",
                environ_prefix=None,
            ),
            "NAME":
            values.Value("marsha",
                         environ_name="POSTGRES_DB",
                         environ_prefix=None),
            "USER":
            values.Value("marsha_user",
                         environ_name="POSTGRES_USER",
                         environ_prefix=None),
            "PASSWORD":
            values.Value("pass",
                         environ_name="POSTGRES_PASSWORD",
                         environ_prefix=None),
            "HOST":
            values.Value("localhost",
                         environ_name="POSTGRES_HOST",
                         environ_prefix=None),
            "PORT":
            values.Value(5432,
                         environ_name="POSTGRES_PORT",
                         environ_prefix=None),
        }
    }

    ALLOWED_HOSTS = []

    SITE_ID = 1

    SECURE_BROWSER_XSS_FILTER = True
    SECURE_CONTENT_TYPE_NOSNIFF = True
    X_FRAME_OPTIONS = "DENY"
    SECURE_REFERRER_POLICY = "same-origin"
    SILENCED_SYSTEM_CHECKS = values.ListValue([])
    CSRF_TRUSTED_ORIGINS = values.ListValue([])

    # Application definition

    INSTALLED_APPS = [
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.sites",
        "django.contrib.staticfiles",
        "django_extensions",
        "dockerflow.django",
        "waffle",
        "rest_framework",
        "corsheaders",
        "channels",
        "parler",  # django-parler, for translated models
        "social_django.apps.PythonSocialAuthConfig",  # python-social-auth for Django
        "social_edu_federation.django.apps.PythonSocialEduFedAuthConfig",
        # Marsha
        "marsha.account.apps.AccountConfig",
        "marsha.core.apps.MarshaAdminConfig",
        "marsha.core.apps.CoreConfig",
        "marsha.bbb.apps.BbbConfig",
        "marsha.markdown.apps.MarkdownConfig",
        "marsha.websocket.apps.WebsocketConfig",
    ]
    MIDDLEWARE = [
        "corsheaders.middleware.CorsMiddleware",
        "django.middleware.security.SecurityMiddleware",
        "whitenoise.middleware.WhiteNoiseMiddleware",
        "django.contrib.sessions.middleware.SessionMiddleware",
        "django.middleware.locale.LocaleMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.middleware.csrf.CsrfViewMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "django.middleware.clickjacking.XFrameOptionsMiddleware",
        "dockerflow.django.middleware.DockerflowMiddleware",
        "waffle.middleware.WaffleMiddleware",
        "social_django.middleware.SocialAuthExceptionMiddleware",
    ]

    ROOT_URLCONF = "marsha.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",
                "social_django.context_processors.backends",
                "social_django.context_processors.login_redirect",
            ]
        },
    }]

    AUTH_USER_MODEL = "core.User"

    AUTHENTICATION_BACKENDS = [
        "social_edu_federation.backends.saml_fer.FERSAMLAuth",
        "django.contrib.auth.backends.ModelBackend",
    ]

    ASGI_APPLICATION = "marsha.asgi.application"

    # For sentinels:
    # CHANNEL_LAYERS = {
    #     "default": {
    #         "BACKEND": "marsha.websocket.layers.JsonRedisChannelLayer",
    #         "CONFIG": {
    #             "hosts": [{
    #                 "sentinels": [(SENTINEL_HOST, SENTINEL_PORT)],
    #                 "master_name": SENTINEL_MASTER,
    #             }],
    #         },
    #     },
    # }
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "marsha.websocket.layers.JsonRedisChannelLayer",
            "CONFIG": {
                "hosts":
                values.ListValue([("redis", 6379)],
                                 environ_name="REDIS_HOST",
                                 environ_prefix=None),
            },
        },
    }

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":
        ("rest_framework_simplejwt.authentication.JWTStatelessUserAuthentication",
         ),
        "EXCEPTION_HANDLER":
        "marsha.core.views.exception_handler",
        "DEFAULT_PAGINATION_CLASS":
        "rest_framework.pagination.LimitOffsetPagination",
        "PAGE_SIZE":
        50,
        "DEFAULT_THROTTLE_RATES": {
            "anon":
            values.Value("3/minute",
                         environ_name="REST_FRAMEWORK_ANON_THROTTLE_RATE"),
            "live_session":
            values.Value(
                "3/minute",
                environ_name="REST_FRAMEWORK_LIVE_SESSION_THROTTLE_RATE"),
        },
    }

    # WAFFLE
    WAFFLE_CREATE_MISSING_SWITCHES = True

    # User login base Django settings
    LOGIN_REDIRECT_URL = "site"  # redirect to home (React site)
    LOGIN_URL = "account:login"

    # Password validation
    # https://docs.djangoproject.com/en/2.0/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"
        },
    ]

    JWT_SIGNING_KEY = values.Value(SECRET_KEY)

    # Internationalization
    # https://docs.djangoproject.com/en/2.0/topics/i18n/

    # Django sets `LANGUAGES` by default with all supported languages. Let's save it to a
    # different setting before overriding it with the languages active in Marsha. We can use it
    # for example for the choice of time text tracks languages which should not be limited to
    # the few languages active in Marsha.
    # pylint: disable=no-member
    ALL_LANGUAGES = Configuration.LANGUAGES

    LANGUAGE_CODE = "en-us"

    # Careful! Languages should be ordered by priority, as this tuple is used to get
    # fallback/default languages throughout the app.
    # Use "en" as default as it is the language that is most likely to be spoken by any visitor
    # when their preferred language, whatever it is, is unavailable
    LANGUAGES = [("en", _("english")), ("fr", _("french"))]
    LANGUAGES_DICT = dict(LANGUAGES)
    LOCALE_PATHS = (os.path.join(BASE_DIR, "../locale"), )
    # Internationalization
    TIME_ZONE = "UTC"
    USE_I18N = True
    USE_L10N = True
    USE_TZ = True

    REACT_LOCALES = values.ListValue(["en_US", "es_ES", "fr_FR", "fr_CA"])
    DEFAULT_LTI_LAUNCH_PRESENTATION_LOCALE = values.Value("en")

    PARLER_DEFAULT_LANGUAGE_CODE = LANGUAGES[0][0]
    PARLER_LANGUAGES = {
        None: tuple({"code": code} for code in LANGUAGES_DICT.keys()),
        "default": {
            "fallbacks": [PARLER_DEFAULT_LANGUAGE_CODE
                          ],  # defaults to PARLER_DEFAULT_LANGUAGE_CODE
            "hide_untranslated": False,
        },
    }
    # Admin tabs don't show up when only Site `None` is defined
    PARLER_LANGUAGES[SITE_ID] = PARLER_LANGUAGES[None]

    VIDEO_RESOLUTIONS = [144, 240, 480, 720, 1080]
    STORAGE_BACKEND = values.Value("marsha.core.storage.s3")

    # Logging
    LOGGING = values.DictValue({
        "version": 1,
        "disable_existing_loggers": False,
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
            },
        },
        "loggers": {
            "marsha": {
                "handlers": ["console"],
                "level": "INFO",
                "propagate": True
            },
        },
    })

    # AWS
    AWS_ACCESS_KEY_ID = values.SecretValue()
    AWS_SECRET_ACCESS_KEY = values.SecretValue()
    AWS_S3_REGION_NAME = values.Value("eu-west-1")
    AWS_S3_URL_PROTOCOL = values.Value("https")
    AWS_BASE_NAME = values.Value()
    UPDATE_STATE_SHARED_SECRETS = values.ListValue()
    AWS_UPLOAD_EXPIRATION_DELAY = values.Value(24 * 60 * 60)  # 24h
    AWS_MEDIALIVE_ROLE_ARN = values.SecretValue()
    AWS_MEDIAPACKAGE_HARVEST_JOB_ARN = values.SecretValue()
    AWS_MEDIALIVE_INPUT_WAITER_DELAY = values.PositiveIntegerValue(5)
    AWS_MEDIALIVE_INPUT_WAITER_MAX_ATTEMPTS = values.PositiveIntegerValue(84)

    # LTI Config
    LTI_CONFIG_TITLE = values.Value("Marsha")
    LTI_CONFIG_DESCRIPTION = values.Value(
        "An LTI first, opensource and extensible Learning Content Management System"
    )
    LTI_CONFIG_ICON = values.Value("marsha_32x32_blue.png")
    LTI_CONFIG_URL = values.Value()
    LTI_CONFIG_CONTACT_EMAIL = values.Value()

    # BBB
    BBB_ENABLED = values.BooleanValue(False)
    BBB_API_ENDPOINT = values.Value()
    BBB_API_SECRET = values.Value(None)

    # Markdown application
    MARKDOWN_ENABLED = values.BooleanValue(False)

    # LIVE_RAW
    LIVE_RAW_ENABLED = values.BooleanValue(False)

    # Cloud Front key pair for signed urls
    CLOUDFRONT_PRIVATE_KEY_PATH = values.Value(
        os.path.join(BASE_DIR, "..", ".ssh", "cloudfront_private_key"))
    CLOUDFRONT_SIGNED_URLS_ACTIVE = values.BooleanValue(True)
    CLOUDFRONT_SIGNED_URLS_VALIDITY = 2 * 60 * 60  # 2 hours
    CLOUDFRONT_SIGNED_URL_CACHE_DURATION = values.Value(900)  # 15 minutes
    CLOUDFRONT_SIGNED_PUBLIC_KEY_ID = values.Value(None)

    CLOUDFRONT_DOMAIN = values.Value(None)

    BYPASS_LTI_VERIFICATION = values.BooleanValue(False)

    # Cache
    APP_DATA_CACHE_DURATION = values.Value(60)  # 60 secondes
    VIDEO_ATTENDANCES_CACHE_DURATION = values.Value(300)  # 5 minutes

    SENTRY_DSN = values.Value(None)

    # Resource max file size
    DOCUMENT_SOURCE_MAX_SIZE = values.Value(2**30)  # 1GB
    VIDEO_SOURCE_MAX_SIZE = values.Value(2**30)  # 1GB
    SUBTITLE_SOURCE_MAX_SIZE = values.Value(2**20)  # 1MB
    THUMBNAIL_SOURCE_MAX_SIZE = values.Value(10 * (2**20))  # 10MB
    SHARED_LIVE_MEDIA_SOURCE_MAX_SIZE = values.Value(300 * (2**20))  # 300MB

    EXTERNAL_JAVASCRIPT_SCRIPTS = values.ListValue([])

    VIDEO_PLAYER = values.Value("videojs")
    FRONT_UPLOAD_POLL_INTERVAL = values.Value("60")

    MAINTENANCE_MODE = values.BooleanValue(False)

    # XMPP Settings
    LIVE_CHAT_ENABLED = values.BooleanValue(False)
    XMPP_BOSH_URL = values.Value(None)
    XMPP_CONVERSE_PERSISTENT_STORE = values.Value("localStorage")
    XMPP_WEBSOCKET_URL = values.Value(None)
    XMPP_CONFERENCE_DOMAIN = values.Value(None)
    XMPP_PRIVATE_ADMIN_JID = values.Value(None)
    XMPP_PRIVATE_SERVER_PORT = values.Value(5222)
    XMPP_PRIVATE_SERVER_PASSWORD = values.Value(None)
    XMPP_JWT_SHARED_SECRET = values.Value(None)
    XMPP_JWT_ISSUER = values.Value("marsha")
    XMPP_JWT_AUDIENCE = values.Value("marsha")
    XMPP_DOMAIN = values.Value(None)

    # LIVE SETTINGS
    NB_DAYS_BEFORE_DELETING_LIVE_RECORDINGS = values.Value(14)
    NB_SECONDS_LIVING_DEV_STACK = values.PositiveIntegerValue(600)
    LIVE_PLAYLIST_WINDOW_SECONDS = values.PositiveIntegerValue(10)
    LIVE_SEGMENT_DURATION_SECONDS = values.PositiveIntegerValue(4)
    LIVE_FRAMERATE_NUMERATOR = values.PositiveIntegerValue(24000)
    LIVE_FRAMERATE_DENOMINATOR = values.PositiveIntegerValue(1000)
    LIVE_GOP_SIZE = values.FloatValue(4)

    # JITSI SETTINGS
    JITSI_EXTERNAL_API_URL = values.Value(
        "https://meet.jit.si/external_api.js")
    JITSI_DOMAIN = values.Value("meet.jit.si")
    JITSI_CONFIG_OVERWRITE = values.DictValue({})
    JITSI_INTERFACE_CONFIG_OVERWRITE = values.DictValue({})
    JITSI_JWT_APP_ID = values.Value()
    JITSI_JWT_APP_SECRET = values.Value()
    JITSI_JWT_TOKEN_EXPIRATION_SECONDS = values.PositiveIntegerValue(600)

    # LIVE PAIRING
    LIVE_PAIRING_EXPIRATION_SECONDS = 60

    # SHARED LIVE MEDIA SETTINGS
    ALLOWED_SHARED_LIVE_MEDIA_MIME_TYPES = values.ListValue(
        ["application/pdf"])

    # Cors
    CORS_ALLOW_ALL_ORIGINS = values.BooleanValue(False)
    CORS_ALLOWED_ORIGINS = values.ListValue([])
    CORS_ALLOWED_ORIGIN_REGEXES = values.ListValue([])
    CORS_URLS_REGEX = values.Value(r"^/api/pairing-challenge$")
    CORS_ALLOW_METHODS = values.ListValue(["POST", "OPTIONS"])
    CORS_ALLOW_HEADERS = values.ListValue(list(default_headers))

    # Mail
    EMAIL_BACKEND = values.Value("django.core.mail.backends.smtp.EmailBackend")
    EMAIL_HOST = values.Value(None)
    EMAIL_HOST_USER = values.Value(None)
    EMAIL_HOST_PASSWORD = values.Value(None)
    EMAIL_PORT = values.PositiveIntegerValue(None)
    EMAIL_USE_TLS = values.BooleanValue(False)
    EMAIL_FROM = values.Value("*****@*****.**")

    # REMINDERS SENT for scheduled webinars
    REMINDER_1, REMINDER_2, REMINDER_3, REMINDER_DATE_UPDATED, REMINDER_ERROR = (
        "REMINDER_1",
        "REMINDER_2",
        "REMINDER_3",
        "REMINDER_DATE_UPDATED",
        "REMINDER_ERROR",
    )
    # keys for REMINDERS_STEP
    (
        REMINDER_ELAPSED_LABEL,
        REGISTER_EXCLUDE_STEP,
        REMINDER_KEY_REGISTER_BEFORE_S,
        REMINDER_KEY_STARTS_IN_S,
        REMINDER_OBJECT_MAIL,
    ) = (
        "REMINDER_ELAPSED_LABEL",
        "REGISTER_EXCLUDE_STEP",
        "STARTS_IN_S",
        "REGISTER_BEFORE_S",
        "REMINDER_OBJECT_MAIL",
    )
    REMINDERS_STEP = values.DictValue({
        REMINDER_1: {
            REMINDER_ELAPSED_LABEL: _("5 minutes"),
            REGISTER_EXCLUDE_STEP: [],
            REMINDER_KEY_STARTS_IN_S:
            300,  # webinar starts in less than 5 minutes
            REMINDER_KEY_REGISTER_BEFORE_S: 10800,  # three hours before
            REMINDER_OBJECT_MAIL: _("Live starts in less than 5 minutes"),
        },
        REMINDER_2: {
            REMINDER_ELAPSED_LABEL: _("3 hours"),
            REGISTER_EXCLUDE_STEP:
            [REMINDER_1],  # if step REMINDER_1 is done it will cancel this one
            REMINDER_KEY_REGISTER_BEFORE_S: 86400,  # 1 day before in seconds
            REMINDER_KEY_STARTS_IN_S:
            10800,  # webinar starts in less than 3 hours
            REMINDER_OBJECT_MAIL: _("Live starts in less than 3 hours"),
        },
        REMINDER_3: {
            REMINDER_ELAPSED_LABEL: _("3 days"),
            REGISTER_EXCLUDE_STEP: [REMINDER_1, REMINDER_2],
            REMINDER_KEY_REGISTER_BEFORE_S: 30 * 86400,  # thirty days before
            REMINDER_KEY_STARTS_IN_S:
            3 * 86400,  # webinar starts in less than 3 days
            REMINDER_OBJECT_MAIL: _("Live starts in less than 3 days"),
        },
    })
    REMINDERS_SECRET = values.Value()

    # Settings related to statistics in potsie project
    STAT_BACKEND = values.Value("marsha.core.stats.grafana_xapi_fun_backend")
    STAT_BACKEND_SETTINGS = values.DictValue({
        "api_key":
        values.Value(environ_name="GRAFANA_XAPI_FUN_API_KEY",
                     environ_prefix=None),
        "api_endpoint":
        values.Value(environ_name="GRAFANA_XAPI_FUN_API_ENDPOINT",
                     environ_prefix=None),
        "api_datasource_id":
        values.Value(environ_name="GRAFANA_XAPI_FUN_API_DATASOURCE_ID",
                     environ_prefix=None),
        "api_datastream":
        values.Value(environ_name="GRAFANA_XAPI_FUN_API_DATASTREAM",
                     environ_prefix=None),
    })
    ATTENDANCE_POINTS = values.Value(20)
    ATTENDANCE_PUSH_DELAY = values.Value(60)

    # Python social auth
    SOCIAL_AUTH_JSONFIELD_ENABLED = True
    SOCIAL_AUTH_URL_NAMESPACE = "account:social"
    SOCIAL_AUTH_SAML_FER_IDP_FAKER = False

    SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG = {
        "authnRequestsSigned":
        values.BooleanValue(
            default=True,
            environ_name=
            "SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG_AUTH_REQUEST_SIGNED",
        ),
        "signMetadata":
        values.BooleanValue(
            default=True,
            environ_name="SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG_SIGN_METADATA",
        ),
        "wantMessagesSigned":
        values.BooleanValue(
            default=True,
            environ_name=
            "SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG_WANT_MESSAGES_SIGNED",
        ),
        "wantAssertionsSigned":
        values.BooleanValue(
            default=True,
            environ_name=
            "SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG_WANT_ASSERTIONS_SIGNED",
        ),
        "wantAssertionsEncrypted":
        values.BooleanValue(
            default=True,
            environ_name=
            "SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG_WANT_ASSERTIONS_ENCRYPTED",
        ),
        "rejectDeprecatedAlgorithm":
        values.BooleanValue(
            default=True,
            environ_name=
            "SOCIAL_AUTH_SAML_FER_SECURITY_CONFIG_REJECT_DEPRECATED_ALGORITHM",
        ),
    }

    # SOCIAL_AUTH_SAML_FER_SP_ENTITY_ID should be a URL that includes a domain name you own
    SOCIAL_AUTH_SAML_FER_SP_ENTITY_ID = values.Value()
    # SOCIAL_AUTH_SAML_FER_SP_PUBLIC_CERT X.509 certificate for the key pair that
    # your app will use
    SOCIAL_AUTH_SAML_FER_SP_PUBLIC_CERT = values.Value()
    # SOCIAL_AUTH_SAML_FER_SP_PRIVATE_KEY The private key to be used by your app
    SOCIAL_AUTH_SAML_FER_SP_PRIVATE_KEY = values.Value()

    # Next certificate management, keep empty when next certificate is still not known
    SOCIAL_AUTH_SAML_FER_SP_NEXT_PUBLIC_CERT = values.Value()
    SOCIAL_AUTH_SAML_FER_SP_EXTRA = ({
        "x509certNew":
        SOCIAL_AUTH_SAML_FER_SP_NEXT_PUBLIC_CERT,
    } if SOCIAL_AUTH_SAML_FER_SP_NEXT_PUBLIC_CERT else {})

    SOCIAL_AUTH_SAML_FER_ORG_INFO = {  # specify values for English at a minimum
        "en-US": {
            "name":
            values.Value(
                "marsha",
                environ_name="SOCIAL_AUTH_SAML_FER_ORG_INFO_NAME",
            ),
            "displayname":
            values.Value(
                "Marsha",
                environ_name="SOCIAL_AUTH_SAML_FER_ORG_INFO_DISPLAY_NAME",
            ),
            "url":
            values.Value(environ_name="SOCIAL_AUTH_SAML_FER_ORG_INFO_URL", ),
        }
    }
    # SOCIAL_AUTH_SAML_FER_TECHNICAL_CONTACT technical contact responsible for your app
    SOCIAL_AUTH_SAML_FER_TECHNICAL_CONTACT = {
        "givenName":
        values.Value(
            "Marsha dev team",
            environ_name="SOCIAL_AUTH_SAML_FER_TECHNICAL_CONTACT_NAME",
        ),
        "emailAddress":
        values.Value(
            environ_name="SOCIAL_AUTH_SAML_FER_TECHNICAL_CONTACT_EMAIL", ),
    }
    # SOCIAL_AUTH_SAML_FER_SUPPORT_CONTACT support contact for your app
    SOCIAL_AUTH_SAML_FER_SUPPORT_CONTACT = {
        "givenName":
        values.Value(
            "Marsha dev team",
            environ_name="SOCIAL_AUTH_SAML_FER_SUPPORT_CONTACT_NAME",
        ),
        "emailAddress":
        values.Value(
            environ_name="SOCIAL_AUTH_SAML_FER_SUPPORT_CONTACT_EMAIL", ),
    }
    # SOCIAL_AUTH_SAML_FER_ENABLED_IDPS is not required since the
    # SAML FER backend is overridden to allow dynamic IdPs.
    # see social_edu_federation.backends.saml_fer.FERSAMLAuth.get_idp(idp_name)

    # Custom parameter to define the Renater Federation Metadata
    SOCIAL_AUTH_SAML_FER_FEDERATION_SAML_METADATA_URL = values.Value(
        "https://metadata.federation.renater.fr/renater/main/main-idps-renater-metadata.xml"
    )

    SOCIAL_AUTH_SAML_FER_PIPELINE = MARSHA_DEFAULT_AUTH_PIPELINE

    # pylint: disable=invalid-name
    @property
    def AWS_SOURCE_BUCKET_NAME(self):
        """Source bucket name.

        If this setting is set in an environment variable we use it. Otherwise
        the value is computed with the AWS_BASE_NAME value.
        """
        return os.environ.get("DJANGO_AWS_SOURCE_BUCKET_NAME",
                              f"{self.AWS_BASE_NAME}-marsha-source")

    # pylint: disable=invalid-name
    @property
    def AWS_DESTINATION_BUCKET_NAME(self):
        """Destination bucket name.

        If this setting is set in an environment variable we use it. Otherwise
        the value is computed with the AWS_BASE_NAME value.
        """
        return os.environ.get(
            "DJANGO_AWS_DESTINATION_BUCKET_NAME",
            f"{self.AWS_BASE_NAME}-marsha-destination",
        )

    # pylint: disable=invalid-name
    @property
    def SIMPLE_JWT(self):
        """Define settings for `djangorestframework_simplejwt`.

        The JWT_SIGNING_KEY must be evaluated late as the jwt library check for string type.
        """
        return {
            "ACCESS_TOKEN_LIFETIME":
            timedelta(days=1),
            "ALGORITHM":
            "HS256",
            "SIGNING_KEY":
            str(self.JWT_SIGNING_KEY),
            "USER_ID_CLAIM":
            "resource_id",
            "AUTH_TOKEN_CLASSES":
            ("rest_framework_simplejwt.tokens.AccessToken", ),
        }

    # pylint: disable=invalid-name
    @property
    def RELEASE(self):
        """
        Return the release information.

        Delegate to the module function to enable easier testing.
        """
        return get_release()

    @classmethod
    def _get_environment(cls):
        """Environment in which the application is launched."""
        return cls.__name__.lower()

    # pylint: disable=invalid-name
    @property
    def ENVIRONMENT(self):
        """Environment in which the application is launched."""
        return self._get_environment()

    @classmethod
    def post_setup(cls):
        """Post setup configuration.

        This is the place where you can configure settings that require other
        settings to be loaded.
        """
        super().post_setup()

        # The DJANGO_SENTRY_DSN environment variable should be set to activate
        # sentry for an environment
        if cls.SENTRY_DSN is not None:
            sentry_sdk.init(
                dsn=cls.SENTRY_DSN,
                environment=cls._get_environment(),
                release=get_release(),
                integrations=[DjangoIntegration()],
            )
            with sentry_sdk.configure_scope() as scope:
                scope.set_extra("application", "backend")
Exemple #8
0
class Base(Configuration):

    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

    SECRET_KEY = values.SecretValue()
    LOG_LEVEL = values.Value(environ_prefix="", default="ERROR")

    DEBUG = values.BooleanValue(environ_prefix="", default=False)

    ALLOWED_HOSTS = values.ListValue(
        environ_prefix="", default=["127.0.0.1", "localhost", "0.0.0.0"])

    # Application definition

    SYSTEM_APPS = [
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        # third party apps
        "rest_framework",
        "django_extensions",
    ]

    PROJECT_APPS = ["movies", "external_services"]

    INSTALLED_APPS = SYSTEM_APPS + PROJECT_APPS

    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",
    ]

    ROOT_URLCONF = "totoro_app.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 = "totoro_app.wsgi.application"

    # Database
    # https://docs.djangoproject.com/en/2.2/ref/settings/#databases

    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
        }
    }

    # Password validation
    # https://docs.djangoproject.com/en/2.2/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/2.2/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/2.2/howto/static-files/

    STATIC_URL = "/static/"
    STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

    LOGGING = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "verbose": {
                "format":
                "%(levelname)s %(asctime)s %(module)s "
                "%(process)d %(thread)d %(message)s"
            }
        },
        "handlers": {
            "console": {
                "level": "DEBUG",
                "class": "logging.StreamHandler",
                "formatter": "verbose",
            }
        },
        "root": {
            "level": "INFO",
            "handlers": ["console"]
        },
    }

    # django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/

    # lazy eval for env based config
    # https://django-configurations.readthedocs.io/en/stable/patterns/#property-settings
    @property
    def REST_FRAMEWORK(self):
        return {
            "DEFAULT_AUTHENTICATION_CLASSES": (
                "rest_framework.authentication.SessionAuthentication",
                "rest_framework.authentication.TokenAuthentication",
            ),
            "DEFAULT_PERMISSION_CLASSES":
            ("rest_framework.permissions.IsAuthenticated", ),
            "DEFAULT_PAGINATION_CLASS":
            "rest_framework.pagination.LimitOffsetPagination",
        }

    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": os.getenv("REDIS_URL"),
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient"
            },
            "KEY_PREFIX": "totoro_app",
        }
    }

    CACHE_TTL = values.PositiveIntegerValue(environ_prefix="", default=(0))

    # sentry setup
    ENABLE_SENTRY = values.BooleanValue(environ_prefix="", default=False)
    SENTRY_PROJECT_ID = values.Value(environ_prefix="", default=None)
    SENTRY_KEY = values.Value(environ_prefix="", default=None)

    # https://django-configurations.readthedocs.io/en/stable/patterns/#setup-methods
    @classmethod
    def post_setup(cls):
        super().post_setup()

        if cls.ENABLE_SENTRY:
            import sentry_sdk
            from sentry_sdk.integrations.django import DjangoIntegration

            sentry_sdk.init(
                dsn=
                f"https://{cls.SENTRY_KEY}.ingest.sentry.io/{cls.SENTRY_PROJECT_ID}",
                integrations=[DjangoIntegration()],
                # If you wish to associate users to errors (assuming you are using
                # django.contrib.auth) you may enable sending PII data.
                send_default_pii=True,
            )
Exemple #9
0
class Settings(Configuration):
    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',
        },
    ]
    AUTH_USER_MODEL = 'telegrambot.User'
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    CACHES = values.CacheURLValue('locmem://')
    DATABASES = values.DatabaseURLValue('sqlite:///db.sqlite3')
    # DOTENV = os.path.join(BASE_DIR, '.env')
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = values.BooleanValue(True)
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        # third party
        'rest_framework',
        # local apps
        'telegrambot',
    ]
    LANGUAGE_CODE = 'en-us'
    LOGGING = {
        "version": 1,
        "disable_existing_loggers": False,
        "loggers": {
            "command_notify": {
                "level": "DEBUG",
                "handlers": ['console'],
                "propagate": False
            },
            "scrappers": {
                "level": "DEBUG",
                "handlers": ['console'],
                "propagate": False
            }
        },
        "handlers": {
            "console": {
                "level": "DEBUG",
                "class": "logging.StreamHandler",
                "formatter": "simple"
            }
        },
        "formatters": {
            "simple": {
                "format":
                "%(asctime)s [%(levelname)s] {%(module)s} %(message)s"
            }
        }
    }
    MIDDLEWARE = [
        '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',
    ]
    PREVENT_NOTIFICATION_REPEAT_TIMEOUT = values.PositiveIntegerValue(
        5 * 60)  # 5 mins
    ROOT_URLCONF = 'onair.urls'
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS':
        'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 10,
        'URL_FIELD_NAME': 'href',
    }
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '1)#=9)t+e=374nx2i*p-o$a_b7%zvreb1ghmxo21+iyh&_*+a9'
    STATIC_ROOT = values.Value(os.path.join(BASE_DIR, '..', 'staticfiles'))
    STATIC_URL = '/assets/'
    # http://whitenoise.evans.io/en/stable/
    STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
    TELEGRAM_TOKEN = values.SecretValue()
    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',
                ],
            },
        },
    ]
    TIME_ZONE = 'UTC'
    USE_I18N = True
    USE_L10N = True
    USE_TZ = True
    WSGI_APPLICATION = 'onair.wsgi.application'