We noticed that since users can choose to reset their due dates, they would have the ability to let due dates pass and then for any assessment that allows viewing the answer after the due date would be visible. The user could thus view all answers and then reset their due dates to receive a perfect score. This PR works to fix that issue by changing all show answer values to not take into account being past the due date when inside a PLS course.
966 lines
40 KiB
Python
966 lines
40 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
This is the default template for our main set of AWS servers.
|
|
|
|
Common traits:
|
|
* Use memcached, and cache-backed sessions
|
|
* Use a MySQL 5.1 database
|
|
"""
|
|
|
|
# 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
|
|
|
|
# Pylint gets confused by path.py instances, which report themselves as class
|
|
# objects. As a result, pylint applies the wrong regex in validating names,
|
|
# and throws spurious errors. Therefore, we disable invalid-name checking.
|
|
# pylint: disable=invalid-name
|
|
|
|
|
|
import codecs
|
|
import copy
|
|
import datetime
|
|
import os
|
|
|
|
import dateutil
|
|
import yaml
|
|
from corsheaders.defaults import default_headers as corsheaders_default_headers
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
from path import Path as path
|
|
|
|
from openedx.core.djangoapps.plugins import plugin_settings, constants as plugin_constants
|
|
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
|
|
|
|
from .common import *
|
|
|
|
|
|
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)
|
|
|
|
################################ ALWAYS THE SAME ##############################
|
|
|
|
DEBUG = False
|
|
DEFAULT_TEMPLATE_ENGINE['OPTIONS']['debug'] = False
|
|
|
|
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')
|
|
################################ END ALWAYS THE SAME ##############################
|
|
|
|
# A file path to a YAML file from which to load all the configuration for the edx platform
|
|
CONFIG_FILE = get_env_setting('LMS_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 compatibility.
|
|
# Removing them may break plugins that rely on them.
|
|
ENV_TOKENS = __config__
|
|
AUTH_TOKENS = __config__
|
|
|
|
# Add the key/values from config into the global namespace of this module.
|
|
# But don't override the FEATURES dict because we do that in an additive way.
|
|
__config_copy__ = copy.deepcopy(__config__)
|
|
|
|
KEYS_WITH_MERGED_VALUES = [
|
|
'FEATURES',
|
|
'TRACKING_BACKENDS',
|
|
'EVENT_TRACKING_BACKENDS',
|
|
'JWT_AUTH',
|
|
'CELERY_QUEUES',
|
|
'MKTG_URL_LINK_MAP',
|
|
'MKTG_URL_OVERRIDES',
|
|
]
|
|
for key in KEYS_WITH_MERGED_VALUES:
|
|
if key in __config_copy__:
|
|
del __config_copy__[key]
|
|
|
|
vars().update(__config_copy__)
|
|
|
|
|
|
try:
|
|
# A file path to a YAML file from which to load all the code revisions currently deployed
|
|
REVISION_CONFIG_FILE = get_env_setting('REVISION_CFG')
|
|
|
|
with codecs.open(REVISION_CONFIG_FILE, encoding='utf-8') as f:
|
|
REVISION_CONFIG = yaml.safe_load(f)
|
|
except Exception: # pylint: disable=broad-except
|
|
REVISION_CONFIG = {}
|
|
|
|
# Do NOT calculate this dynamically at startup with git because it's *slow*.
|
|
EDX_PLATFORM_REVISION = REVISION_CONFIG.get('EDX_PLATFORM_REVISION', EDX_PLATFORM_REVISION)
|
|
|
|
# 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 ""
|
|
|
|
###################################### 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 = ENV_TOKENS.get('BROKER_HEARTBEAT', 60.0)
|
|
BROKER_HEARTBEAT_CHECKRATE = ENV_TOKENS.get('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)
|
|
HIGH_MEM_QUEUE = 'edx.{0}core.high_mem'.format(QUEUE_VARIANT)
|
|
|
|
CELERY_DEFAULT_QUEUE = DEFAULT_PRIORITY_QUEUE
|
|
CELERY_DEFAULT_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE
|
|
|
|
CELERY_QUEUES = {
|
|
HIGH_PRIORITY_QUEUE: {},
|
|
DEFAULT_PRIORITY_QUEUE: {},
|
|
HIGH_MEM_QUEUE: {},
|
|
}
|
|
|
|
CELERY_ROUTES = "{}celery.Router".format(QUEUE_VARIANT)
|
|
CELERYBEAT_SCHEDULE = {} # For scheduling tasks, entries can be added to this dict
|
|
|
|
# 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)
|
|
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
|
|
WEBPACK_LOADER['WORKERS']['STATS_FILE'] = STATIC_ROOT / "webpack-worker-stats.json"
|
|
|
|
|
|
# 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 += "/"
|
|
|
|
# Allow overriding build profile used by RequireJS with one
|
|
# contained on a custom theme
|
|
REQUIRE_BUILD_PROFILE = ENV_TOKENS.get('REQUIRE_BUILD_PROFILE', REQUIRE_BUILD_PROFILE)
|
|
|
|
# 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
|
|
|
|
CC_MERCHANT_NAME = ENV_TOKENS.get('CC_MERCHANT_NAME', PLATFORM_NAME)
|
|
EMAIL_FILE_PATH = ENV_TOKENS.get('EMAIL_FILE_PATH', None)
|
|
EMAIL_HOST = ENV_TOKENS.get('EMAIL_HOST', 'localhost') # django default is localhost
|
|
EMAIL_PORT = ENV_TOKENS.get('EMAIL_PORT', 25) # django default is 25
|
|
EMAIL_USE_TLS = ENV_TOKENS.get('EMAIL_USE_TLS', False) # django default is False
|
|
SITE_NAME = ENV_TOKENS['SITE_NAME']
|
|
SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN')
|
|
SESSION_COOKIE_HTTPONLY = ENV_TOKENS.get('SESSION_COOKIE_HTTPONLY', True)
|
|
|
|
DCS_SESSION_COOKIE_SAMESITE = ENV_TOKENS.get('DCS_SESSION_COOKIE_SAMESITE', DCS_SESSION_COOKIE_SAMESITE)
|
|
DCS_SESSION_COOKIE_SAMESITE_FORCE_ALL = ENV_TOKENS.get('DCS_SESSION_COOKIE_SAMESITE_FORCE_ALL', DCS_SESSION_COOKIE_SAMESITE_FORCE_ALL)
|
|
|
|
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')
|
|
|
|
REGISTRATION_EMAIL_PATTERNS_ALLOWED = ENV_TOKENS.get('REGISTRATION_EMAIL_PATTERNS_ALLOWED')
|
|
|
|
LMS_ROOT_URL = ENV_TOKENS.get('LMS_ROOT_URL')
|
|
LMS_INTERNAL_ROOT_URL = ENV_TOKENS.get('LMS_INTERNAL_ROOT_URL', LMS_ROOT_URL)
|
|
|
|
# List of logout URIs for each IDA that the learner should be logged out of when they logout of the LMS. 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', [])
|
|
|
|
ENV_FEATURES = ENV_TOKENS.get('FEATURES', {})
|
|
for feature, value in ENV_FEATURES.items():
|
|
FEATURES[feature] = value
|
|
|
|
CMS_BASE = ENV_TOKENS.get('CMS_BASE', 'studio.edx.org')
|
|
|
|
ALLOWED_HOSTS = [
|
|
# TODO: bbeggs remove this before prod, temp fix to get load testing running
|
|
"*",
|
|
ENV_TOKENS.get('LMS_BASE'),
|
|
FEATURES['PREVIEW_LMS_BASE'],
|
|
]
|
|
|
|
# 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'))
|
|
|
|
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',
|
|
}
|
|
|
|
if 'staticfiles' in CACHES:
|
|
CACHES['staticfiles']['KEY_PREFIX'] = EDX_PLATFORM_REVISION
|
|
|
|
# In order to transition from local disk asset storage to S3 backed asset storage,
|
|
# we need to run asset collection twice, once for local disk and once for S3.
|
|
# Once we have migrated to service assets off S3, then we can convert this back to
|
|
# managed by the yaml file contents
|
|
STATICFILES_STORAGE = os.environ.get('STATICFILES_STORAGE', ENV_TOKENS.get('STATICFILES_STORAGE', STATICFILES_STORAGE))
|
|
|
|
# Load all AWS_ prefixed variables to allow an S3Boto3Storage to be configured
|
|
_locals = locals()
|
|
for key, value in ENV_TOKENS.items():
|
|
if key.startswith('AWS_'):
|
|
_locals[key] = value
|
|
|
|
# Currency
|
|
PAID_COURSE_REGISTRATION_CURRENCY = ENV_TOKENS.get('PAID_COURSE_REGISTRATION_CURRENCY',
|
|
PAID_COURSE_REGISTRATION_CURRENCY)
|
|
|
|
# We want Bulk Email running on the high-priority queue, so we define the
|
|
# routing key that points to it. At the moment, the name is the same.
|
|
# We have to reset the value here, since we have changed the value of the queue name.
|
|
BULK_EMAIL_ROUTING_KEY = ENV_TOKENS.get('BULK_EMAIL_ROUTING_KEY', HIGH_PRIORITY_QUEUE)
|
|
|
|
# We can run smaller jobs on the low priority queue. See note above for why
|
|
# we have to reset the value here.
|
|
BULK_EMAIL_ROUTING_KEY_SMALL_JOBS = ENV_TOKENS.get('BULK_EMAIL_ROUTING_KEY_SMALL_JOBS', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
# Queue to use for expiring old entitlements
|
|
ENTITLEMENTS_EXPIRATION_ROUTING_KEY = ENV_TOKENS.get('ENTITLEMENTS_EXPIRATION_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
# 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())
|
|
}
|
|
)
|
|
|
|
# following setting is for backward compatibility
|
|
if ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR', None):
|
|
COMPREHENSIVE_THEME_DIR = ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR')
|
|
|
|
|
|
# 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', [])
|
|
|
|
|
|
MKTG_URL_LINK_MAP.update(ENV_TOKENS.get('MKTG_URL_LINK_MAP', {}))
|
|
ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS = ENV_TOKENS.get(
|
|
'ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS',
|
|
ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS
|
|
)
|
|
# Marketing link overrides
|
|
MKTG_URL_OVERRIDES.update(ENV_TOKENS.get('MKTG_URL_OVERRIDES', MKTG_URL_OVERRIDES))
|
|
|
|
# Intentional defaults.
|
|
ID_VERIFICATION_SUPPORT_LINK = ENV_TOKENS.get('ID_VERIFICATION_SUPPORT_LINK', SUPPORT_SITE_LINK)
|
|
PASSWORD_RESET_SUPPORT_LINK = ENV_TOKENS.get('PASSWORD_RESET_SUPPORT_LINK', SUPPORT_SITE_LINK)
|
|
ACTIVATION_EMAIL_SUPPORT_LINK = ENV_TOKENS.get(
|
|
'ACTIVATION_EMAIL_SUPPORT_LINK', SUPPORT_SITE_LINK
|
|
)
|
|
|
|
# Timezone overrides
|
|
TIME_ZONE = ENV_TOKENS.get('CELERY_TIMEZONE', CELERY_TIMEZONE)
|
|
|
|
# Translation overrides
|
|
LANGUAGE_DICT = dict(LANGUAGES)
|
|
|
|
# Additional installed apps
|
|
for app in ENV_TOKENS.get('ADDL_INSTALLED_APPS', []):
|
|
INSTALLED_APPS.append(app)
|
|
|
|
|
|
local_loglevel = ENV_TOKENS.get('LOCAL_LOGLEVEL', 'INFO')
|
|
LOG_DIR = ENV_TOKENS['LOG_DIR']
|
|
DATA_DIR = path(ENV_TOKENS.get('DATA_DIR', DATA_DIR))
|
|
|
|
LOGGING = get_logger_config(LOG_DIR,
|
|
logging_env=ENV_TOKENS['LOGGING_ENV'],
|
|
local_loglevel=local_loglevel,
|
|
service_variant=SERVICE_VARIANT)
|
|
|
|
COURSE_LISTINGS = ENV_TOKENS.get('COURSE_LISTINGS', {})
|
|
COMMENTS_SERVICE_URL = ENV_TOKENS.get("COMMENTS_SERVICE_URL", '')
|
|
COMMENTS_SERVICE_KEY = ENV_TOKENS.get("COMMENTS_SERVICE_KEY", '')
|
|
CERT_QUEUE = ENV_TOKENS.get("CERT_QUEUE", 'test-pull')
|
|
|
|
# git repo loading environment
|
|
GIT_REPO_DIR = ENV_TOKENS.get('GIT_REPO_DIR', '/edx/var/edxapp/course_repos')
|
|
GIT_IMPORT_STATIC = ENV_TOKENS.get('GIT_IMPORT_STATIC', True)
|
|
GIT_IMPORT_PYTHON_LIB = ENV_TOKENS.get('GIT_IMPORT_PYTHON_LIB', True)
|
|
PYTHON_LIB_FILENAME = ENV_TOKENS.get('PYTHON_LIB_FILENAME', 'python_lib.zip')
|
|
|
|
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", [])
|
|
|
|
# Event Tracking
|
|
if "TRACKING_IGNORE_URL_PATTERNS" in ENV_TOKENS:
|
|
TRACKING_IGNORE_URL_PATTERNS = ENV_TOKENS.get("TRACKING_IGNORE_URL_PATTERNS")
|
|
|
|
# SSL external authentication settings
|
|
SSL_AUTH_EMAIL_DOMAIN = ENV_TOKENS.get("SSL_AUTH_EMAIL_DOMAIN", "MIT.EDU")
|
|
SSL_AUTH_DN_FORMAT_STRING = ENV_TOKENS.get(
|
|
"SSL_AUTH_DN_FORMAT_STRING",
|
|
u"/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}"
|
|
)
|
|
|
|
# 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', {})
|
|
|
|
# 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 enviroments accessed
|
|
# by end users.
|
|
CSRF_COOKIE_SECURE = ENV_TOKENS.get('CSRF_COOKIE_SECURE', False)
|
|
|
|
# Determines which origins are trusted for unsafe requests eg. POST requests.
|
|
CSRF_TRUSTED_ORIGINS = ENV_TOKENS.get('CSRF_TRUSTED_ORIGINS', [])
|
|
|
|
############# CORS headers for cross-domain requests #################
|
|
|
|
if FEATURES.get('ENABLE_CORS_HEADERS') or FEATURES.get('ENABLE_CROSS_DOMAIN_CSRF_COOKIE'):
|
|
CORS_ALLOW_CREDENTIALS = True
|
|
CORS_ORIGIN_WHITELIST = ENV_TOKENS.get('CORS_ORIGIN_WHITELIST', ())
|
|
CORS_ORIGIN_ALLOW_ALL = ENV_TOKENS.get('CORS_ORIGIN_ALLOW_ALL', False)
|
|
CORS_ALLOW_INSECURE = ENV_TOKENS.get('CORS_ALLOW_INSECURE', False)
|
|
CORS_ALLOW_HEADERS = corsheaders_default_headers + (
|
|
'use-jwt-cookie',
|
|
)
|
|
|
|
# If setting a cross-domain cookie, it's really important to choose
|
|
# a name for the cookie that is DIFFERENT than the cookies used
|
|
# by each subdomain. For example, suppose the applications
|
|
# at these subdomains are configured to use the following cookie names:
|
|
#
|
|
# 1) foo.example.com --> "csrftoken"
|
|
# 2) baz.example.com --> "csrftoken"
|
|
# 3) bar.example.com --> "csrftoken"
|
|
#
|
|
# For the cross-domain version of the CSRF cookie, you need to choose
|
|
# a name DIFFERENT than "csrftoken"; otherwise, the new token configured
|
|
# for ".example.com" could conflict with the other cookies,
|
|
# non-deterministically causing 403 responses.
|
|
#
|
|
# Because of the way Django stores cookies, the cookie name MUST
|
|
# be a `str`, not unicode. Otherwise there will `TypeError`s will be raised
|
|
# when Django tries to call the unicode `translate()` method with the wrong
|
|
# number of parameters.
|
|
CROSS_DOMAIN_CSRF_COOKIE_NAME = str(ENV_TOKENS.get('CROSS_DOMAIN_CSRF_COOKIE_NAME'))
|
|
|
|
# When setting the domain for the "cross-domain" version of the CSRF
|
|
# cookie, you should choose something like: ".example.com"
|
|
# (note the leading dot), where both the referer and the host
|
|
# are subdomains of "example.com".
|
|
#
|
|
# Browser security rules require that
|
|
# the cookie domain matches the domain of the server; otherwise
|
|
# the cookie won't get set. And once the cookie gets set, the client
|
|
# needs to be on a domain that matches the cookie domain, otherwise
|
|
# the client won't be able to read the cookie.
|
|
CROSS_DOMAIN_CSRF_COOKIE_DOMAIN = ENV_TOKENS.get('CROSS_DOMAIN_CSRF_COOKIE_DOMAIN')
|
|
|
|
|
|
# Field overrides. To use the IDDE feature, add
|
|
# 'courseware.student_field_overrides.IndividualStudentOverrideProvider'.
|
|
FIELD_OVERRIDE_PROVIDERS = tuple(ENV_TOKENS.get('FIELD_OVERRIDE_PROVIDERS', []))
|
|
|
|
############### XBlock filesystem field config ##########
|
|
if 'DJFS' in AUTH_TOKENS and AUTH_TOKENS['DJFS'] is not None:
|
|
DJFS = AUTH_TOKENS['DJFS']
|
|
|
|
############### Module Store Items ##########
|
|
HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = ENV_TOKENS.get('HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS', {})
|
|
# PREVIEW DOMAIN must be present in HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS for the preview to show draft changes
|
|
if 'PREVIEW_LMS_BASE' in FEATURES and FEATURES['PREVIEW_LMS_BASE'] != '':
|
|
PREVIEW_DOMAIN = FEATURES['PREVIEW_LMS_BASE'].split(':')[0]
|
|
# update dictionary with preview domain regex
|
|
HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS.update({
|
|
PREVIEW_DOMAIN: 'draft-preferred'
|
|
})
|
|
|
|
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
|
|
)
|
|
|
|
############### Mixed Related(Secure/Not-Secure) Items ##########
|
|
LMS_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_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'
|
|
|
|
# If there is a database called 'read_replica', you can use the use_read_replica_if_available
|
|
# function in util/query.py, which is useful for very large database reads
|
|
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']),
|
|
})
|
|
|
|
XQUEUE_INTERFACE = AUTH_TOKENS['XQUEUE_INTERFACE']
|
|
|
|
# Get the MODULESTORE from auth.json, but if it doesn't exist,
|
|
# use the one from common.py
|
|
MODULESTORE = convert_module_store_setting_if_needed(AUTH_TOKENS.get('MODULESTORE', MODULESTORE))
|
|
MONGODB_LOG = AUTH_TOKENS.get('MONGODB_LOG', {})
|
|
|
|
EMAIL_HOST_USER = AUTH_TOKENS.get('EMAIL_HOST_USER', '') # django default is ''
|
|
EMAIL_HOST_PASSWORD = AUTH_TOKENS.get('EMAIL_HOST_PASSWORD', '') # django default is ''
|
|
|
|
############################### 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']
|
|
|
|
# Analytics API
|
|
ANALYTICS_API_KEY = AUTH_TOKENS.get("ANALYTICS_API_KEY", ANALYTICS_API_KEY)
|
|
ANALYTICS_API_URL = ENV_TOKENS.get("ANALYTICS_API_URL", ANALYTICS_API_URL)
|
|
|
|
# Zendesk
|
|
ZENDESK_USER = AUTH_TOKENS.get("ZENDESK_USER")
|
|
ZENDESK_API_KEY = AUTH_TOKENS.get("ZENDESK_API_KEY")
|
|
|
|
# API Key for inbound requests from Notifier service
|
|
EDX_API_KEY = AUTH_TOKENS.get("EDX_API_KEY")
|
|
|
|
# Celery Broker
|
|
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)
|
|
|
|
# Block Structures
|
|
|
|
# upload limits
|
|
STUDENT_FILEUPLOAD_MAX_SIZE = ENV_TOKENS.get("STUDENT_FILEUPLOAD_MAX_SIZE", STUDENT_FILEUPLOAD_MAX_SIZE)
|
|
|
|
# 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", []))
|
|
TRACKING_SEGMENTIO_WEBHOOK_SECRET = AUTH_TOKENS.get(
|
|
"TRACKING_SEGMENTIO_WEBHOOK_SECRET",
|
|
TRACKING_SEGMENTIO_WEBHOOK_SECRET
|
|
)
|
|
TRACKING_SEGMENTIO_ALLOWED_TYPES = ENV_TOKENS.get("TRACKING_SEGMENTIO_ALLOWED_TYPES", TRACKING_SEGMENTIO_ALLOWED_TYPES)
|
|
TRACKING_SEGMENTIO_DISALLOWED_SUBSTRING_NAMES = ENV_TOKENS.get(
|
|
"TRACKING_SEGMENTIO_DISALLOWED_SUBSTRING_NAMES",
|
|
TRACKING_SEGMENTIO_DISALLOWED_SUBSTRING_NAMES
|
|
)
|
|
TRACKING_SEGMENTIO_SOURCE_MAP = ENV_TOKENS.get("TRACKING_SEGMENTIO_SOURCE_MAP", TRACKING_SEGMENTIO_SOURCE_MAP)
|
|
|
|
# Student identity verification settings
|
|
VERIFY_STUDENT = AUTH_TOKENS.get("VERIFY_STUDENT", VERIFY_STUDENT)
|
|
DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH = ENV_TOKENS.get(
|
|
"DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH",
|
|
DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH
|
|
)
|
|
|
|
# Grades download
|
|
GRADES_DOWNLOAD_ROUTING_KEY = ENV_TOKENS.get('GRADES_DOWNLOAD_ROUTING_KEY', HIGH_MEM_QUEUE)
|
|
|
|
GRADES_DOWNLOAD = ENV_TOKENS.get("GRADES_DOWNLOAD", GRADES_DOWNLOAD)
|
|
|
|
# Rate limit for regrading tasks that a grading policy change can kick off
|
|
|
|
# financial reports
|
|
FINANCIAL_REPORTS = ENV_TOKENS.get("FINANCIAL_REPORTS", FINANCIAL_REPORTS)
|
|
|
|
##### ORA2 ######
|
|
# Prefix for uploads of example-based assessment AI classifiers
|
|
# This can be used to separate uploads for different environments
|
|
# within the same S3 bucket.
|
|
ORA2_FILE_PREFIX = ENV_TOKENS.get("ORA2_FILE_PREFIX", ORA2_FILE_PREFIX)
|
|
|
|
##### 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
|
|
)
|
|
|
|
##### LOGISTRATION RATE LIMIT SETTINGS #####
|
|
LOGISTRATION_RATELIMIT_RATE = ENV_TOKENS.get('LOGISTRATION_RATELIMIT_RATE', LOGISTRATION_RATELIMIT_RATE)
|
|
|
|
#### 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")
|
|
|
|
##### LMS DEADLINE DISPLAY TIME_ZONE #######
|
|
TIME_ZONE_DISPLAYED_FOR_DEADLINES = ENV_TOKENS.get("TIME_ZONE_DISPLAYED_FOR_DEADLINES",
|
|
TIME_ZONE_DISPLAYED_FOR_DEADLINES)
|
|
|
|
##### Third-party auth options ################################################
|
|
if FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
|
|
tmp_backends = ENV_TOKENS.get('THIRD_PARTY_AUTH_BACKENDS', [
|
|
'social_core.backends.google.GoogleOAuth2',
|
|
'social_core.backends.linkedin.LinkedinOAuth2',
|
|
'social_core.backends.facebook.FacebookOAuth2',
|
|
'social_core.backends.azuread.AzureADOAuth2',
|
|
'social_core.backends.apple.AppleIdAuth',
|
|
'third_party_auth.identityserver3.IdentityServer3',
|
|
'third_party_auth.saml.SAMLAuthBackend',
|
|
'third_party_auth.lti.LTIAuthBackend',
|
|
])
|
|
|
|
AUTHENTICATION_BACKENDS = list(tmp_backends) + list(AUTHENTICATION_BACKENDS)
|
|
del tmp_backends
|
|
|
|
# The reduced session expiry time during the third party login pipeline. (Value in seconds)
|
|
SOCIAL_AUTH_PIPELINE_TIMEOUT = ENV_TOKENS.get('SOCIAL_AUTH_PIPELINE_TIMEOUT', 600)
|
|
|
|
# Most provider configuration is done via ConfigurationModels but for a few sensitive values
|
|
# we allow configuration via AUTH_TOKENS instead (optionally).
|
|
# The SAML private/public key values do not need the delimiter lines (such as
|
|
# "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----" etc.) but they may be included
|
|
# if you want (though it's easier to format the key values as JSON without the delimiters).
|
|
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = AUTH_TOKENS.get('SOCIAL_AUTH_SAML_SP_PRIVATE_KEY', '')
|
|
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = AUTH_TOKENS.get('SOCIAL_AUTH_SAML_SP_PUBLIC_CERT', '')
|
|
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY_DICT = AUTH_TOKENS.get('SOCIAL_AUTH_SAML_SP_PRIVATE_KEY_DICT', {})
|
|
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT_DICT = AUTH_TOKENS.get('SOCIAL_AUTH_SAML_SP_PUBLIC_CERT_DICT', {})
|
|
SOCIAL_AUTH_OAUTH_SECRETS = AUTH_TOKENS.get('SOCIAL_AUTH_OAUTH_SECRETS', {})
|
|
SOCIAL_AUTH_LTI_CONSUMER_SECRETS = AUTH_TOKENS.get('SOCIAL_AUTH_LTI_CONSUMER_SECRETS', {})
|
|
|
|
# third_party_auth config moved to ConfigurationModels. This is for data migration only:
|
|
THIRD_PARTY_AUTH_OLD_CONFIG = AUTH_TOKENS.get('THIRD_PARTY_AUTH', None)
|
|
|
|
if ENV_TOKENS.get('THIRD_PARTY_AUTH_SAML_FETCH_PERIOD_HOURS', 24) is not None:
|
|
CELERYBEAT_SCHEDULE['refresh-saml-metadata'] = {
|
|
'task': 'third_party_auth.fetch_saml_metadata',
|
|
'schedule': datetime.timedelta(hours=ENV_TOKENS.get('THIRD_PARTY_AUTH_SAML_FETCH_PERIOD_HOURS', 24)),
|
|
}
|
|
|
|
# The following can be used to integrate a custom login form with third_party_auth.
|
|
# It should be a dict where the key is a word passed via ?auth_entry=, and the value is a
|
|
# dict with an arbitrary 'secret_key' and a 'url'.
|
|
THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS = AUTH_TOKENS.get('THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS', {})
|
|
|
|
##### OAUTH2 Provider ##############
|
|
if FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
|
|
OAUTH_ENFORCE_SECURE = ENV_TOKENS.get('OAUTH_ENFORCE_SECURE', True)
|
|
OAUTH_ENFORCE_CLIENT_SECURE = ENV_TOKENS.get('OAUTH_ENFORCE_CLIENT_SECURE', True)
|
|
# Defaults for the following are defined in lms.envs.common
|
|
OAUTH_EXPIRE_DELTA = datetime.timedelta(
|
|
days=ENV_TOKENS.get('OAUTH_EXPIRE_CONFIDENTIAL_CLIENT_DAYS', OAUTH_EXPIRE_CONFIDENTIAL_CLIENT_DAYS)
|
|
)
|
|
OAUTH_EXPIRE_DELTA_PUBLIC = datetime.timedelta(
|
|
days=ENV_TOKENS.get('OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS', OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS)
|
|
)
|
|
|
|
|
|
##### GOOGLE ANALYTICS IDS #####
|
|
GOOGLE_ANALYTICS_ACCOUNT = AUTH_TOKENS.get('GOOGLE_ANALYTICS_ACCOUNT')
|
|
GOOGLE_ANALYTICS_TRACKING_ID = AUTH_TOKENS.get('GOOGLE_ANALYTICS_TRACKING_ID')
|
|
GOOGLE_ANALYTICS_LINKEDIN = AUTH_TOKENS.get('GOOGLE_ANALYTICS_LINKEDIN')
|
|
GOOGLE_SITE_VERIFICATION_ID = ENV_TOKENS.get('GOOGLE_SITE_VERIFICATION_ID')
|
|
|
|
##### BRANCH.IO KEY #####
|
|
BRANCH_IO_KEY = AUTH_TOKENS.get('BRANCH_IO_KEY')
|
|
|
|
#### Course Registration Code length ####
|
|
REGISTRATION_CODE_LENGTH = ENV_TOKENS.get('REGISTRATION_CODE_LENGTH', 8)
|
|
|
|
# Which access.py permission names to check;
|
|
# We default this to the legacy permission 'see_exists'.
|
|
COURSE_CATALOG_VISIBILITY_PERMISSION = ENV_TOKENS.get(
|
|
'COURSE_CATALOG_VISIBILITY_PERMISSION',
|
|
COURSE_CATALOG_VISIBILITY_PERMISSION
|
|
)
|
|
COURSE_ABOUT_VISIBILITY_PERMISSION = ENV_TOKENS.get(
|
|
'COURSE_ABOUT_VISIBILITY_PERMISSION',
|
|
COURSE_ABOUT_VISIBILITY_PERMISSION
|
|
)
|
|
|
|
DEFAULT_COURSE_VISIBILITY_IN_CATALOG = ENV_TOKENS.get(
|
|
'DEFAULT_COURSE_VISIBILITY_IN_CATALOG',
|
|
DEFAULT_COURSE_VISIBILITY_IN_CATALOG
|
|
)
|
|
|
|
DEFAULT_MOBILE_AVAILABLE = ENV_TOKENS.get(
|
|
'DEFAULT_MOBILE_AVAILABLE',
|
|
DEFAULT_MOBILE_AVAILABLE
|
|
)
|
|
|
|
# Enrollment API Cache Timeout
|
|
ENROLLMENT_COURSE_DETAILS_CACHE_TIMEOUT = ENV_TOKENS.get('ENROLLMENT_COURSE_DETAILS_CACHE_TIMEOUT', 60)
|
|
|
|
if FEATURES.get('ENABLE_COURSEWARE_SEARCH') or \
|
|
FEATURES.get('ENABLE_DASHBOARD_SEARCH') or \
|
|
FEATURES.get('ENABLE_COURSE_DISCOVERY') or \
|
|
FEATURES.get('ENABLE_TEAMS'):
|
|
# Use ElasticSearch as the search engine herein
|
|
SEARCH_ENGINE = "search.elastic.ElasticSearchEngine"
|
|
SEARCH_FILTER_GENERATOR = ENV_TOKENS.get('SEARCH_FILTER_GENERATOR', SEARCH_FILTER_GENERATOR)
|
|
|
|
ELASTIC_SEARCH_CONFIG = ENV_TOKENS.get('ELASTIC_SEARCH_CONFIG', [{}])
|
|
|
|
# Facebook app
|
|
FACEBOOK_API_VERSION = AUTH_TOKENS.get("FACEBOOK_API_VERSION")
|
|
FACEBOOK_APP_SECRET = AUTH_TOKENS.get("FACEBOOK_APP_SECRET")
|
|
FACEBOOK_APP_ID = AUTH_TOKENS.get("FACEBOOK_APP_ID")
|
|
|
|
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)
|
|
|
|
##### Custom Courses for EdX #####
|
|
if FEATURES.get('CUSTOM_COURSES_EDX'):
|
|
INSTALLED_APPS += ['lms.djangoapps.ccx', 'openedx.core.djangoapps.ccxcon.apps.CCXConnectorConfig']
|
|
MODULESTORE_FIELD_OVERRIDE_PROVIDERS += (
|
|
'lms.djangoapps.ccx.overrides.CustomCoursesForEdxOverrideProvider',
|
|
)
|
|
|
|
##### Individual Due Date Extensions #####
|
|
if FEATURES.get('INDIVIDUAL_DUE_DATES'):
|
|
FIELD_OVERRIDE_PROVIDERS += (
|
|
'courseware.student_field_overrides.IndividualStudentOverrideProvider',
|
|
)
|
|
|
|
##### Show Answer Override for Self-Paced Courses #####
|
|
FIELD_OVERRIDE_PROVIDERS += (
|
|
'openedx.features.personalized_learner_schedules.show_answer.show_answer_field_override.ShowAnswerFieldOverride',
|
|
)
|
|
|
|
##### Self-Paced Course Due Dates #####
|
|
XBLOCK_FIELD_DATA_WRAPPERS += (
|
|
'lms.djangoapps.courseware.field_overrides:OverrideModulestoreFieldData.wrap',
|
|
)
|
|
|
|
MODULESTORE_FIELD_OVERRIDE_PROVIDERS += (
|
|
'lms.djangoapps.courseware.self_paced_overrides.SelfPacedDateOverrideProvider',
|
|
)
|
|
|
|
# PROFILE IMAGE CONFIG
|
|
PROFILE_IMAGE_DEFAULT_FILENAME = 'images/profiles/default'
|
|
PROFILE_IMAGE_SIZES_MAP = ENV_TOKENS.get(
|
|
'PROFILE_IMAGE_SIZES_MAP',
|
|
PROFILE_IMAGE_SIZES_MAP
|
|
)
|
|
|
|
##### Credit Provider Integration #####
|
|
|
|
CREDIT_PROVIDER_SECRET_KEYS = AUTH_TOKENS.get("CREDIT_PROVIDER_SECRET_KEYS", {})
|
|
|
|
##################### LTI Provider #####################
|
|
if FEATURES.get('ENABLE_LTI_PROVIDER'):
|
|
INSTALLED_APPS.append('lti_provider.apps.LtiProviderConfig')
|
|
AUTHENTICATION_BACKENDS.append('lti_provider.users.LtiBackend')
|
|
|
|
LTI_USER_EMAIL_DOMAIN = ENV_TOKENS.get('LTI_USER_EMAIL_DOMAIN', 'lti.example.com')
|
|
|
|
# For more info on this, see the notes in common.py
|
|
LTI_AGGREGATE_SCORE_PASSBACK_DELAY = ENV_TOKENS.get(
|
|
'LTI_AGGREGATE_SCORE_PASSBACK_DELAY', LTI_AGGREGATE_SCORE_PASSBACK_DELAY
|
|
)
|
|
|
|
##################### Credit Provider help link ####################
|
|
|
|
#### JWT configuration ####
|
|
JWT_AUTH.update(ENV_TOKENS.get('JWT_AUTH', {}))
|
|
JWT_AUTH.update(AUTH_TOKENS.get('JWT_AUTH', {}))
|
|
|
|
# Offset for pk of courseware.StudentModuleHistoryExtended
|
|
STUDENTMODULEHISTORYEXTENDED_OFFSET = ENV_TOKENS.get(
|
|
'STUDENTMODULEHISTORYEXTENDED_OFFSET', STUDENTMODULEHISTORYEXTENDED_OFFSET
|
|
)
|
|
|
|
# Cutoff date for granting audit certificates
|
|
if ENV_TOKENS.get('AUDIT_CERT_CUTOFF_DATE', None):
|
|
AUDIT_CERT_CUTOFF_DATE = dateutil.parser.parse(ENV_TOKENS.get('AUDIT_CERT_CUTOFF_DATE'))
|
|
|
|
################################ Settings for Credentials Service ################################
|
|
|
|
CREDENTIALS_GENERATION_ROUTING_KEY = ENV_TOKENS.get('CREDENTIALS_GENERATION_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
# Queue to use for award program certificates
|
|
PROGRAM_CERTIFICATES_ROUTING_KEY = ENV_TOKENS.get('PROGRAM_CERTIFICATES_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE)
|
|
SOFTWARE_SECURE_VERIFICATION_ROUTING_KEY = ENV_TOKENS.get(
|
|
'SOFTWARE_SECURE_VERIFICATION_ROUTING_KEY',
|
|
HIGH_PRIORITY_QUEUE
|
|
)
|
|
|
|
API_ACCESS_MANAGER_EMAIL = ENV_TOKENS.get('API_ACCESS_MANAGER_EMAIL')
|
|
API_ACCESS_FROM_EMAIL = ENV_TOKENS.get('API_ACCESS_FROM_EMAIL')
|
|
|
|
############## OPEN EDX ENTERPRISE SERVICE CONFIGURATION ######################
|
|
# The Open edX Enterprise service is currently hosted via the LMS container/process.
|
|
# However, for all intents and purposes this service is treated as a standalone IDA.
|
|
# These configuration settings are specific to the Enterprise service and you should
|
|
# not find references to them within the edx-platform project.
|
|
|
|
# Publicly-accessible enrollment URL, for use on the client side.
|
|
ENTERPRISE_PUBLIC_ENROLLMENT_API_URL = ENV_TOKENS.get(
|
|
'ENTERPRISE_PUBLIC_ENROLLMENT_API_URL',
|
|
(LMS_ROOT_URL or '') + LMS_ENROLLMENT_API_PATH
|
|
)
|
|
|
|
# Enrollment URL used on the server-side.
|
|
ENTERPRISE_ENROLLMENT_API_URL = ENV_TOKENS.get(
|
|
'ENTERPRISE_ENROLLMENT_API_URL',
|
|
(LMS_INTERNAL_ROOT_URL or '') + LMS_ENROLLMENT_API_PATH
|
|
)
|
|
|
|
# Enterprise logo image size limit in KB's
|
|
ENTERPRISE_CUSTOMER_LOGO_IMAGE_SIZE = ENV_TOKENS.get(
|
|
'ENTERPRISE_CUSTOMER_LOGO_IMAGE_SIZE',
|
|
ENTERPRISE_CUSTOMER_LOGO_IMAGE_SIZE
|
|
)
|
|
|
|
# Course enrollment modes to be hidden in the Enterprise enrollment page
|
|
# if the "Hide audit track" flag is enabled for an EnterpriseCustomer
|
|
ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES = ENV_TOKENS.get(
|
|
'ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES',
|
|
ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES
|
|
)
|
|
|
|
# A support URL used on Enterprise landing pages for when a warning
|
|
# message goes off.
|
|
ENTERPRISE_SUPPORT_URL = ENV_TOKENS.get(
|
|
'ENTERPRISE_SUPPORT_URL',
|
|
ENTERPRISE_SUPPORT_URL
|
|
)
|
|
|
|
# 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
|
|
)
|
|
|
|
############## ENTERPRISE SERVICE API CLIENT CONFIGURATION ######################
|
|
# The LMS communicates with the Enterprise service via the EdxRestApiClient class
|
|
# The below environmental settings are utilized by the LMS when interacting with
|
|
# the service, and override the default parameters which are defined in common.py
|
|
|
|
DEFAULT_ENTERPRISE_API_URL = None
|
|
if LMS_INTERNAL_ROOT_URL is not None:
|
|
DEFAULT_ENTERPRISE_API_URL = LMS_INTERNAL_ROOT_URL + '/enterprise/api/v1/'
|
|
ENTERPRISE_API_URL = ENV_TOKENS.get('ENTERPRISE_API_URL', DEFAULT_ENTERPRISE_API_URL)
|
|
|
|
DEFAULT_ENTERPRISE_CONSENT_API_URL = None
|
|
if LMS_INTERNAL_ROOT_URL is not None:
|
|
DEFAULT_ENTERPRISE_CONSENT_API_URL = LMS_INTERNAL_ROOT_URL + '/consent/api/v1/'
|
|
ENTERPRISE_CONSENT_API_URL = ENV_TOKENS.get('ENTERPRISE_CONSENT_API_URL', DEFAULT_ENTERPRISE_CONSENT_API_URL)
|
|
|
|
ENTERPRISE_SERVICE_WORKER_USERNAME = ENV_TOKENS.get(
|
|
'ENTERPRISE_SERVICE_WORKER_USERNAME',
|
|
ENTERPRISE_SERVICE_WORKER_USERNAME
|
|
)
|
|
ENTERPRISE_API_CACHE_TIMEOUT = ENV_TOKENS.get(
|
|
'ENTERPRISE_API_CACHE_TIMEOUT',
|
|
ENTERPRISE_API_CACHE_TIMEOUT
|
|
)
|
|
ENTERPRISE_CATALOG_INTERNAL_ROOT_URL = ENV_TOKENS.get(
|
|
'ENTERPRISE_CATALOG_INTERNAL_ROOT_URL',
|
|
ENTERPRISE_CATALOG_INTERNAL_ROOT_URL
|
|
)
|
|
|
|
# List of enterprise customer uuids to exclude from transition to use of enterprise-catalog
|
|
ENTERPRISE_CUSTOMERS_EXCLUDED_FROM_CATALOG = ENV_TOKENS.get('ENTERPRISE_CUSTOMERS_EXCLUDED_FROM_CATALOG', ())
|
|
|
|
############## ENTERPRISE SERVICE LMS CONFIGURATION ##################################
|
|
# The LMS has some features embedded that are related to the Enterprise service, but
|
|
# which are not provided by the Enterprise service. These settings override the
|
|
# base values for the parameters as defined in common.py
|
|
|
|
ENTERPRISE_PLATFORM_WELCOME_TEMPLATE = ENV_TOKENS.get(
|
|
'ENTERPRISE_PLATFORM_WELCOME_TEMPLATE',
|
|
ENTERPRISE_PLATFORM_WELCOME_TEMPLATE
|
|
)
|
|
ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE = ENV_TOKENS.get(
|
|
'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
|
|
ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
|
|
)
|
|
ENTERPRISE_TAGLINE = ENV_TOKENS.get(
|
|
'ENTERPRISE_TAGLINE',
|
|
ENTERPRISE_TAGLINE
|
|
)
|
|
ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS = set(
|
|
ENV_TOKENS.get(
|
|
'ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS',
|
|
ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS
|
|
)
|
|
)
|
|
BASE_COOKIE_DOMAIN = ENV_TOKENS.get(
|
|
'BASE_COOKIE_DOMAIN',
|
|
BASE_COOKIE_DOMAIN
|
|
)
|
|
SYSTEM_TO_FEATURE_ROLE_MAPPING = ENV_TOKENS.get(
|
|
'SYSTEM_TO_FEATURE_ROLE_MAPPING',
|
|
SYSTEM_TO_FEATURE_ROLE_MAPPING
|
|
)
|
|
|
|
# Add an ICP license for serving content in China if your organization is registered to do so
|
|
ICP_LICENSE = ENV_TOKENS.get('ICP_LICENSE', None)
|
|
ICP_LICENSE_INFO = ENV_TOKENS.get('ICP_LICENSE_INFO', {})
|
|
|
|
############## Settings for CourseGraph ############################
|
|
COURSEGRAPH_JOB_QUEUE = ENV_TOKENS.get('COURSEGRAPH_JOB_QUEUE', DEFAULT_PRIORITY_QUEUE)
|
|
|
|
# How long to cache OpenAPI schemas and UI, in seconds.
|
|
OPENAPI_CACHE_TIMEOUT = ENV_TOKENS.get('OPENAPI_CACHE_TIMEOUT', 60 * 60)
|
|
|
|
########################## 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.extend(ENV_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', []))
|
|
|
|
################# Settings for the maintenance banner #################
|
|
MAINTENANCE_BANNER_TEXT = ENV_TOKENS.get('MAINTENANCE_BANNER_TEXT', None)
|
|
|
|
########################## limiting dashboard courses ######################
|
|
DASHBOARD_COURSE_LIMIT = ENV_TOKENS.get('DASHBOARD_COURSE_LIMIT', None)
|
|
|
|
######################## Setting for content libraries ########################
|
|
MAX_BLOCKS_PER_CONTENT_LIBRARY = ENV_TOKENS.get('MAX_BLOCKS_PER_CONTENT_LIBRARY', MAX_BLOCKS_PER_CONTENT_LIBRARY)
|
|
|
|
############################### Plugin Settings ###############################
|
|
|
|
# This is at the bottom because it is going to load more settings after base settings are loaded
|
|
|
|
# Load production.py in plugins
|
|
plugin_settings.add_plugins(__name__, plugin_constants.ProjectType.LMS, plugin_constants.SettingsType.PRODUCTION)
|
|
|
|
########################## Derive Any Derived Settings #######################
|
|
|
|
derive_settings(__name__)
|
|
|
|
############## 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)
|
|
COMPLETION_VIDEO_COMPLETE_PERCENTAGE = ENV_TOKENS.get('COMPLETION_BY_VIEWING_DELAY_MS',
|
|
COMPLETION_BY_VIEWING_DELAY_MS)
|