From 4035aa3487d5d22db181e580b3743f3c8f204e51 Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Fri, 17 Jan 2025 11:16:38 -0500 Subject: [PATCH] Revert "refactor: Clean up lms/envs/production.py cruft (#36115)" (#36129) This reverts commit 15939232d547b751a06e9cb0c1c39ba78144bade. --- lms/envs/common.py | 28 +- lms/envs/production.py | 875 +++++++++++++++++++++++++++++++---------- 2 files changed, 678 insertions(+), 225 deletions(-) diff --git a/lms/envs/common.py b/lms/envs/common.py index 23e0d12b3dd..cb7643c3668 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3392,34 +3392,8 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring CSRF_COOKIE_SECURE = False CSRF_TRUSTED_ORIGINS = [] CSRF_TRUSTED_ORIGINS_WITH_SCHEME = [] - -# 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. -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 = '' - +CROSS_DOMAIN_CSRF_COOKIE_NAME = '' ######################### Django Rest Framework ######################## diff --git a/lms/envs/production.py b/lms/envs/production.py index 3ea1c952e47..addcea0e602 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -22,6 +22,7 @@ import os import yaml +import django from django.core.exceptions import ImproperlyConfigured from edx_django_utils.plugins import add_plugins from openedx_events.event_bus import merge_producer_configs @@ -43,11 +44,7 @@ def get_env_setting(setting): error_msg = "Set the %s env variable" % setting raise ImproperlyConfigured(error_msg) # lint-amnesty, pylint: disable=raise-missing-from - -################################################# PRODUCTION DEFAULTS ################################################ -# We configure some defaults (beyond what has already been configured in common.py) before loading the YAML file below. -# DO NOT ADD NEW DEFAULTS HERE! Put any new setting defaults in common.py instead, along with a setting annotation. -# TODO: Move all these defaults into common.py. +################################ ALWAYS THE SAME ############################## DEBUG = False DEFAULT_TEMPLATE_ENGINE['OPTIONS']['debug'] = False @@ -61,72 +58,7 @@ def get_env_setting(setting): # 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_RESULT_BACKEND = 'django-cache' -BROKER_HEARTBEAT = 60.0 -BROKER_HEARTBEAT_CHECKRATE = 2 -STATIC_ROOT_BASE = None -STATIC_URL_BASE = None -EMAIL_HOST = 'localhost' -EMAIL_PORT = 25 -EMAIL_USE_TLS = False -SESSION_COOKIE_DOMAIN = None -SESSION_COOKIE_HTTPONLY = True -AWS_SES_REGION_NAME = 'us-east-1' -AWS_SES_REGION_ENDPOINT = 'email.us-east-1.amazonaws.com' -REGISTRATION_EMAIL_PATTERNS_ALLOWED = None -LMS_ROOT_URL = None -CMS_BASE = 'studio.edx.org' -CELERY_EVENT_QUEUE_TTL = None -COMPREHENSIVE_THEME_LOCALE_PATHS = [] -PREPEND_LOCALE_PATHS = [] -COURSE_LISTINGS = {} -COMMENTS_SERVICE_URL = '' -COMMENTS_SERVICE_KEY = '' -CERT_QUEUE = 'test-pull' -PYTHON_LIB_FILENAME = 'python_lib.zip' -VIDEO_CDN_URL = {} -HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = {} -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 = True -AWS_S3_CUSTOM_DOMAIN = 'edxuploads.s3.amazonaws.com' -MONGODB_LOG = {} -ZENDESK_USER = None -ZENDESK_API_KEY = None -EDX_API_KEY = None -CELERY_BROKER_TRANSPORT = "" -CELERY_BROKER_HOSTNAME = "" -CELERY_BROKER_VHOST = "" -CELERY_BROKER_USER = "" -CELERY_BROKER_PASSWORD = "" -BROKER_USE_SSL = False -SESSION_INACTIVITY_TIMEOUT_IN_SECONDS = None -ENABLE_REQUIRE_THIRD_PARTY_AUTH = False -GOOGLE_ANALYTICS_TRACKING_ID = None -GOOGLE_ANALYTICS_LINKEDIN = None -GOOGLE_SITE_VERIFICATION_ID = None -BRANCH_IO_KEY = None -REGISTRATION_CODE_LENGTH = 8 -FACEBOOK_API_VERSION = None -FACEBOOK_APP_SECRET = None -FACEBOOK_APP_ID = None -API_ACCESS_MANAGER_EMAIL = None -API_ACCESS_FROM_EMAIL = None -CHAT_COMPLETION_API = '' -CHAT_COMPLETION_API_KEY = '' -OPENAPI_CACHE_TIMEOUT = 60 * 60 -MAINTENANCE_BANNER_TEXT = None -DASHBOARD_COURSE_LIMIT = None - -# TODO: We believe these were part of the DEPR'd sysadmin dashboard, and can likely be removed. -SSL_AUTH_EMAIL_DOMAIN = "MIT.EDU" -SSL_AUTH_DN_FORMAT_STRING = ( - "/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}" -) - -####################################################################################################################### +################################ 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') @@ -134,13 +66,10 @@ def get_env_setting(setting): with codecs.open(CONFIG_FILE, encoding='utf-8') as f: __config__ = yaml.safe_load(f) - # _YAML_TOKENS contains the exact contents of the LMS_CFG YAML file. - # We do splat the entirety of the LMS_CFG YAML file (except KEYS_WITH_MERGED_VALUES) into this module. - # However, for precise backwards compatibility, we need to reference _YAML_TOKENS directly a few times, - # particularly we need to derive Django setting values from YAML values. - # This pattern is confusing and we discourage it. Rather than adding more _YAML_TOKENS references, please - # consider just referencing this module's variables directly. - _YAML_TOKENS = __config__ + # 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. @@ -153,6 +82,7 @@ def get_env_setting(setting): 'JWT_AUTH', 'CELERY_QUEUES', 'MKTG_URL_LINK_MAP', + 'MKTG_URL_OVERRIDES', 'REST_FRAMEWORK', 'EVENT_BUS_PRODUCER_CONFIG', ] @@ -181,11 +111,20 @@ def get_env_setting(setting): BROKER_POOL_LIMIT = 0 BROKER_CONNECTION_TIMEOUT = 1 +# Allow env to configure celery result backend with default set to django-cache +CELERY_RESULT_BACKEND = ENV_TOKENS.get('CELERY_RESULT_BACKEND', 'django-cache') + +# 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 # 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" @@ -193,34 +132,83 @@ def get_env_setting(setting): # 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 += "/" -DATA_DIR = path(DATA_DIR) -CC_MERCHANT_NAME = _YAML_TOKENS.get('CC_MERCHANT_NAME', PLATFORM_NAME) -EMAIL_FILE_PATH = _YAML_TOKENS.get('EMAIL_FILE_PATH', DATA_DIR / "emails" / "lms") - -# TODO: This was for backwards compatibility back when installed django-cookie-samesite (not since 2022). -# The DCS_ version of the setting can be DEPR'd at this point. +# 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 variable is an empty string. Therefore, setting these variable as empty text in related +# json files will make the system reads their 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 + +DATA_DIR = path(ENV_TOKENS.get('DATA_DIR', DATA_DIR)) +CC_MERCHANT_NAME = ENV_TOKENS.get('CC_MERCHANT_NAME', PLATFORM_NAME) +EMAIL_FILE_PATH = ENV_TOKENS.get('EMAIL_FILE_PATH', DATA_DIR / "emails" / "lms") +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.get('SITE_NAME', 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) # lint-amnesty, pylint: disable=line-too-long + +# As django-cookies-samesite package is set to be removed from base requirements when we upgrade to Django 3.2, +# we should follow the settings name provided by Django. +# https://docs.djangoproject.com/en/3.2/ref/settings/#session-cookie-samesite SESSION_COOKIE_SAMESITE = DCS_SESSION_COOKIE_SAMESITE -LMS_INTERNAL_ROOT_URL = _YAML_TOKENS.get('LMS_INTERNAL_ROOT_URL', LMS_ROOT_URL) +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') -for feature, value in _YAML_TOKENS.get('FEATURES', {}).items(): +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 = [ "*", - _YAML_TOKENS.get('LMS_BASE'), + ENV_TOKENS.get('LMS_BASE'), FEATURES['PREVIEW_LMS_BASE'], ] +# Sometimes, OAuth2 clients want the user to redirect back to their site after logout. But to determine if the given +# redirect URL/path is safe for redirection, the following variable is used by edX. +LOGIN_REDIRECT_WHITELIST = ENV_TOKENS.get( + 'LOGIN_REDIRECT_WHITELIST', + LOGIN_REDIRECT_WHITELIST +) + +# 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')) + # This is the domain that is used to set shared cookies between various sub-domains. # By default, it's set to the same thing as the SESSION_COOKIE_DOMAIN, but we want to make it overrideable. -SHARED_COOKIE_DOMAIN = _YAML_TOKENS.get('SHARED_kCOOKIE_DOMAIN', SESSION_COOKIE_DOMAIN) +SHARED_COOKIE_DOMAIN = ENV_TOKENS.get('SHARED_COOKIE_DOMAIN', SESSION_COOKIE_DOMAIN) +CACHES = ENV_TOKENS.get('CACHES', CACHES) # Cache used for location mapping -- called many times with the same key/value # in a given request. if 'loc_cache' not in CACHES: @@ -236,76 +224,206 @@ def get_env_setting(setting): # 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 = _YAML_TOKENS.get('BULK_EMAIL_ROUTING_KEY', HIGH_PRIORITY_QUEUE) +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 = _YAML_TOKENS.get('BULK_EMAIL_ROUTING_KEY_SMALL_JOBS', DEFAULT_PRIORITY_QUEUE) +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 = _YAML_TOKENS.get('ENTITLEMENTS_EXPIRATION_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE) +ENTITLEMENTS_EXPIRATION_ROUTING_KEY = ENV_TOKENS.get('ENTITLEMENTS_EXPIRATION_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE) -# Build a CELERY_QUEUES dict the way that celery expects, based on a couple lists of queue names from the YAML. -_YAML_CELERY_QUEUES = _YAML_TOKENS.get('CELERY_QUEUES', None) -if _YAML_CELERY_QUEUES: - CELERY_QUEUES = {queue: {} for queue in _YAML_CELERY_QUEUES} +# 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 -_YAML_ALTERNATE_WORKER_QUEUES = _YAML_TOKENS.get('ALTERNATE_WORKER_QUEUES', '').split() +ALTERNATE_QUEUE_ENVS = ENV_TOKENS.get('ALTERNATE_WORKER_QUEUES', '').split() ALTERNATE_QUEUES = [ DEFAULT_PRIORITY_QUEUE.replace(QUEUE_VARIANT, alternate + '.') - for alternate in _YAML_ALTERNATE_WORKER_QUEUES + for alternate in ALTERNATE_QUEUE_ENVS ] - CELERY_QUEUES.update( { alternate: {} for alternate in ALTERNATE_QUEUES - if alternate not in CELERY_QUEUES.keys() + if alternate not in list(CELERY_QUEUES.keys()) } ) -MKTG_URL_LINK_MAP.update(_YAML_TOKENS.get('MKTG_URL_LINK_MAP', {})) +# 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', []) + + +# PREPEND_LOCALE_PATHS contain the paths to locale directories to load first e.g. +# "PREPEND_LOCALE_PATHS" : [ +# "/edx/my-locale" +# ], +PREPEND_LOCALE_PATHS = ENV_TOKENS.get('PREPEND_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 = _YAML_TOKENS.get('ID_VERIFICATION_SUPPORT_LINK', SUPPORT_SITE_LINK) -PASSWORD_RESET_SUPPORT_LINK = _YAML_TOKENS.get('PASSWORD_RESET_SUPPORT_LINK', SUPPORT_SITE_LINK) -ACTIVATION_EMAIL_SUPPORT_LINK = _YAML_TOKENS.get('ACTIVATION_EMAIL_SUPPORT_LINK', SUPPORT_SITE_LINK) -LOGIN_ISSUE_SUPPORT_LINK = _YAML_TOKENS.get('LOGIN_ISSUE_SUPPORT_LINK', SUPPORT_SITE_LINK) +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) +LOGIN_ISSUE_SUPPORT_LINK = ENV_TOKENS.get('LOGIN_ISSUE_SUPPORT_LINK', SUPPORT_SITE_LINK) # Timezone overrides -TIME_ZONE = CELERY_TIMEZONE +TIME_ZONE = ENV_TOKENS.get('CELERY_TIMEZONE', CELERY_TIMEZONE) # Translation overrides LANGUAGE_DICT = dict(LANGUAGES) -LANGUAGE_COOKIE_NAME = _YAML_TOKENS.get('LANGUAGE_COOKIE') or LANGUAGE_COOKIE_NAME +LANGUAGE_COOKIE_NAME = ENV_TOKENS.get('LANGUAGE_COOKIE', None) or ENV_TOKENS.get( + 'LANGUAGE_COOKIE_NAME', LANGUAGE_COOKIE_NAME) # Additional installed apps -for app in _YAML_TOKENS.get('ADDL_INSTALLED_APPS', []): +for app in ENV_TOKENS.get('ADDL_INSTALLED_APPS', []): INSTALLED_APPS.append(app) -LOGGING = get_logger_config( - LOG_DIR, - logging_env=LOGGING_ENV, - local_loglevel=LOCAL_LOGLEVEL, - service_variant=SERVICE_VARIANT, + +local_loglevel = ENV_TOKENS.get('LOCAL_LOGLEVEL', 'INFO') +LOG_DIR = ENV_TOKENS.get('LOG_DIR', LOG_DIR) + +LOGGING = get_logger_config(LOG_DIR, + logging_env=ENV_TOKENS.get('LOGGING_ENV', 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') + +# Python lib settings +PYTHON_LIB_FILENAME = ENV_TOKENS.get('PYTHON_LIB_FILENAME', 'python_lib.zip') + +# Code jail settings +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", + "/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 environments 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 = _YAML_TOKENS.get('CSRF_TRUSTED_ORIGINS_WITH_SCHEME', []) +CSRF_TRUSTED_ORIGINS = ENV_TOKENS.get('CSRF_TRUSTED_ORIGINS', []) +# values are already updated above with default CSRF_TRUSTED_ORIGINS values but in +# case of new django version these values will override. +if django.VERSION[0] >= 4: # for greater than django 3.2 use schemes. + CSRF_TRUSTED_ORIGINS = ENV_TOKENS.get('CSRF_TRUSTED_ORIGINS_WITH_SCHEME', []) -if FEATURES['ENABLE_CORS_HEADERS'] or FEATURES.get('ENABLE_CROSS_DOMAIN_CSRF_COOKIE'): - CORS_ALLOW_CREDENTIALS = True - CORS_ORIGIN_WHITELIST = _YAML_TOKENS.get('CORS_ORIGIN_WHITELIST', ()) - CORS_ORIGIN_ALLOW_ALL = _YAML_TOKENS.get('CORS_ORIGIN_ALLOW_ALL', False) - CORS_ALLOW_INSECURE = _YAML_TOKENS.get('CORS_ALLOW_INSECURE', False) - CROSS_DOMAIN_CSRF_COOKIE_DOMAIN = _YAML_TOKENS.get('CROSS_DOMAIN_CSRF_COOKIE_DOMAIN') +############# 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) + + # 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] @@ -314,11 +432,26 @@ def get_env_setting(setting): 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 = _YAML_TOKENS.get('SEGMENT_KEY') +LMS_SEGMENT_KEY = AUTH_TOKENS.get('SEGMENT_KEY') + +SECRET_KEY = AUTH_TOKENS['SECRET_KEY'] +AWS_ACCESS_KEY_ID = AUTH_TOKENS.get("AWS_ACCESS_KEY_ID", AWS_ACCESS_KEY_ID) if AWS_ACCESS_KEY_ID == "": AWS_ACCESS_KEY_ID = None + +AWS_SECRET_ACCESS_KEY = AUTH_TOKENS.get("AWS_SECRET_ACCESS_KEY", AWS_SECRET_ACCESS_KEY) if AWS_SECRET_ACCESS_KEY == "": AWS_SECRET_ACCESS_KEY = None @@ -327,10 +460,24 @@ def get_env_setting(setting): # same with upcoming version setting it to `public-read`. AWS_DEFAULT_ACL = 'public-read' AWS_BUCKET_ACL = AWS_DEFAULT_ACL +AWS_STORAGE_BUCKET_NAME = AUTH_TOKENS.get('AWS_STORAGE_BUCKET_NAME', 'edxuploads') -# Change to S3Boto3 if we haven't specified another default storage AND we have specified AWS creds. -if (not _YAML_TOKENS.get('DEFAULT_FILE_STORAGE')) and AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY: +# 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.s3boto3.S3Boto3Storage' +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.get('DATABASES', DATABASES) # The normal database user does not have enough permissions to run migrations. # Migrations are run with separate credentials, given as DB_MIGRATION_* @@ -346,9 +493,11 @@ def get_env_setting(setting): 'PORT': os.environ.get('DB_MIGRATION_PORT', database['PORT']), }) +XQUEUE_INTERFACE = AUTH_TOKENS.get('XQUEUE_INTERFACE', 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(_YAML_TOKENS.get('MODULESTORE', MODULESTORE)) +MODULESTORE = convert_module_store_setting_if_needed(AUTH_TOKENS.get('MODULESTORE', MODULESTORE)) # After conversion above, the modulestore will have a "stores" list with all defined stores, for all stores, add the # fs_root entry to derived collection so that if it's a callable it can be resolved. We need to do this because the @@ -361,34 +510,137 @@ def get_env_setting(setting): if 'OPTIONS' in store and 'fs_root' in store["OPTIONS"]: derived_collection_entry('MODULESTORE', 'default', 'OPTIONS', 'stores', idx, 'OPTIONS', 'fs_root') +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 '' + +# 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 = "{}://{}:{}@{}/{}".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) + try: BROKER_TRANSPORT_OPTIONS = { 'fanout_patterns': True, 'fanout_prefix': True, - **_YAML_TOKENS.get('CELERY_BROKER_TRANSPORT_OPTIONS', {}) + **ENV_TOKENS.get('CELERY_BROKER_TRANSPORT_OPTIONS', {}) } except TypeError as exc: raise ImproperlyConfigured('CELERY_BROKER_TRANSPORT_OPTIONS must be a dict') from exc +# Block Structures + +# upload limits +STUDENT_FILEUPLOAD_MAX_SIZE = ENV_TOKENS.get("STUDENT_FILEUPLOAD_MAX_SIZE", STUDENT_FILEUPLOAD_MAX_SIZE) + # Event tracking -TRACKING_BACKENDS.update(_YAML_TOKENS.get("TRACKING_BACKENDS", {})) -EVENT_TRACKING_BACKENDS['tracking_logs']['OPTIONS']['backends'].update( - _YAML_TOKENS.get("EVENT_TRACKING_BACKENDS", {}) -) +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( - EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST + 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) + +# Heartbeat +HEARTBEAT_CELERY_ROUTING_KEY = ENV_TOKENS.get('HEARTBEAT_CELERY_ROUTING_KEY', HEARTBEAT_CELERY_ROUTING_KEY) + +# 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 = _YAML_TOKENS.get('GRADES_DOWNLOAD_ROUTING_KEY', HIGH_MEM_QUEUE) +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) +LOGISTRATION_API_RATELIMIT = ENV_TOKENS.get('LOGISTRATION_API_RATELIMIT', LOGISTRATION_API_RATELIMIT) +LOGIN_AND_REGISTER_FORM_RATELIMIT = ENV_TOKENS.get( + 'LOGIN_AND_REGISTER_FORM_RATELIMIT', LOGIN_AND_REGISTER_FORM_RATELIMIT +) +RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT = ENV_TOKENS.get( + 'RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT', RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT +) +RESET_PASSWORD_API_RATELIMIT = ENV_TOKENS.get('RESET_PASSWORD_API_RATELIMIT', RESET_PASSWORD_API_RATELIMIT) + +##### REGISTRATION RATE LIMIT SETTINGS ##### +REGISTRATION_VALIDATION_RATELIMIT = ENV_TOKENS.get( + 'REGISTRATION_VALIDATION_RATELIMIT', REGISTRATION_VALIDATION_RATELIMIT +) + +REGISTRATION_RATELIMIT = ENV_TOKENS.get('REGISTRATION_RATELIMIT', REGISTRATION_RATELIMIT) + +#### 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) + +#### PROCTORED EXAM SETTINGS #### +PROCTORED_EXAM_VIEWABLE_PAST_DUE = ENV_TOKENS.get('PROCTORED_EXAM_VIEWABLE_PAST_DUE', False) + +##### Third-party auth options ################################################ +ENABLE_REQUIRE_THIRD_PARTY_AUTH = ENV_TOKENS.get('ENABLE_REQUIRE_THIRD_PARTY_AUTH', False) if FEATURES.get('ENABLE_THIRD_PARTY_AUTH'): - AUTHENTICATION_BACKENDS = _YAML_TOKENS.get('THIRD_PARTY_AUTH_BACKENDS', [ + tmp_backends = ENV_TOKENS.get('THIRD_PARTY_AUTH_BACKENDS', [ 'social_core.backends.google.GoogleOAuth2', 'social_core.backends.linkedin.LinkedinOAuth2', 'social_core.backends.facebook.FacebookOAuth2', @@ -397,66 +649,136 @@ def get_env_setting(setting): 'common.djangoapps.third_party_auth.identityserver3.IdentityServer3', 'common.djangoapps.third_party_auth.saml.SAMLAuthBackend', 'common.djangoapps.third_party_auth.lti.LTIAuthBackend', - ]) + list(AUTHENTICATION_BACKENDS) + ]) - # The reduced session expiry time during the third party login pipeline. (Value in seconds) - SOCIAL_AUTH_PIPELINE_TIMEOUT = _YAML_TOKENS.get('SOCIAL_AUTH_PIPELINE_TIMEOUT', 600) + AUTHENTICATION_BACKENDS = list(tmp_backends) + list(AUTHENTICATION_BACKENDS) + del tmp_backends - # TODO: Would it be safe to just set this default in common.py, even if ENABLE_THIRD_PARTY_AUTH is False? - SOCIAL_AUTH_LTI_CONSUMER_SECRETS = _YAML_TOKENS.get('SOCIAL_AUTH_LTI_CONSUMER_SECRETS', {}) + # 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 = _YAML_TOKENS.get('THIRD_PARTY_AUTH', None) + THIRD_PARTY_AUTH_OLD_CONFIG = AUTH_TOKENS.get('THIRD_PARTY_AUTH', None) - # TODO: This logic is somewhat insane. We're not sure if it's intentional or not. We've left it - # as-is for strict backwards compatibility, but it's worth revisiting. - if hours := _YAML_TOKENS.get('THIRD_PARTY_AUTH_SAML_FETCH_PERIOD_HOURS', 24): - # If we didn't override the value in YAML, OR we overrode it to a truthy value, - # then update CELERYBEAT_SCHEDULE. + if ENV_TOKENS.get('THIRD_PARTY_AUTH_SAML_FETCH_PERIOD_HOURS', 24) is not None: CELERYBEAT_SCHEDULE['refresh-saml-metadata'] = { 'task': 'common.djangoapps.third_party_auth.fetch_saml_metadata', - 'schedule': datetime.timedelta(hours=hours), + '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 = _YAML_TOKENS.get('THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS', {}) + THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS = AUTH_TOKENS.get('THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS', {}) ##### OAUTH2 Provider ############## -if FEATURES['ENABLE_OAUTH2_PROVIDER']: - OAUTH_ENFORCE_SECURE = True - OAUTH_ENFORCE_CLIENT_SECURE = True +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=OAUTH_EXPIRE_CONFIDENTIAL_CLIENT_DAYS) - OAUTH_EXPIRE_DELTA_PUBLIC = datetime.timedelta(days=OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS) - -if ( - FEATURES['ENABLE_COURSEWARE_SEARCH'] or - FEATURES['ENABLE_DASHBOARD_SEARCH'] or - FEATURES['ENABLE_COURSE_DISCOVERY'] or - FEATURES['ENABLE_TEAMS'] - ): + 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') +GOOGLE_ANALYTICS_4_ID = AUTH_TOKENS.get('GOOGLE_ANALYTICS_4_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) + +# Ecommerce Orders API Cache Timeout +ECOMMERCE_ORDERS_API_CACHE_TIMEOUT = ENV_TOKENS.get('ECOMMERCE_ORDERS_API_CACHE_TIMEOUT', 3600) + +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) + +SEARCH_SKIP_INVITATION_ONLY_FILTERING = ENV_TOKENS.get( + 'SEARCH_SKIP_INVITATION_ONLY_FILTERING', + SEARCH_SKIP_INVITATION_ONLY_FILTERING, +) +SEARCH_SKIP_SHOW_IN_CATALOG_FILTERING = ENV_TOKENS.get( + 'SEARCH_SKIP_SHOW_IN_CATALOG_FILTERING', + SEARCH_SKIP_SHOW_IN_CATALOG_FILTERING, +) + +SEARCH_COURSEWARE_CONTENT_LOG_PARAMS = ENV_TOKENS.get( + 'SEARCH_COURSEWARE_CONTENT_LOG_PARAMS', + SEARCH_COURSEWARE_CONTENT_LOG_PARAMS, +) # TODO: Once we have successfully upgraded to ES7, switch this back to ELASTIC_SEARCH_CONFIG. -ELASTIC_SEARCH_CONFIG = _YAML_TOKENS.get('ELASTIC_SEARCH_CONFIG_ES7', [{}]) +ELASTIC_SEARCH_CONFIG = ENV_TOKENS.get('ELASTIC_SEARCH_CONFIG_ES7', [{}]) + +# 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.setdefault("VideoBlock", {})["licensing_enabled"] = FEATURES["LICENSING"] -XBLOCK_SETTINGS.setdefault("VideoBlock", {})['YOUTUBE_API_KEY'] = YOUTUBE_API_KEY +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['CUSTOM_COURSES_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', ) -FIELD_OVERRIDE_PROVIDERS = tuple(FIELD_OVERRIDE_PROVIDERS) - ##### Individual Due Date Extensions ##### -if FEATURES['INDIVIDUAL_DUE_DATES']: +if FEATURES.get('INDIVIDUAL_DUE_DATES'): FIELD_OVERRIDE_PROVIDERS += ( 'lms.djangoapps.courseware.student_field_overrides.IndividualStudentOverrideProvider', ) @@ -477,30 +799,52 @@ def get_env_setting(setting): # 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['ENABLE_LTI_PROVIDER']: +if FEATURES.get('ENABLE_LTI_PROVIDER'): INSTALLED_APPS.append('lms.djangoapps.lti_provider.apps.LtiProviderConfig') AUTHENTICATION_BACKENDS.append('lms.djangoapps.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(_YAML_TOKENS.get('JWT_AUTH', {})) +JWT_AUTH.update(ENV_TOKENS.get('JWT_AUTH', {})) +JWT_AUTH.update(AUTH_TOKENS.get('JWT_AUTH', {})) -################################ Settings for Credentials Service ################################ +# Offset for pk of courseware.StudentModuleHistoryExtended +STUDENTMODULEHISTORYEXTENDED_OFFSET = ENV_TOKENS.get( + 'STUDENTMODULEHISTORYEXTENDED_OFFSET', STUDENTMODULEHISTORYEXTENDED_OFFSET +) -CREDENTIALS_GENERATION_ROUTING_KEY = _YAML_TOKENS.get('CREDENTIALS_GENERATION_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE) +################################ Settings for Credentials Service ################################ -PROGRAM_CERTIFICATES_ROUTING_KEY = _YAML_TOKENS.get('PROGRAM_CERTIFICATES_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE) +CREDENTIALS_GENERATION_ROUTING_KEY = ENV_TOKENS.get('CREDENTIALS_GENERATION_ROUTING_KEY', DEFAULT_PRIORITY_QUEUE) -SOFTWARE_SECURE_VERIFICATION_ROUTING_KEY = _YAML_TOKENS.get( +# 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. @@ -508,17 +852,47 @@ def get_env_setting(setting): # 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 = _YAML_TOKENS.get( +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 = _YAML_TOKENS.get( +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 +) +INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT = ENV_TOKENS.get( + 'INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT', + INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT +) + ############## ENTERPRISE SERVICE API CLIENT CONFIGURATION ###################### # The LMS communicates with the Enterprise service via the requests.Session() client # The below environmental settings are utilized by the LMS when interacting with @@ -527,24 +901,97 @@ def get_env_setting(setting): 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 = _YAML_TOKENS.get('ENTERPRISE_API_URL', DEFAULT_ENTERPRISE_API_URL) +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 = _YAML_TOKENS.get('ENTERPRISE_CONSENT_API_URL', DEFAULT_ENTERPRISE_CONSENT_API_URL) +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 +) + +CHAT_COMPLETION_API = ENV_TOKENS.get('CHAT_COMPLETION_API', '') +CHAT_COMPLETION_API_KEY = ENV_TOKENS.get('CHAT_COMPLETION_API_KEY', '') +LEARNER_ENGAGEMENT_PROMPT_FOR_ACTIVE_CONTRACT = ENV_TOKENS.get('LEARNER_ENGAGEMENT_PROMPT_FOR_ACTIVE_CONTRACT', '') +LEARNER_ENGAGEMENT_PROMPT_FOR_NON_ACTIVE_CONTRACT = ENV_TOKENS.get( + 'LEARNER_ENGAGEMENT_PROMPT_FOR_NON_ACTIVE_CONTRACT', + '' +) +LEARNER_PROGRESS_PROMPT_FOR_ACTIVE_CONTRACT = ENV_TOKENS.get('LEARNER_PROGRESS_PROMPT_FOR_ACTIVE_CONTRACT', '') +LEARNER_PROGRESS_PROMPT_FOR_NON_ACTIVE_CONTRACT = ENV_TOKENS.get('LEARNER_PROGRESS_PROMPT_FOR_NON_ACTIVE_CONTRACT', '') ############## 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_EXCLUDED_REGISTRATION_FIELDS = set(ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS) +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', {}) + +# 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(_YAML_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', [])) +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) ########################## Derive Any Derived Settings ####################### @@ -554,15 +1001,23 @@ def get_env_setting(setting): # This is at the bottom because it is going to load more settings after base settings are loaded -# ENV_TOKENS and AUTH_TOKENS are included for reverse compatibility. -# Removing them may break plugins that rely on them. -# Please do not add new references to them... just use `django.conf.settings` instead. -ENV_TOKENS = __config__ -AUTH_TOKENS = __config__ - # Load production.py in plugins add_plugins(__name__, ProjectType.LMS, SettingsType.PRODUCTION) +############## 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_BY_VIEWING_DELAY_MS = ENV_TOKENS.get('COMPLETION_BY_VIEWING_DELAY_MS', + COMPLETION_BY_VIEWING_DELAY_MS) + +################# Settings for brand logos. ################# +LOGO_URL = ENV_TOKENS.get('LOGO_URL', LOGO_URL) +LOGO_URL_PNG = ENV_TOKENS.get('LOGO_URL_PNG', LOGO_URL_PNG) +LOGO_TRADEMARK_URL = ENV_TOKENS.get('LOGO_TRADEMARK_URL', LOGO_TRADEMARK_URL) +FAVICON_URL = ENV_TOKENS.get('FAVICON_URL', FAVICON_URL) ######################## CELERY ROUTING ######################## @@ -622,32 +1077,56 @@ def get_env_setting(setting): } +LOGO_IMAGE_EXTRA_TEXT = ENV_TOKENS.get('LOGO_IMAGE_EXTRA_TEXT', '') + ############## XBlock extra mixins ############################ XBLOCK_MIXINS += tuple(XBLOCK_EXTRA_MIXINS) +############## Settings for course import olx validation ############################ +COURSE_OLX_VALIDATION_STAGE = ENV_TOKENS.get('COURSE_OLX_VALIDATION_STAGE', COURSE_OLX_VALIDATION_STAGE) +COURSE_OLX_VALIDATION_IGNORE_LIST = ENV_TOKENS.get( + 'COURSE_OLX_VALIDATION_IGNORE_LIST', + COURSE_OLX_VALIDATION_IGNORE_LIST +) + +################# show account activate cta after register ######################## +SHOW_ACCOUNT_ACTIVATION_CTA = ENV_TOKENS.get('SHOW_ACCOUNT_ACTIVATION_CTA', SHOW_ACCOUNT_ACTIVATION_CTA) + +################# Discussions micro frontend URL ######################## +DISCUSSIONS_MICROFRONTEND_URL = ENV_TOKENS.get('DISCUSSIONS_MICROFRONTEND_URL', DISCUSSIONS_MICROFRONTEND_URL) + +################### Discussions micro frontend Feedback URL################### +DISCUSSIONS_MFE_FEEDBACK_URL = ENV_TOKENS.get('DISCUSSIONS_MFE_FEEDBACK_URL', DISCUSSIONS_MFE_FEEDBACK_URL) + +############################ AI_TRANSLATIONS URL ################################## +AI_TRANSLATIONS_API_URL = ENV_TOKENS.get('AI_TRANSLATIONS_API_URL', AI_TRANSLATIONS_API_URL) + ############## DRF overrides ############## -REST_FRAMEWORK.update(_YAML_TOKENS.get('REST_FRAMEWORK', {})) +REST_FRAMEWORK.update(ENV_TOKENS.get('REST_FRAMEWORK', {})) ############################# CELERY ############################ -CELERY_IMPORTS.extend(_YAML_TOKENS.get('CELERY_EXTRA_IMPORTS', [])) +CELERY_IMPORTS.extend(ENV_TOKENS.get('CELERY_EXTRA_IMPORTS', [])) # keys for big blue button live provider -# TODO: This should not be in the core platform. If it has to stay for now, though, then we should move these -# defaults into common.py COURSE_LIVE_GLOBAL_CREDENTIALS["BIG_BLUE_BUTTON"] = { - "KEY": _YAML_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_KEY'), - "SECRET": _YAML_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_SECRET'), - "URL": _YAML_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_URL'), + "KEY": ENV_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_KEY', None), + "SECRET": ENV_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_SECRET', None), + "URL": ENV_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_URL', None), } -############## Event bus producer ############## -EVENT_BUS_PRODUCER_CONFIG = merge_producer_configs( - EVENT_BUS_PRODUCER_CONFIG, - _YAML_TOKENS.get('EVENT_BUS_PRODUCER_CONFIG', {}) -) +AVAILABLE_DISCUSSION_TOURS = ENV_TOKENS.get('AVAILABLE_DISCUSSION_TOURS', []) -##################################################################################################### -# HEY! Don't add anything to the end of this file. -# Add your defaults to common.py instead! -# If you really need to add post-YAML logic, add it above the "Derive Any Derived Settings" section. -###################################################################################################### +############## NOTIFICATIONS EXPIRY ############## +NOTIFICATIONS_EXPIRY = ENV_TOKENS.get('NOTIFICATIONS_EXPIRY', NOTIFICATIONS_EXPIRY) + +############## Event bus producer ############## +EVENT_BUS_PRODUCER_CONFIG = merge_producer_configs(EVENT_BUS_PRODUCER_CONFIG, + ENV_TOKENS.get('EVENT_BUS_PRODUCER_CONFIG', {})) +BEAMER_PRODUCT_ID = ENV_TOKENS.get('BEAMER_PRODUCT_ID', BEAMER_PRODUCT_ID) + +# .. setting_name: DISABLED_COUNTRIES +# .. setting_default: [] +# .. setting_description: List of country codes that should be disabled +# .. for now it wil impact country listing in auth flow and user profile. +# .. eg ['US', 'CA'] +DISABLED_COUNTRIES = ENV_TOKENS.get('DISABLED_COUNTRIES', [])