Required these be added to CMS since enterprise is installed in the CMS and there are import failures when these keys don't exist
625 lines
26 KiB
Python
625 lines
26 KiB
Python
"""
|
|
This is the default template for our main set of AWS servers.
|
|
"""
|
|
|
|
# We intentionally define lots of variables that aren't used, and
|
|
# want to import all variables from base settings files
|
|
# pylint: disable=wildcard-import, unused-wildcard-import, wrong-import-order
|
|
|
|
from __future__ import absolute_import
|
|
|
|
import codecs
|
|
import os
|
|
import yaml
|
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
from django.core.urlresolvers import reverse_lazy
|
|
from path import Path as path
|
|
|
|
from .common import *
|
|
|
|
from openedx.core.djangoapps.plugins import constants as plugin_constants
|
|
from openedx.core.djangoapps.plugins import plugin_settings
|
|
from openedx.core.lib.derived import derive_settings
|
|
from openedx.core.lib.logsettings import get_logger_config
|
|
from xmodule.modulestore.modulestore_settings import convert_module_store_setting_if_needed
|
|
|
|
|
|
def get_env_setting(setting):
|
|
""" Get the environment setting or return exception """
|
|
try:
|
|
return os.environ[setting]
|
|
except KeyError:
|
|
error_msg = u"Set the %s env variable" % setting
|
|
raise ImproperlyConfigured(error_msg)
|
|
|
|
# A file path to a YAML file from which to load all the configuration for the edx platform
|
|
CONFIG_FILE = get_env_setting('STUDIO_CFG')
|
|
|
|
with codecs.open(CONFIG_FILE, encoding='utf-8') as f:
|
|
__config__ = yaml.safe_load(f)
|
|
|
|
# ENV_TOKENS and AUTH_TOKENS are included for reverse compatability.
|
|
# Removing them may break plugins that rely on them.
|
|
ENV_TOKENS = __config__
|
|
AUTH_TOKENS = __config__
|
|
|
|
|
|
# SERVICE_VARIANT specifies name of the variant used, which decides what JSON
|
|
# configuration files are read during startup.
|
|
SERVICE_VARIANT = os.environ.get('SERVICE_VARIANT', None)
|
|
|
|
# CONFIG_ROOT specifies the directory where the JSON configuration
|
|
# files are expected to be found. If not specified, use the project
|
|
# directory.
|
|
CONFIG_ROOT = path(os.environ.get('CONFIG_ROOT', ENV_ROOT))
|
|
|
|
# CONFIG_PREFIX specifies the prefix of the JSON configuration files,
|
|
# based on the service variant. If no variant is use, don't use a
|
|
# prefix.
|
|
CONFIG_PREFIX = SERVICE_VARIANT + "." if SERVICE_VARIANT else ""
|
|
|
|
############### ALWAYS THE SAME ################################
|
|
|
|
DEBUG = False
|
|
|
|
EMAIL_BACKEND = 'django_ses.SESBackend'
|
|
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
|
|
|
# IMPORTANT: With this enabled, the server must always be behind a proxy that
|
|
# strips the header HTTP_X_FORWARDED_PROTO from client requests. Otherwise,
|
|
# a user can fool our server into thinking it was an https connection.
|
|
# See
|
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header
|
|
# for other warnings.
|
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
|
|
|
###################################### CELERY ################################
|
|
|
|
# Don't use a connection pool, since connections are dropped by ELB.
|
|
BROKER_POOL_LIMIT = 0
|
|
BROKER_CONNECTION_TIMEOUT = 1
|
|
|
|
# For the Result Store, use the django cache named 'celery'
|
|
CELERY_RESULT_BACKEND = 'djcelery.backends.cache:CacheBackend'
|
|
|
|
# When the broker is behind an ELB, use a heartbeat to refresh the
|
|
# connection and to detect if it has been dropped.
|
|
BROKER_HEARTBEAT = 60.0
|
|
BROKER_HEARTBEAT_CHECKRATE = 2
|
|
|
|
# Each worker should only fetch one message at a time
|
|
CELERYD_PREFETCH_MULTIPLIER = 1
|
|
|
|
# Rename the exchange and queues for each variant
|
|
|
|
QUEUE_VARIANT = CONFIG_PREFIX.lower()
|
|
|
|
CELERY_DEFAULT_EXCHANGE = 'edx.{0}core'.format(QUEUE_VARIANT)
|
|
|
|
HIGH_PRIORITY_QUEUE = 'edx.{0}core.high'.format(QUEUE_VARIANT)
|
|
DEFAULT_PRIORITY_QUEUE = 'edx.{0}core.default'.format(QUEUE_VARIANT)
|
|
|
|
CELERY_DEFAULT_QUEUE = DEFAULT_PRIORITY_QUEUE
|
|
CELERY_DEFAULT_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE
|
|
|
|
CELERY_QUEUES = {
|
|
HIGH_PRIORITY_QUEUE: {},
|
|
DEFAULT_PRIORITY_QUEUE: {}
|
|
}
|
|
|
|
CELERY_ROUTES = "{}celery.Router".format(QUEUE_VARIANT)
|
|
|
|
# Do NOT calculate this dynamically at startup with git because it's *slow*.
|
|
EDX_PLATFORM_REVISION = ENV_TOKENS.get('EDX_PLATFORM_REVISION', EDX_PLATFORM_REVISION)
|
|
|
|
# STATIC_URL_BASE specifies the base url to use for static files
|
|
STATIC_URL_BASE = ENV_TOKENS.get('STATIC_URL_BASE', None)
|
|
if STATIC_URL_BASE:
|
|
STATIC_URL = STATIC_URL_BASE
|
|
if not STATIC_URL.endswith("/"):
|
|
STATIC_URL += "/"
|
|
STATIC_URL += 'studio/'
|
|
|
|
# DEFAULT_COURSE_ABOUT_IMAGE_URL specifies the default image to show for courses that don't provide one
|
|
DEFAULT_COURSE_ABOUT_IMAGE_URL = ENV_TOKENS.get('DEFAULT_COURSE_ABOUT_IMAGE_URL', DEFAULT_COURSE_ABOUT_IMAGE_URL)
|
|
|
|
DEFAULT_COURSE_VISIBILITY_IN_CATALOG = ENV_TOKENS.get(
|
|
'DEFAULT_COURSE_VISIBILITY_IN_CATALOG',
|
|
DEFAULT_COURSE_VISIBILITY_IN_CATALOG
|
|
)
|
|
|
|
# DEFAULT_MOBILE_AVAILABLE specifies if the course is available for mobile by default
|
|
DEFAULT_MOBILE_AVAILABLE = ENV_TOKENS.get(
|
|
'DEFAULT_MOBILE_AVAILABLE',
|
|
DEFAULT_MOBILE_AVAILABLE
|
|
)
|
|
|
|
# How long to cache OpenAPI schemas and UI, in seconds.
|
|
OPENAPI_CACHE_TIMEOUT = ENV_TOKENS.get('OPENAPI_CACHE_TIMEOUT', 60 * 60)
|
|
|
|
# MEDIA_ROOT specifies the directory where user-uploaded files are stored.
|
|
MEDIA_ROOT = ENV_TOKENS.get('MEDIA_ROOT', MEDIA_ROOT)
|
|
MEDIA_URL = ENV_TOKENS.get('MEDIA_URL', MEDIA_URL)
|
|
|
|
# GITHUB_REPO_ROOT is the base directory
|
|
# for course data
|
|
GITHUB_REPO_ROOT = ENV_TOKENS.get('GITHUB_REPO_ROOT', GITHUB_REPO_ROOT)
|
|
|
|
# STATIC_ROOT specifies the directory where static files are
|
|
# collected
|
|
|
|
STATIC_ROOT_BASE = ENV_TOKENS.get('STATIC_ROOT_BASE', None)
|
|
if STATIC_ROOT_BASE:
|
|
STATIC_ROOT = path(STATIC_ROOT_BASE) / 'studio'
|
|
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
|
|
WEBPACK_LOADER['WORKERS']['STATS_FILE'] = STATIC_ROOT / "webpack-worker-stats.json"
|
|
|
|
EMAIL_BACKEND = ENV_TOKENS.get('EMAIL_BACKEND', EMAIL_BACKEND)
|
|
EMAIL_FILE_PATH = ENV_TOKENS.get('EMAIL_FILE_PATH', None)
|
|
|
|
EMAIL_HOST = ENV_TOKENS.get('EMAIL_HOST', EMAIL_HOST)
|
|
EMAIL_PORT = ENV_TOKENS.get('EMAIL_PORT', EMAIL_PORT)
|
|
EMAIL_USE_TLS = ENV_TOKENS.get('EMAIL_USE_TLS', EMAIL_USE_TLS)
|
|
|
|
# CMS_BASE: Public domain name of Studio (should be resolvable from the end-user's browser)
|
|
CMS_BASE = ENV_TOKENS.get('CMS_BASE')
|
|
LMS_BASE = ENV_TOKENS.get('LMS_BASE')
|
|
LMS_ROOT_URL = ENV_TOKENS.get('LMS_ROOT_URL')
|
|
LMS_INTERNAL_ROOT_URL = ENV_TOKENS.get('LMS_INTERNAL_ROOT_URL', LMS_ROOT_URL)
|
|
ENTERPRISE_API_URL = ENV_TOKENS.get('ENTERPRISE_API_URL', LMS_INTERNAL_ROOT_URL + '/enterprise/api/v1/')
|
|
ENTERPRISE_CONSENT_API_URL = ENV_TOKENS.get('ENTERPRISE_CONSENT_API_URL', LMS_INTERNAL_ROOT_URL + '/consent/api/v1/')
|
|
# Note that FEATURES['PREVIEW_LMS_BASE'] gets read in from the environment file.
|
|
|
|
COURSE_CATALOG_API_URL = ENV_TOKENS.get('COURSE_CATALOG_API_URL', COURSE_CATALOG_API_URL)
|
|
|
|
# List of logout URIs for each IDA that the learner should be logged out of when they logout of
|
|
# Studio. Only applies to IDA for which the social auth flow uses DOT (Django OAuth Toolkit).
|
|
IDA_LOGOUT_URI_LIST = ENV_TOKENS.get('IDA_LOGOUT_URI_LIST', [])
|
|
|
|
SITE_NAME = ENV_TOKENS['SITE_NAME']
|
|
|
|
ALLOWED_HOSTS = [
|
|
# TODO: bbeggs remove this before prod, temp fix to get load testing running
|
|
"*",
|
|
CMS_BASE,
|
|
]
|
|
|
|
LOG_DIR = ENV_TOKENS['LOG_DIR']
|
|
DATA_DIR = path(ENV_TOKENS.get('DATA_DIR', DATA_DIR))
|
|
|
|
CACHES = ENV_TOKENS['CACHES']
|
|
# Cache used for location mapping -- called many times with the same key/value
|
|
# in a given request.
|
|
if 'loc_cache' not in CACHES:
|
|
CACHES['loc_cache'] = {
|
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
|
'LOCATION': 'edx_location_mem_cache',
|
|
}
|
|
|
|
SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN')
|
|
SESSION_COOKIE_HTTPONLY = ENV_TOKENS.get('SESSION_COOKIE_HTTPONLY', True)
|
|
SESSION_ENGINE = ENV_TOKENS.get('SESSION_ENGINE', SESSION_ENGINE)
|
|
SESSION_COOKIE_SECURE = ENV_TOKENS.get('SESSION_COOKIE_SECURE', SESSION_COOKIE_SECURE)
|
|
SESSION_SAVE_EVERY_REQUEST = ENV_TOKENS.get('SESSION_SAVE_EVERY_REQUEST', SESSION_SAVE_EVERY_REQUEST)
|
|
|
|
# social sharing settings
|
|
SOCIAL_SHARING_SETTINGS = ENV_TOKENS.get('SOCIAL_SHARING_SETTINGS', SOCIAL_SHARING_SETTINGS)
|
|
|
|
REGISTRATION_EMAIL_PATTERNS_ALLOWED = ENV_TOKENS.get('REGISTRATION_EMAIL_PATTERNS_ALLOWED')
|
|
|
|
# allow for environments to specify what cookie name our login subsystem should use
|
|
# this is to fix a bug regarding simultaneous logins between edx.org and edge.edx.org which can
|
|
# happen with some browsers (e.g. Firefox)
|
|
if ENV_TOKENS.get('SESSION_COOKIE_NAME', None):
|
|
# NOTE, there's a bug in Django (http://bugs.python.org/issue18012) which necessitates this being a str()
|
|
SESSION_COOKIE_NAME = str(ENV_TOKENS.get('SESSION_COOKIE_NAME'))
|
|
|
|
# Set the names of cookies shared with the marketing site
|
|
# These have the same cookie domain as the session, which in production
|
|
# usually includes subdomains.
|
|
EDXMKTG_LOGGED_IN_COOKIE_NAME = ENV_TOKENS.get('EDXMKTG_LOGGED_IN_COOKIE_NAME', EDXMKTG_LOGGED_IN_COOKIE_NAME)
|
|
EDXMKTG_USER_INFO_COOKIE_NAME = ENV_TOKENS.get('EDXMKTG_USER_INFO_COOKIE_NAME', EDXMKTG_USER_INFO_COOKIE_NAME)
|
|
|
|
# Determines whether the CSRF token can be transported on
|
|
# unencrypted channels. It is set to False here for backward compatibility,
|
|
# but it is highly recommended that this is True for environments accessed
|
|
# by end users.
|
|
CSRF_COOKIE_SECURE = ENV_TOKENS.get('CSRF_COOKIE_SECURE', False)
|
|
|
|
#Email overrides
|
|
DEFAULT_FROM_EMAIL = ENV_TOKENS.get('DEFAULT_FROM_EMAIL', DEFAULT_FROM_EMAIL)
|
|
DEFAULT_FEEDBACK_EMAIL = ENV_TOKENS.get('DEFAULT_FEEDBACK_EMAIL', DEFAULT_FEEDBACK_EMAIL)
|
|
ADMINS = ENV_TOKENS.get('ADMINS', ADMINS)
|
|
SERVER_EMAIL = ENV_TOKENS.get('SERVER_EMAIL', SERVER_EMAIL)
|
|
MKTG_URLS = ENV_TOKENS.get('MKTG_URLS', MKTG_URLS)
|
|
MKTG_URL_LINK_MAP.update(ENV_TOKENS.get('MKTG_URL_LINK_MAP', {}))
|
|
TECH_SUPPORT_EMAIL = ENV_TOKENS.get('TECH_SUPPORT_EMAIL', TECH_SUPPORT_EMAIL)
|
|
|
|
for name, value in ENV_TOKENS.get("CODE_JAIL", {}).items():
|
|
oldvalue = CODE_JAIL.get(name)
|
|
if isinstance(oldvalue, dict):
|
|
for subname, subvalue in value.items():
|
|
oldvalue[subname] = subvalue
|
|
else:
|
|
CODE_JAIL[name] = value
|
|
|
|
COURSES_WITH_UNSAFE_CODE = ENV_TOKENS.get("COURSES_WITH_UNSAFE_CODE", [])
|
|
|
|
ASSET_IGNORE_REGEX = ENV_TOKENS.get('ASSET_IGNORE_REGEX', ASSET_IGNORE_REGEX)
|
|
|
|
COMPREHENSIVE_THEME_DIRS = ENV_TOKENS.get('COMPREHENSIVE_THEME_DIRS', COMPREHENSIVE_THEME_DIRS) or []
|
|
|
|
# COMPREHENSIVE_THEME_LOCALE_PATHS contain the paths to themes locale directories e.g.
|
|
# "COMPREHENSIVE_THEME_LOCALE_PATHS" : [
|
|
# "/edx/src/edx-themes/conf/locale"
|
|
# ],
|
|
COMPREHENSIVE_THEME_LOCALE_PATHS = ENV_TOKENS.get('COMPREHENSIVE_THEME_LOCALE_PATHS', [])
|
|
|
|
DEFAULT_SITE_THEME = ENV_TOKENS.get('DEFAULT_SITE_THEME', DEFAULT_SITE_THEME)
|
|
ENABLE_COMPREHENSIVE_THEMING = ENV_TOKENS.get('ENABLE_COMPREHENSIVE_THEMING', ENABLE_COMPREHENSIVE_THEMING)
|
|
|
|
#Timezone overrides
|
|
TIME_ZONE = ENV_TOKENS.get('CELERY_TIMEZONE', CELERY_TIMEZONE)
|
|
|
|
# Push to LMS overrides
|
|
GIT_REPO_EXPORT_DIR = ENV_TOKENS.get('GIT_REPO_EXPORT_DIR', '/edx/var/edxapp/export_course_repos')
|
|
|
|
# Translation overrides
|
|
LANGUAGES = ENV_TOKENS.get('LANGUAGES', LANGUAGES)
|
|
LANGUAGE_CODE = ENV_TOKENS.get('LANGUAGE_CODE', LANGUAGE_CODE)
|
|
LANGUAGE_COOKIE = ENV_TOKENS.get('LANGUAGE_COOKIE', LANGUAGE_COOKIE)
|
|
|
|
USE_I18N = ENV_TOKENS.get('USE_I18N', USE_I18N)
|
|
ALL_LANGUAGES = ENV_TOKENS.get('ALL_LANGUAGES', ALL_LANGUAGES)
|
|
|
|
DEFAULT_COURSE_LANGUAGE = ENV_TOKENS.get('DEFAULT_COURSE_LANGUAGE', DEFAULT_COURSE_LANGUAGE)
|
|
|
|
ENV_FEATURES = ENV_TOKENS.get('FEATURES', {})
|
|
for feature, value in ENV_FEATURES.items():
|
|
FEATURES[feature] = value
|
|
|
|
# Additional installed apps
|
|
for app in ENV_TOKENS.get('ADDL_INSTALLED_APPS', []):
|
|
INSTALLED_APPS.append(app)
|
|
|
|
WIKI_ENABLED = ENV_TOKENS.get('WIKI_ENABLED', WIKI_ENABLED)
|
|
|
|
LOGGING = get_logger_config(LOG_DIR,
|
|
logging_env=ENV_TOKENS['LOGGING_ENV'],
|
|
service_variant=SERVICE_VARIANT)
|
|
|
|
#theming start:
|
|
|
|
# The following variables use (or) instead of the default value inside (get). This is to enforce using the Lazy Text
|
|
# values when the varibale is an empty string. Therefore, setting these variable as empty text in related
|
|
# json files will make the system reads thier values from django translation files
|
|
PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME') or PLATFORM_NAME
|
|
PLATFORM_DESCRIPTION = ENV_TOKENS.get('PLATFORM_DESCRIPTION') or PLATFORM_DESCRIPTION
|
|
STUDIO_NAME = ENV_TOKENS.get('STUDIO_NAME') or STUDIO_NAME
|
|
STUDIO_SHORT_NAME = ENV_TOKENS.get('STUDIO_SHORT_NAME') or STUDIO_SHORT_NAME
|
|
|
|
# Event Tracking
|
|
if "TRACKING_IGNORE_URL_PATTERNS" in ENV_TOKENS:
|
|
TRACKING_IGNORE_URL_PATTERNS = ENV_TOKENS.get("TRACKING_IGNORE_URL_PATTERNS")
|
|
|
|
# Heartbeat
|
|
HEARTBEAT_CHECKS = ENV_TOKENS.get('HEARTBEAT_CHECKS', HEARTBEAT_CHECKS)
|
|
HEARTBEAT_EXTENDED_CHECKS = ENV_TOKENS.get('HEARTBEAT_EXTENDED_CHECKS', HEARTBEAT_EXTENDED_CHECKS)
|
|
HEARTBEAT_CELERY_TIMEOUT = ENV_TOKENS.get('HEARTBEAT_CELERY_TIMEOUT', HEARTBEAT_CELERY_TIMEOUT)
|
|
|
|
# Login using the LMS as the identity provider.
|
|
# Turning the flag to True means that the LMS will NOT be used as the Identity Provider (idp)
|
|
if FEATURES.get('DISABLE_STUDIO_SSO_OVER_LMS', False):
|
|
LOGIN_URL = reverse_lazy('login')
|
|
FRONTEND_LOGIN_URL = LOGIN_URL
|
|
FRONTEND_LOGOUT_URL = reverse_lazy('logout')
|
|
|
|
LOGIN_REDIRECT_WHITELIST = [reverse_lazy('home')]
|
|
|
|
# Specific setting for the File Upload Service to store media in a bucket.
|
|
FILE_UPLOAD_STORAGE_BUCKET_NAME = ENV_TOKENS.get('FILE_UPLOAD_STORAGE_BUCKET_NAME', FILE_UPLOAD_STORAGE_BUCKET_NAME)
|
|
FILE_UPLOAD_STORAGE_PREFIX = ENV_TOKENS.get('FILE_UPLOAD_STORAGE_PREFIX', FILE_UPLOAD_STORAGE_PREFIX)
|
|
|
|
# Zendesk
|
|
ZENDESK_URL = ENV_TOKENS.get('ZENDESK_URL', ZENDESK_URL)
|
|
ZENDESK_CUSTOM_FIELDS = ENV_TOKENS.get('ZENDESK_CUSTOM_FIELDS', ZENDESK_CUSTOM_FIELDS)
|
|
ZENDESK_GROUP_ID_MAPPING = ENV_TOKENS.get('ZENDESK_GROUP_ID_MAPPING', ZENDESK_GROUP_ID_MAPPING)
|
|
|
|
############### XBlock filesystem field config ##########
|
|
if 'DJFS' in AUTH_TOKENS and AUTH_TOKENS['DJFS'] is not None:
|
|
DJFS = AUTH_TOKENS['DJFS']
|
|
if 'url_root' in DJFS:
|
|
DJFS['url_root'] = DJFS['url_root'].format(platform_revision=EDX_PLATFORM_REVISION)
|
|
|
|
EMAIL_HOST_USER = AUTH_TOKENS.get('EMAIL_HOST_USER', EMAIL_HOST_USER)
|
|
EMAIL_HOST_PASSWORD = AUTH_TOKENS.get('EMAIL_HOST_PASSWORD', EMAIL_HOST_PASSWORD)
|
|
|
|
AWS_SES_REGION_NAME = ENV_TOKENS.get('AWS_SES_REGION_NAME', 'us-east-1')
|
|
AWS_SES_REGION_ENDPOINT = ENV_TOKENS.get('AWS_SES_REGION_ENDPOINT', 'email.us-east-1.amazonaws.com')
|
|
|
|
# Note that this is the Studio key for Segment. There is a separate key for the LMS.
|
|
CMS_SEGMENT_KEY = AUTH_TOKENS.get('SEGMENT_KEY')
|
|
|
|
SECRET_KEY = AUTH_TOKENS['SECRET_KEY']
|
|
|
|
AWS_ACCESS_KEY_ID = AUTH_TOKENS["AWS_ACCESS_KEY_ID"]
|
|
if AWS_ACCESS_KEY_ID == "":
|
|
AWS_ACCESS_KEY_ID = None
|
|
|
|
AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"]
|
|
if AWS_SECRET_ACCESS_KEY == "":
|
|
AWS_SECRET_ACCESS_KEY = None
|
|
|
|
AWS_STORAGE_BUCKET_NAME = AUTH_TOKENS.get('AWS_STORAGE_BUCKET_NAME', 'edxuploads')
|
|
|
|
# Disabling querystring auth instructs Boto to exclude the querystring parameters (e.g. signature, access key) it
|
|
# normally appends to every returned URL.
|
|
AWS_QUERYSTRING_AUTH = AUTH_TOKENS.get('AWS_QUERYSTRING_AUTH', True)
|
|
|
|
AWS_DEFAULT_ACL = 'private'
|
|
AWS_BUCKET_ACL = AWS_DEFAULT_ACL
|
|
AWS_QUERYSTRING_EXPIRE = 7 * 24 * 60 * 60 # 7 days
|
|
AWS_S3_CUSTOM_DOMAIN = AUTH_TOKENS.get('AWS_S3_CUSTOM_DOMAIN', 'edxuploads.s3.amazonaws.com')
|
|
|
|
if AUTH_TOKENS.get('DEFAULT_FILE_STORAGE'):
|
|
DEFAULT_FILE_STORAGE = AUTH_TOKENS.get('DEFAULT_FILE_STORAGE')
|
|
elif AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY:
|
|
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
|
|
else:
|
|
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
|
|
|
COURSE_IMPORT_EXPORT_BUCKET = ENV_TOKENS.get('COURSE_IMPORT_EXPORT_BUCKET', '')
|
|
|
|
if COURSE_IMPORT_EXPORT_BUCKET:
|
|
COURSE_IMPORT_EXPORT_STORAGE = 'contentstore.storage.ImportExportS3Storage'
|
|
else:
|
|
COURSE_IMPORT_EXPORT_STORAGE = DEFAULT_FILE_STORAGE
|
|
|
|
USER_TASKS_ARTIFACT_STORAGE = COURSE_IMPORT_EXPORT_STORAGE
|
|
|
|
DATABASES = AUTH_TOKENS['DATABASES']
|
|
|
|
# The normal database user does not have enough permissions to run migrations.
|
|
# Migrations are run with separate credentials, given as DB_MIGRATION_*
|
|
# environment variables
|
|
for name, database in DATABASES.items():
|
|
if name != 'read_replica':
|
|
database.update({
|
|
'ENGINE': os.environ.get('DB_MIGRATION_ENGINE', database['ENGINE']),
|
|
'USER': os.environ.get('DB_MIGRATION_USER', database['USER']),
|
|
'PASSWORD': os.environ.get('DB_MIGRATION_PASS', database['PASSWORD']),
|
|
'NAME': os.environ.get('DB_MIGRATION_NAME', database['NAME']),
|
|
'HOST': os.environ.get('DB_MIGRATION_HOST', database['HOST']),
|
|
'PORT': os.environ.get('DB_MIGRATION_PORT', database['PORT']),
|
|
})
|
|
|
|
MODULESTORE = convert_module_store_setting_if_needed(AUTH_TOKENS.get('MODULESTORE', MODULESTORE))
|
|
|
|
MODULESTORE_FIELD_OVERRIDE_PROVIDERS = ENV_TOKENS.get(
|
|
'MODULESTORE_FIELD_OVERRIDE_PROVIDERS',
|
|
MODULESTORE_FIELD_OVERRIDE_PROVIDERS
|
|
)
|
|
|
|
XBLOCK_FIELD_DATA_WRAPPERS = ENV_TOKENS.get(
|
|
'XBLOCK_FIELD_DATA_WRAPPERS',
|
|
XBLOCK_FIELD_DATA_WRAPPERS
|
|
)
|
|
|
|
CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE']
|
|
DOC_STORE_CONFIG = AUTH_TOKENS['DOC_STORE_CONFIG']
|
|
|
|
############################### BLOCKSTORE #####################################
|
|
BLOCKSTORE_API_URL = ENV_TOKENS.get('BLOCKSTORE_API_URL', None) # e.g. "https://blockstore.example.com/api/v1/"
|
|
# Configure an API auth token at (blockstore URL)/admin/authtoken/token/
|
|
BLOCKSTORE_API_AUTH_TOKEN = AUTH_TOKENS.get('BLOCKSTORE_API_AUTH_TOKEN', None)
|
|
|
|
# Datadog for events!
|
|
DATADOG = AUTH_TOKENS.get("DATADOG", {})
|
|
DATADOG.update(ENV_TOKENS.get("DATADOG", {}))
|
|
|
|
# TODO: deprecated (compatibility with previous settings)
|
|
if 'DATADOG_API' in AUTH_TOKENS:
|
|
DATADOG['api_key'] = AUTH_TOKENS['DATADOG_API']
|
|
|
|
# Celery Broker
|
|
CELERY_ALWAYS_EAGER = ENV_TOKENS.get("CELERY_ALWAYS_EAGER", False)
|
|
CELERY_BROKER_TRANSPORT = ENV_TOKENS.get("CELERY_BROKER_TRANSPORT", "")
|
|
CELERY_BROKER_HOSTNAME = ENV_TOKENS.get("CELERY_BROKER_HOSTNAME", "")
|
|
CELERY_BROKER_VHOST = ENV_TOKENS.get("CELERY_BROKER_VHOST", "")
|
|
CELERY_BROKER_USER = AUTH_TOKENS.get("CELERY_BROKER_USER", "")
|
|
CELERY_BROKER_PASSWORD = AUTH_TOKENS.get("CELERY_BROKER_PASSWORD", "")
|
|
|
|
BROKER_URL = "{0}://{1}:{2}@{3}/{4}".format(CELERY_BROKER_TRANSPORT,
|
|
CELERY_BROKER_USER,
|
|
CELERY_BROKER_PASSWORD,
|
|
CELERY_BROKER_HOSTNAME,
|
|
CELERY_BROKER_VHOST)
|
|
BROKER_USE_SSL = ENV_TOKENS.get('CELERY_BROKER_USE_SSL', False)
|
|
|
|
# Message expiry time in seconds
|
|
CELERY_EVENT_QUEUE_TTL = ENV_TOKENS.get('CELERY_EVENT_QUEUE_TTL', None)
|
|
|
|
# Allow CELERY_QUEUES to be overwritten by ENV_TOKENS,
|
|
ENV_CELERY_QUEUES = ENV_TOKENS.get('CELERY_QUEUES', None)
|
|
if ENV_CELERY_QUEUES:
|
|
CELERY_QUEUES = {queue: {} for queue in ENV_CELERY_QUEUES}
|
|
|
|
# Then add alternate environment queues
|
|
ALTERNATE_QUEUE_ENVS = ENV_TOKENS.get('ALTERNATE_WORKER_QUEUES', '').split()
|
|
ALTERNATE_QUEUES = [
|
|
DEFAULT_PRIORITY_QUEUE.replace(QUEUE_VARIANT, alternate + '.')
|
|
for alternate in ALTERNATE_QUEUE_ENVS
|
|
]
|
|
|
|
CELERY_QUEUES.update(
|
|
{
|
|
alternate: {}
|
|
for alternate in ALTERNATE_QUEUES
|
|
if alternate not in list(CELERY_QUEUES.keys())
|
|
}
|
|
)
|
|
|
|
# Queue to use for updating grades due to grading policy change
|
|
POLICY_CHANGE_GRADES_ROUTING_KEY = ENV_TOKENS.get('POLICY_CHANGE_GRADES_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
# Rate limit for regrading tasks that a grading policy change can kick off
|
|
POLICY_CHANGE_TASK_RATE_LIMIT = ENV_TOKENS.get('POLICY_CHANGE_TASK_RATE_LIMIT', POLICY_CHANGE_TASK_RATE_LIMIT)
|
|
|
|
# Event tracking
|
|
TRACKING_BACKENDS.update(AUTH_TOKENS.get("TRACKING_BACKENDS", {}))
|
|
EVENT_TRACKING_BACKENDS['tracking_logs']['OPTIONS']['backends'].update(AUTH_TOKENS.get("EVENT_TRACKING_BACKENDS", {}))
|
|
EVENT_TRACKING_BACKENDS['segmentio']['OPTIONS']['processors'][0]['OPTIONS']['whitelist'].extend(
|
|
AUTH_TOKENS.get("EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST", []))
|
|
|
|
##### ACCOUNT LOCKOUT DEFAULT PARAMETERS #####
|
|
MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED = ENV_TOKENS.get(
|
|
"MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED", MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED
|
|
)
|
|
|
|
MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS = ENV_TOKENS.get(
|
|
"MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS", MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS
|
|
)
|
|
|
|
#### PASSWORD POLICY SETTINGS #####
|
|
AUTH_PASSWORD_VALIDATORS = ENV_TOKENS.get("AUTH_PASSWORD_VALIDATORS", AUTH_PASSWORD_VALIDATORS)
|
|
|
|
### INACTIVITY SETTINGS ####
|
|
SESSION_INACTIVITY_TIMEOUT_IN_SECONDS = AUTH_TOKENS.get("SESSION_INACTIVITY_TIMEOUT_IN_SECONDS")
|
|
|
|
##### X-Frame-Options response header settings #####
|
|
X_FRAME_OPTIONS = ENV_TOKENS.get('X_FRAME_OPTIONS', X_FRAME_OPTIONS)
|
|
|
|
################ ADVANCED COMPONENT/PROBLEM TYPES ###############
|
|
|
|
ADVANCED_PROBLEM_TYPES = ENV_TOKENS.get('ADVANCED_PROBLEM_TYPES', ADVANCED_PROBLEM_TYPES)
|
|
|
|
################ VIDEO UPLOAD PIPELINE ###############
|
|
|
|
VIDEO_UPLOAD_PIPELINE = ENV_TOKENS.get('VIDEO_UPLOAD_PIPELINE', VIDEO_UPLOAD_PIPELINE)
|
|
|
|
################ VIDEO IMAGE STORAGE ###############
|
|
|
|
VIDEO_IMAGE_SETTINGS = ENV_TOKENS.get('VIDEO_IMAGE_SETTINGS', VIDEO_IMAGE_SETTINGS)
|
|
|
|
################ VIDEO TRANSCRIPTS STORAGE ###############
|
|
|
|
VIDEO_TRANSCRIPTS_SETTINGS = ENV_TOKENS.get('VIDEO_TRANSCRIPTS_SETTINGS', VIDEO_TRANSCRIPTS_SETTINGS)
|
|
|
|
################ PUSH NOTIFICATIONS ###############
|
|
|
|
PARSE_KEYS = AUTH_TOKENS.get("PARSE_KEYS", {})
|
|
|
|
|
|
# Video Caching. Pairing country codes with CDN URLs.
|
|
# Example: {'CN': 'http://api.xuetangx.com/edx/video?s3_url='}
|
|
VIDEO_CDN_URL = ENV_TOKENS.get('VIDEO_CDN_URL', {})
|
|
|
|
if FEATURES['ENABLE_COURSEWARE_INDEX'] or FEATURES['ENABLE_LIBRARY_INDEX']:
|
|
# Use ElasticSearch for the search engine
|
|
SEARCH_ENGINE = "search.elastic.ElasticSearchEngine"
|
|
|
|
ELASTIC_SEARCH_CONFIG = ENV_TOKENS.get('ELASTIC_SEARCH_CONFIG', [{}])
|
|
|
|
XBLOCK_SETTINGS = ENV_TOKENS.get('XBLOCK_SETTINGS', {})
|
|
XBLOCK_SETTINGS.setdefault("VideoBlock", {})["licensing_enabled"] = FEATURES.get("LICENSING", False)
|
|
XBLOCK_SETTINGS.setdefault("VideoBlock", {})['YOUTUBE_API_KEY'] = AUTH_TOKENS.get('YOUTUBE_API_KEY', YOUTUBE_API_KEY)
|
|
|
|
############################ OAUTH2 Provider ###################################
|
|
|
|
# OpenID Connect issuer ID. Normally the URL of the authentication endpoint.
|
|
OAUTH_OIDC_ISSUER = ENV_TOKENS['OAUTH_OIDC_ISSUER']
|
|
|
|
#### JWT configuration ####
|
|
JWT_AUTH.update(ENV_TOKENS.get('JWT_AUTH', {}))
|
|
JWT_AUTH.update(AUTH_TOKENS.get('JWT_AUTH', {}))
|
|
|
|
######################## CUSTOM COURSES for EDX CONNECTOR ######################
|
|
if FEATURES.get('CUSTOM_COURSES_EDX'):
|
|
INSTALLED_APPS.append('openedx.core.djangoapps.ccxcon.apps.CCXConnectorConfig')
|
|
|
|
# Partner support link for CMS footer
|
|
PARTNER_SUPPORT_EMAIL = ENV_TOKENS.get('PARTNER_SUPPORT_EMAIL', PARTNER_SUPPORT_EMAIL)
|
|
|
|
# Affiliate cookie tracking
|
|
AFFILIATE_COOKIE_NAME = ENV_TOKENS.get('AFFILIATE_COOKIE_NAME', AFFILIATE_COOKIE_NAME)
|
|
|
|
############## Settings for Studio Context Sensitive Help ##############
|
|
|
|
HELP_TOKENS_BOOKS = ENV_TOKENS.get('HELP_TOKENS_BOOKS', HELP_TOKENS_BOOKS)
|
|
|
|
############## Settings for CourseGraph ############################
|
|
COURSEGRAPH_JOB_QUEUE = ENV_TOKENS.get('COURSEGRAPH_JOB_QUEUE', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
########## Settings for video transcript migration tasks ############
|
|
VIDEO_TRANSCRIPT_MIGRATIONS_JOB_QUEUE = ENV_TOKENS.get('VIDEO_TRANSCRIPT_MIGRATIONS_JOB_QUEUE', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
########## Settings youtube thumbnails scraper tasks ############
|
|
SCRAPE_YOUTUBE_THUMBNAILS_JOB_QUEUE = ENV_TOKENS.get('SCRAPE_YOUTUBE_THUMBNAILS_JOB_QUEUE', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
########################## Parental controls config #######################
|
|
|
|
# The age at which a learner no longer requires parental consent, or None
|
|
# if parental consent is never required.
|
|
PARENTAL_CONSENT_AGE_LIMIT = ENV_TOKENS.get(
|
|
'PARENTAL_CONSENT_AGE_LIMIT',
|
|
PARENTAL_CONSENT_AGE_LIMIT
|
|
)
|
|
|
|
########################## Extra middleware classes #######################
|
|
|
|
# Allow extra middleware classes to be added to the app through configuration.
|
|
MIDDLEWARE_CLASSES.extend(ENV_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', []))
|
|
|
|
########################## Settings for Completion API #####################
|
|
|
|
# Once a user has watched this percentage of a video, mark it as complete:
|
|
# (0.0 = 0%, 1.0 = 100%)
|
|
COMPLETION_VIDEO_COMPLETE_PERCENTAGE = ENV_TOKENS.get(
|
|
'COMPLETION_VIDEO_COMPLETE_PERCENTAGE',
|
|
COMPLETION_VIDEO_COMPLETE_PERCENTAGE,
|
|
)
|
|
|
|
############### Settings for django-fernet-fields ##################
|
|
FERNET_KEYS = AUTH_TOKENS.get('FERNET_KEYS', FERNET_KEYS)
|
|
|
|
####################### Enterprise Settings ######################
|
|
|
|
# A default dictionary to be used for filtering out enterprise customer catalog.
|
|
ENTERPRISE_CUSTOMER_CATALOG_DEFAULT_CONTENT_FILTER = ENV_TOKENS.get(
|
|
'ENTERPRISE_CUSTOMER_CATALOG_DEFAULT_CONTENT_FILTER',
|
|
ENTERPRISE_CUSTOMER_CATALOG_DEFAULT_CONTENT_FILTER
|
|
)
|
|
|
|
# This limits the type of roles that are submittable via the `student` app's manual enrollment
|
|
# audit API. While this isn't used in CMS, it is used via Enterprise which is installed in
|
|
# the CMS. Without this, we get errors.
|
|
MANUAL_ENROLLMENT_ROLE_CHOICES = ENV_TOKENS.get('MANUAL_ENROLLMENT_ROLE_CHOICES', MANUAL_ENROLLMENT_ROLE_CHOICES)
|
|
|
|
############### Settings for Retirement #####################
|
|
RETIRED_USERNAME_PREFIX = ENV_TOKENS.get('RETIRED_USERNAME_PREFIX', RETIRED_USERNAME_PREFIX)
|
|
RETIRED_EMAIL_PREFIX = ENV_TOKENS.get('RETIRED_EMAIL_PREFIX', RETIRED_EMAIL_PREFIX)
|
|
RETIRED_EMAIL_DOMAIN = ENV_TOKENS.get('RETIRED_EMAIL_DOMAIN', RETIRED_EMAIL_DOMAIN)
|
|
RETIRED_USER_SALTS = ENV_TOKENS.get('RETIRED_USER_SALTS', RETIRED_USER_SALTS)
|
|
RETIREMENT_SERVICE_WORKER_USERNAME = ENV_TOKENS.get(
|
|
'RETIREMENT_SERVICE_WORKER_USERNAME',
|
|
RETIREMENT_SERVICE_WORKER_USERNAME
|
|
)
|
|
RETIREMENT_STATES = ENV_TOKENS.get('RETIREMENT_STATES', RETIREMENT_STATES)
|
|
|
|
############## Settings for Course Enrollment Modes ######################
|
|
COURSE_ENROLLMENT_MODES = ENV_TOKENS.get('COURSE_ENROLLMENT_MODES', COURSE_ENROLLMENT_MODES)
|
|
|
|
############### Settings for edx-rbac ###############
|
|
SYSTEM_WIDE_ROLE_CLASSES = ENV_TOKENS.get('SYSTEM_WIDE_ROLE_CLASSES') or SYSTEM_WIDE_ROLE_CLASSES
|
|
|
|
####################### Plugin Settings ##########################
|
|
|
|
# This is at the bottom because it is going to load more settings after base settings are loaded
|
|
|
|
plugin_settings.add_plugins(__name__, plugin_constants.ProjectType.CMS, plugin_constants.SettingsType.PRODUCTION)
|
|
|
|
########################## Derive Any Derived Settings #######################
|
|
|
|
derive_settings(__name__)
|