Ejemplo n.º 1
0
    def test_setting_overridden_by_setting_toggle(self):
        _toggle2 = SettingToggle("MYSETTING2", module_name="module1")
        _toggle3 = SettingDictToggle("MYDICT",
                                     "MYSETTING3",
                                     module_name="module1")
        with override_settings(MYSETTING1=True,
                               MYSETTING2=False,
                               MYDICT={"MYSETTING3": False}):
            # Need to pre-load settings, otherwise they are not picked up by the view
            self.assertTrue(settings.MYSETTING1)
            report = ToggleStateReport().as_dict()

        setting_dict = {
            toggle["name"]: toggle
            for toggle in report["django_settings"]
        }

        # Check that Django settings for which a SettingToggle exists have both the correct is_active and class values
        self.assertTrue(setting_dict["MYSETTING1"]["is_active"])
        self.assertNotIn("class", setting_dict["MYSETTING1"])
        self.assertFalse(setting_dict["MYSETTING2"]["is_active"])
        self.assertEqual("SettingToggle", setting_dict["MYSETTING2"]["class"])
        self.assertFalse(setting_dict["MYDICT['MYSETTING3']"]["is_active"])
        self.assertEqual("SettingDictToggle",
                         setting_dict["MYDICT['MYSETTING3']"]["class"])
def is_comprehensive_theming_enabled():
    """
    Returns boolean indicating whether comprehensive theming functionality is enabled or disabled.
    Example:
        >> is_comprehensive_theming_enabled()
        True

    Returns:
         (bool): True if comprehensive theming is enabled else False
    """
    ENABLE_COMPREHENSIVE_THEMING = SettingToggle("ENABLE_COMPREHENSIVE_THEMING", default=False)

    if ENABLE_COMPREHENSIVE_THEMING.is_enabled() and current_request_has_associated_site_theme():
        return True

    return ENABLE_COMPREHENSIVE_THEMING.is_enabled()
Ejemplo n.º 3
0
    def test_setting_overridden_by_setting_toggle(self):
        _toggle2 = SettingToggle("MYSETTING2", module_name="module1")
        _toggle3 = SettingDictToggle("MYDICT",
                                     "MYSETTING3",
                                     module_name="module1")
        with override_settings(MYSETTING1=True,
                               MYSETTING2=False,
                               MYDICT={"MYSETTING3": False}):
            # Need to pre-load settings, otherwise they are not picked up by the view
            assert settings.MYSETTING1
            response = self._get_toggle_state_response()

        setting_dict = {
            toggle["name"]: toggle
            for toggle in response.data["django_settings"]
        }

        # Check that Django settings for which a SettingToggle exists have both the correct is_active and class values
        assert setting_dict['MYSETTING1']['is_active']
        assert 'class' not in setting_dict['MYSETTING1']
        assert not setting_dict['MYSETTING2']['is_active']
        assert 'SettingToggle' == setting_dict['MYSETTING2']['class']
        assert not setting_dict["MYDICT['MYSETTING3']"]['is_active']
        assert 'SettingDictToggle' == setting_dict["MYDICT['MYSETTING3']"][
            'class']
Ejemplo n.º 4
0
 def _add_setting_toggles(self, settings_dict):
     """
     Fill the `settings_dict` with values from the list of SettingToggle instances.
     """
     for toggle in SettingToggle.get_instances():
         toggle_response = self._get_or_create_toggle_response(settings_dict, toggle.name)
         toggle_response["is_active"] = toggle.is_enabled()
         self._add_toggle_instance_details(toggle_response, toggle)
Ejemplo n.º 5
0
    def test_response_with_setting_toggle(self):
        _toggle = SettingToggle("MYSETTING",
                                default=False,
                                module_name="module1")
        with override_settings(MYSETTING=True):
            response = self._get_toggle_state_response()

        assert {'name': 'MYSETTING', 'is_active': True, 'module': 'module1', 'class': 'SettingToggle'}\
               in response.data['django_settings']
Ejemplo n.º 6
0
    def test_response_with_setting_toggle(self):
        _toggle = SettingToggle("MYSETTING", default=False, module_name="module1")
        with override_settings(MYSETTING=True):
            response = self._get_toggle_state_response()

        self.assertIn(
            {
                "name": "MYSETTING",
                "is_active": True,
                "module": "module1",
                "class": "SettingToggle",
            },
            response.data["django_settings"],
        )
Ejemplo n.º 7
0
    def test_response_with_setting_toggle(self):
        _toggle = SettingToggle("MYSETTING",
                                default=False,
                                module_name="module1")
        with override_settings(MYSETTING=True):
            report = ToggleStateReport().as_dict()

        self.assertIn(
            {
                "name": "MYSETTING",
                "is_active": True,
                "module": "module1",
                "class": "SettingToggle",
            },
            report["django_settings"],
        )
Ejemplo n.º 8
0
    def test_no_duplicate_setting_toggle(self):
        _toggle1 = SettingToggle(
            "MYSETTING1", module_name="module1"
        )
        _toggle2 = SettingDictToggle(
            "MYDICT", "MYSETTING2", module_name="module1"
        )
        with override_settings(MYSETTING1=True, MYDICT={"MYSETTING2": False}):
            response = self._get_toggle_state_response()

        # Check there are no duplicate setting/toggle
        response_toggles_1 = [toggle for toggle in response.data["django_settings"] if toggle["name"] == "MYSETTING1"]
        response_toggles_2 = [
            toggle for toggle in response.data["django_settings"] if toggle["name"] == "MYDICT['MYSETTING2']"
        ]
        self.assertEqual(1, len(response_toggles_1))
        self.assertEqual(1, len(response_toggles_2))
Ejemplo n.º 9
0
"""
xAPI processors and spec implementation.
"""

from edx_toggles.toggles import SettingToggle

# .. toggle_name: XAPI_EVENTS_ENABLED
# .. toggle_implementation: SettingToggle
# .. toggle_default: True
# .. toggle_description: Allow sending events to external servers via xAPI.
#   Toggle intended both for gating initial release and for shutting off all
#   xAPI events in case of emergency.
# .. toggle_warnings: Do not enable sending of xAPI events until there has
#   been a thorough review of PII implications and safeguards put in place to
#   prevent accidental leakage of novel event fields to third parties. See
#   ARCHBOM-1655 for details.
# .. toggle_use_cases: circuit_breaker
# .. toggle_creation_date: 2021-01-01
# .. toggle_tickets: https://openedx.atlassian.net/browse/ARCHBOM-1658
XAPI_EVENTS_ENABLED = SettingToggle("XAPI_EVENTS_ENABLED", default=False)
Ejemplo n.º 10
0
# .. toggle_name: LOG_REQUEST_USER_CHANGE_HEADERS
# .. toggle_implementation: SettingToggle
# .. toggle_default: False
# .. toggle_description: Turn this toggle on to log all request headers, for all requests, for all user ids involved in
#      any user id change detected by safe sessions. The headers will provide additional debugging information. The
#      headers will be logged for all requests up until LOG_REQUEST_USER_CHANGE_HEADERS_DURATION seconds after
#      the time of the last mismatch. The header details will be encrypted, and only available with the private key.
# .. toggle_warnings: To work correctly, LOG_REQUEST_USER_CHANGES must be enabled and ENFORCE_SAFE_SESSIONS must be
#      disabled. Also, SAFE_SESSIONS_DEBUG_PUBLIC_KEY must be set. See
#      https://github.com/edx/edx-platform/blob/master/common/djangoapps/util/log_sensitive.py
#      for instructions.
# .. toggle_use_cases: opt_in
# .. toggle_creation_date: 2021-12-22
# .. toggle_tickets: https://openedx.atlassian.net/browse/ARCHBOM-1940
LOG_REQUEST_USER_CHANGE_HEADERS = SettingToggle(
    'LOG_REQUEST_USER_CHANGE_HEADERS', default=False)

# Duration in seconds to log user change request headers for additional requests; defaults to 5 minutes
LOG_REQUEST_USER_CHANGE_HEADERS_DURATION = getattr(
    settings, 'LOG_REQUEST_USER_CHANGE_HEADERS_DURATION', 300)

# .. toggle_name: ENFORCE_SAFE_SESSIONS
# .. toggle_implementation: SettingToggle
# .. toggle_default: True
# .. toggle_description: Invalidate session and response if mismatch detected.
#   That is, when the `user` attribute of the request object gets changed or
#   no longer matches the session, the session will be invalidated and the
#   response cancelled (changed to an error). This is intended as a backup
#   safety measure in case an attacker (or bug) is able to change the user
#   on a session in an unexpected way.
# .. toggle_warnings: Should be disabled if debugging mismatches using the
Ejemplo n.º 11
0
from requests.exceptions import RequestException, HTTPError
from simplejson import JSONDecodeError

from .exceptions import CodejailServiceParseError, CodejailServiceStatusError, CodejailServiceUnavailable

log = logging.getLogger(__name__)

# .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE
# .. toggle_implementation: SettingToggle
# .. toggle_default: False
# .. toggle_description: Set this to True if you want to run Codejail code using
#   a separate VM or container and communicate with edx-platform using REST API.
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2021-08-19
ENABLE_CODEJAIL_REST_SERVICE = SettingToggle("ENABLE_CODEJAIL_REST_SERVICE",
                                             default=False,
                                             module_name=__name__)


def is_codejail_rest_service_enabled():
    return ENABLE_CODEJAIL_REST_SERVICE.is_enabled()


def get_remote_exec(*args, **kwargs):
    """Get remote exec function based on setting and executes it."""
    remote_exec_function_name = settings.CODE_JAIL_REST_SERVICE_REMOTE_EXEC
    try:
        mod_name, func_name = remote_exec_function_name.rsplit('.', 1)
        remote_exec_module = import_module(mod_name)
        remote_exec_function = getattr(remote_exec_module, func_name)
        if not remote_exec_function:
Ejemplo n.º 12
0
"""
This module contains various configuration toggles
for edx's analytics dashboard.
"""

from edx_toggles.toggles import SettingToggle

# .. toggle_name: ENROLLMENT_AGE_AVAILABLE
# .. toggle_implementation: SettingToggle
# .. toggle_default: True
# .. toggle_description: Turn this toggle on to show age demographic information in the analytics dashboard.
#      Turn it off if your installation cannot or does not collect or share age information.
# .. toggle_use_cases: opt_out
# .. toggle_creation_date: 2021-01-12
# .. toggle_tickets: https://openedx.atlassian.net/browse/MST-1241
ENROLLMENT_AGE_AVAILABLE = SettingToggle('ENROLLMENT_AGE_AVAILABLE',
                                         default=True)


def age_available():
    return ENROLLMENT_AGE_AVAILABLE.is_enabled()
Ejemplo n.º 13
0
def check_comprehensive_theme_settings(app_configs, **kwargs):  # lint-amnesty, pylint: disable=unused-argument
    """
    Checks the comprehensive theming theme directory settings.

    Raises compatibility Errors upon:
        - COMPREHENSIVE_THEME_DIRS is not a list
        - theme dir path is not a string
        - theme dir path is not an absolute path
        - path specified in COMPREHENSIVE_THEME_DIRS does not exist

    Returns:
        List of any Errors.
    """
    if not SettingToggle("ENABLE_COMPREHENSIVE_THEMING",
                         default=False).is_enabled():
        # Only perform checks when comprehensive theming is enabled.
        return []

    errors = []

    # COMPREHENSIVE_THEME_DIR is no longer supported - support has been removed.
    if hasattr(settings, "COMPREHENSIVE_THEME_DIR"):
        theme_dir = settings.COMPREHENSIVE_THEME_DIR

        errors.append(
            Error(
                "COMPREHENSIVE_THEME_DIR setting has been removed in favor of COMPREHENSIVE_THEME_DIRS.",
                hint=
                'Transfer the COMPREHENSIVE_THEME_DIR value to COMPREHENSIVE_THEME_DIRS.',
                obj=theme_dir,
                id='openedx.core.djangoapps.theming.E001',
            ))

    if hasattr(settings, "COMPREHENSIVE_THEME_DIRS"):
        theme_dirs = settings.COMPREHENSIVE_THEME_DIRS

        if not isinstance(theme_dirs, list):
            errors.append(
                Error(
                    "COMPREHENSIVE_THEME_DIRS must be a list.",
                    obj=theme_dirs,
                    id='openedx.core.djangoapps.theming.E004',
                ))
        if not all([
                isinstance(theme_dir, six.string_types)
                for theme_dir in theme_dirs
        ]):
            errors.append(
                Error(
                    "COMPREHENSIVE_THEME_DIRS must contain only strings.",
                    obj=theme_dirs,
                    id='openedx.core.djangoapps.theming.E005',
                ))
        if not all([theme_dir.startswith("/") for theme_dir in theme_dirs]):
            errors.append(
                Error(
                    "COMPREHENSIVE_THEME_DIRS must contain only absolute paths to themes dirs.",
                    obj=theme_dirs,
                    id='openedx.core.djangoapps.theming.E006',
                ))
        if not all([os.path.isdir(theme_dir) for theme_dir in theme_dirs]):
            errors.append(
                Error(
                    "COMPREHENSIVE_THEME_DIRS must contain valid paths.",
                    obj=theme_dirs,
                    id='openedx.core.djangoapps.theming.E007',
                ))

    return errors
Ejemplo n.º 14
0
from django.conf import settings
from edx_toggles.toggles import SettingToggle

from .event_bus_utils import create_topic_if_not_exists

logger = logging.getLogger(__name__)

# .. toggle_name: KAFKA_ENABLED
# .. toggle_implementation: SettingToggle
# .. toggle_default: False
# .. toggle_description: Enable producing events to the Kafka event bus
# .. toggle_creation_date: 2022-01-12
# .. toggle_target_removal_date: 2022-03-31
# .. toggle_tickets: https://openedx.atlassian.net/browse/ARCHBOM-1991
# .. toggle_use_cases: temporary
KAFKA_ENABLED = SettingToggle("KAFKA_ENABLED", default=False)


class SubscriptionsConfig(AppConfig):
    """
    The app config for subscriptions.
    """
    name = 'license_manager.apps.subscriptions'
    default = False

    def ready(self):
        if getattr(settings, 'SEGMENT_KEY', None):
            logger.debug("Found segment key, setting up")
            analytics.write_key = settings.SEGMENT_KEY

        # TODO: (ARCHBOM-2004) remove pragma and add tests when finalizing
Ejemplo n.º 15
0
def is_email_use_default_from_bulk_enabled():
    return SettingToggle("EMAIL_USE_DEFAULT_FROM_FOR_BULK",
                         default=False).is_enabled()
Ejemplo n.º 16
0
COURSEWARE_OPTIMIZED_RENDER_XBLOCK = CourseWaffleFlag(
    WAFFLE_FLAG_NAMESPACE, 'optimized_render_xblock', __name__
)

# .. toggle_name: COURSES_INVITE_ONLY
# .. toggle_implementation: SettingToggle
# .. toggle_type: feature_flag
# .. toggle_default: False
# .. toggle_description: Setting this sets the default value of INVITE_ONLY across all courses in a given deployment
# .. toggle_category: admin
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2019-05-16
# .. toggle_expiration_date: None
# .. toggle_tickets: https://github.com/mitodl/edx-platform/issues/123
# .. toggle_status: unsupported
COURSES_INVITE_ONLY = SettingToggle('COURSES_INVITE_ONLY', default=False)


def courseware_mfe_is_active(course_key: CourseKey) -> bool:
    """
    Should we serve the Learning MFE as the canonical courseware experience?
    """
    #Avoid circular imports.
    from lms.djangoapps.courseware.access_utils import in_preview_mode
    # NO: Old Mongo courses are always served in the Legacy frontend,
    #     regardless of configuration.
    if course_key.deprecated:
        return False
    # NO: MFE courseware can be disabled for users/courses/globally via this
    #     Waffle flag.
    if COURSEWARE_USE_LEGACY_FRONTEND.is_enabled(course_key):
Ejemplo n.º 17
0
def is_email_use_course_id_from_for_bulk_enabled():
    return SettingToggle("EMAIL_USE_COURSE_ID_FROM_FOR_BULK",
                         default=False).is_enabled()
Ejemplo n.º 18
0

# .. toggle_name: GLOBAL_NOTICE_ENABLED
# .. toggle_implementation: SettingToggle
# .. toggle_default: False
# .. toggle_description: When enabled, show the contents of GLOBAL_NOTICE_MESSAGE
#   as a message on every page. This is intended to be used as a way of
#   communicating an upcoming or currently active maintenance window or to
#   warn of known site issues. HTML is not supported for the message content,
#   only plaintext. Message styling can be controlled with GLOBAL_NOTICE_TYPE,
#   set to one of INFO, SUCCESS, WARNING, or ERROR (defaulting to INFO). Also
#   see openedx.core.djangoapps.util.maintenance_banner.add_maintenance_banner
#   for a variation that only shows a message on specific views.
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2021-09-08
GLOBAL_NOTICE_ENABLED = SettingToggle('GLOBAL_NOTICE_ENABLED', default=False)


class PageLevelMessages(UserMessageCollection):
    """
    This set of messages appears as top page level messages.
    """
    NAMESPACE = 'page_level_messages'

    @classmethod
    def user_messages(cls, request):
        """
        Returns outstanding user messages, along with any persistent site-wide messages.
        """
        msgs = list(super().user_messages(request))
Ejemplo n.º 19
0
            set_custom_attribute(name_attribute, name)
            set_custom_attribute(size_attribute, size)
            log.debug('%s = %d', name, size)


# .. toggle_name: ENABLE_403_MONITORING
# .. toggle_implementation: SettingToggle
# .. toggle_default: False
# .. toggle_description: Temporary toggle to track down the source of 403s for /oauth2/exchange_access_token/.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-02-12
# .. toggle_target_removal_date: 2021-03-12
# .. toggle_tickets: https://openedx.atlassian.net/browse/ARCHBOM-1667
ENABLE_403_MONITORING = SettingToggle('ENABLE_403_MONITORING',
                                      default=False,
                                      module_name=__name__)


def custom_exception_handler(exc, context):
    """ Enables monitoring of 403s for /oauth2/exchange_access_token/ to gather data. """
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    log_403s = ENABLE_403_MONITORING.is_enabled(
    ) and response.status_code == 403
    log_403s = log_403s and 'request' in context and context[
        'request'] and context['request'].path
    log_403s = log_403s and context['request'].path.startswith(
        '/oauth2/exchange_access_token/')
Ejemplo n.º 20
0
def is_courses_default_invite_only_enabled():
    return SettingToggle("COURSES_INVITE_ONLY", default=False).is_enabled()
Ejemplo n.º 21
0
def is_bulk_email_edx_ace_enabled():
    return SettingToggle("BULK_EMAIL_SEND_USING_EDX_ACE", default=False).is_enabled()