class SentryMixin(ConfigMixin): """ Configure Sentry.io error reporting. This requires the `sentry-sdk` package to be installed. The `DJANGO_SENTRY_DSN` environment variable should be externally set to a Sentry DSN. The `DJANGO_SENTRY_ENVIRONMENT`, `DJANGO_SENTRY_RELEASE`, and `DJANGO_SENTRY_TRACES_SAMPLE_RATE` environment variables may also be set, if desired. """ @staticmethod def mutate_configuration( configuration: Type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += [ 'composed_configuration.sentry.apps.SentryConfig', ] SENTRY_DSN = values.Value(None) SENTRY_ENVIRONMENT = values.Value(None) SENTRY_RELEASE = values.Value(None) # None is a valid default value, but if this is set via environment variable, # the value must be interpretable as a float SENTRY_TRACES_SAMPLE_RATE = values.FloatValue(None)
class AWS(object): "AWS configuration" SPOT_INSTANCES = values.BooleanValue(default=True) CORE_SPOT_BID = values.FloatValue(default=0.84) AWS_CONFIG = { # AWS EC2 configuration 'AWS_REGION': 'us-west-2', # EMR configuration # Master and slave instance types should be the same as the telemetry # setup bootstrap action depends on it to autotune the cluster. 'MASTER_INSTANCE_TYPE': 'c3.4xlarge', 'WORKER_INSTANCE_TYPE': 'c3.4xlarge', 'USE_SPOT_INSTANCES': SPOT_INSTANCES, 'CORE_SPOT_BID': CORE_SPOT_BID, # available EMR releases, to be used as choices for Spark jobs and clusters # forms. Please keep the latest (newest) as the first item 'EMR_RELEASES': ( '5.0.0', '4.5.0', ), 'SPARK_INSTANCE_PROFILE': 'telemetry-spark-cloudformation-' 'TelemetrySparkInstanceProfile-1SATUBVEXG7E3', 'SPARK_EMR_BUCKET': 'telemetry-spark-emr-2', 'INSTANCE_APP_TAG': 'telemetry-analysis-worker-instance', 'EMAIL_SOURCE': '*****@*****.**', 'MAX_CLUSTER_SIZE': 30, # Tags for accounting purposes 'ACCOUNTING_APP_TAG': 'telemetry-analysis', 'ACCOUNTING_TYPE_TAG': 'worker', # Buckets for storing S3 data 'CODE_BUCKET': 'telemetry-analysis-code-2', 'PUBLIC_DATA_BUCKET': 'telemetry-public-analysis-2', 'PRIVATE_DATA_BUCKET': 'telemetry-private-analysis-2', 'LOG_BUCKET': 'telemetry-analysis-logs-2' } PUBLIC_DATA_URL = 'https://s3-{}.amazonaws.com/{}/'.format( AWS_CONFIG['AWS_REGION'], AWS_CONFIG['PUBLIC_DATA_BUCKET']) PUBLIC_NB_URL = 'https://nbviewer.jupyter.org/url/s3-{}.amazonaws.com/{}/'.format( AWS_CONFIG['AWS_REGION'], AWS_CONFIG['PUBLIC_DATA_BUCKET'])
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)
class Settings(Configuration): ACCESS_TOKEN_LIFETIME_MINUTES = values.FloatValue() ADMIN_HEADER_COLOR = values.Value() ADMIN_HEADER_TITLE = "{environment}{title}".format( environment=ENVIRONMENT or '{DJANGO_ENVIRONMENT}', title=values.Value('{DJANGO_ADMIN_HEADER_TITLE}', environ_name='ADMIN_HEADER_TITLE'), ) ADMINS = values.SingleNestedListValue([]) ALLOWED_HOSTS = values.ListValue([]) ANYMAIL = values.DictValue() 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 = "main.User" CORS_ORIGIN_WHITELIST = values.ListValue([]) DATABASE_ENGINE = values.Value(environ_required=True) DATABASE_NAME = values.Value(environ_required=True) DATABASE_USER = values.Value() DATABASE_PASSWORD = values.Value() DATABASE_HOST = values.Value() DATABASE_PORT = values.Value() @property def DATABASES(self): return { '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, } } # SECURITY WARNING: don't run with debug turned on in production! DEBUG = values.BooleanValue(False) DEFAULT_FROM_EMAIL = values.Value() ELASTICSEARCH_HOST = values.Value() ELASTICSEARCH_PORT = values.Value() @property def ELASTICSEARCH_DSL(self): # https://django-configurations.readthedocs.io/en/stable/patterns/#property-settings return { 'default': { 'hosts': 'http://{}:{}'.format(self.ELASTICSEARCH_HOST, self.ELASTICSEARCH_PORT) } } EMAIL_BACKEND = values.Value() EMAIL_HOST = values.Value() EMAIL_HOST_PASSWORD = values.Value() EMAIL_HOST_USER = values.Value() EMAIL_PORT = values.Value() EMAIL_USE_TLS = values.BooleanValue(False) INSTALLED_APPS = [ # Django apps 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Third party 'anymail', 'django_extensions', 'django_elasticsearch_dsl', 'rest_framework', 'rest_framework_simplejwt.token_blacklist', 'corsheaders', 'django_filters', # Local apps 'main.apps.MainConfig' ] LANGUAGE_CODE = values.Value('en-us') LOGGING = logging_config() MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', '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', 'main.middleware.timezone_middleware.TimezoneMiddleware', ] RECAPTCHA_PRIVATE_KEY = values.SecretValue() RECAPTCHA_URL = values.Value( default="https://www.google.com/recaptcha/api/siteverify") REST_FRAMEWORK = { 'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S', 'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_RENDERER_CLASSES': ('rest_framework.renderers.JSONRenderer', ), 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning', } ROOT_URLCONF = 'storageofknowledge.urls' SECRET_KEY = values.SecretValue() SERVER_EMAIL = values.Value() SESSION_COOKIE_AGE = values.IntegerValue(5 * 60) # 5 minutes SESSION_SAVE_EVERY_REQUEST = values.BooleanValue(True) @property def SIMPLE_JWT(self): # https://django-configurations.readthedocs.io/en/stable/patterns/#property-settings return { 'ROTATE_REFRESH_TOKENS': True, 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=self.ACCESS_TOKEN_LIFETIME_MINUTES) } STATIC_ROOT = values.Value(None) STATIC_URL = '/static/' STATICFILES_DIRS = values.SingleNestedListValue([]) STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [(os.path.join(BASE_DIR, "templates"))], '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', 'django.template.context_processors.media', 'main.context_processors.from_settings', ], }, }, ] TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True USER_CONFIRMATION_LIFETIME_HOURS = values.FloatValue() WSGI_APPLICATION = 'storageofknowledge.wsgi.application'
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")
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")
class Common(Configuration): BASE_DIR = os.path.dirname(os.path.dirname(__file__)) ENVIRONMENT = values.Value(environ_prefix=None, default='DEVELOPMENT') SECRET_KEY = values.SecretValue(environ_prefix=None) DEBUG = False TEMPLATE_DEBUG = False ALLOWED_HOSTS = ['aecc-uprb.herokuapp.com', '*'] # Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', # Apps 'apps.users', 'apps.events', 'apps.contact', 'apps.blog', 'apps.surveys', # Third party 'allauth', 'allauth.account', 'allauth.socialaccount', 'django_extensions', 'debug_toolbar', 'import_export', 'taggit', 'tinymce', 'multiselectfield', 'storages', ) DISQUS_API_KEY = values.Value(environ_prefix=None) DISQUS_WEBSITE_SHORTNAME = values.Value(environ_prefix=None) MIDDLEWARE_CLASSES = ( '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 = 'aecc.urls' WSGI_APPLICATION = 'aecc.wsgi.application' # Database # https://docs.djangoproject.com/en/dev/ref/settings/#databases DATABASES = values.DatabaseURLValue('sqlite:///{}'.format( os.path.join(BASE_DIR, 'storage.sqlite3'))) # Internationalization # https://docs.djangoproject.com/en/1.6/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/1.6/howto/static-files/ STATIC_ROOT = 'staticfiles' STATIC_URL = '/static/' TEMPLATE_CONTEXT_PROCESSORS = ( "django.core.context_processors.request", "django.contrib.auth.context_processors.auth", "allauth.account.context_processors.account", "allauth.socialaccount.context_processors.socialaccount", "django.contrib.messages.context_processors.messages", "aecc.context_processor.months_dropdown_content") AUTHENTICATION_BACKENDS = ( "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", ) SITE_ID = 1 STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'), ) TEMPLATE_DIRS = (os.path.join(BASE_DIR, 'templates'), ) LOGGING = { "version": 1, "disable_existing_loggers": False, "handlers": { "console": { "level": "INFO", "class": "logging.StreamHandler", }, }, "loggers": { "django": { "handlers": ["console"], } } } AUTH_USER_MODEL = "users.User" # auth and allauth settings LOGIN_REDIRECT_URL = '/' LOGIN_URL = '/accounts/login/' ACCOUNT_EMAIL_VERIFICATION = "none" ACCOUNT_EMAIL_SUBJECT_PREFIX = '[AECC]' ACCOUNT_LOGOUT_ON_GET = True ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_CONFIRM_EMAIL_ON_GET = True ACCOUNT_LOGOUT_REDIRECT_URL = '/' ACCOUNT_USERNAME_BLACKLIST = ['admin'] ACCOUNT_USER_MODEL_USERNAME_FIELD = "email" ACCOUNT_SIGNUP_FORM_CLASS = 'apps.users.forms.SignupForm' ACCOUNT_AUTHENTICATION_METHOD = "email" ACCOUNT_USERNAME_REQUIRED = False TINYMCE_JS_ROOT = os.path.join(STATIC_URL, "js/tiny_mce") TINYMCE_JS_URL = os.path.join(STATIC_URL, "js/tiny_mce/tiny_mce_src.js") TINYMCE_DEFAULT_CONFIG = { 'plugins': "table,spellchecker,paste,searchreplace", 'theme': "advanced", 'cleanup_on_startup': True, 'custom_undo_redo_levels': 10, } TINYMCE_SPELLCHECKER = True EMAIL_HOST = values.Value() EMAIL_HOST_USER = values.Value() EMAIL_HOST_PASSWORD = values.Value() EMAIL_PORT = values.IntegerValue() EMAIL_USE_TLS = values.BooleanValue(False) AECC_UPRB_MEMBER_FEE = values.FloatValue(environ_prefix=None) AECC_TSHIRT = values.FloatValue(environ_prefix=None) AECC_TSHIRT_CUSTOM = values.FloatValue(environ_prefix=None)