From a7f6e6a56b5775764d5c1eb331d0c9a48f695d22 Mon Sep 17 00:00:00 2001 From: Matt Drayer Date: Wed, 17 May 2017 22:38:25 -0400 Subject: [PATCH] Consider waffle switch when selecting ecommerce workflow --- cms/envs/common.py | 2 ++ lms/djangoapps/commerce/api/v1/permissions.py | 15 +++++++++++++++ lms/djangoapps/commerce/api/v1/views.py | 15 +++++++++++++-- lms/djangoapps/commerce/tests/test_utils.py | 9 +++++++++ lms/djangoapps/commerce/utils.py | 18 +++++++++++++++++- .../verify_student/tests/test_views.py | 3 +-- lms/djangoapps/verify_student/views.py | 11 +++-------- lms/envs/aws.py | 4 ++++ lms/envs/common.py | 1 + lms/envs/devstack.py | 1 + 10 files changed, 66 insertions(+), 13 deletions(-) diff --git a/cms/envs/common.py b/cms/envs/common.py index 8f47ae2cca..e8579c8f3a 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -96,6 +96,8 @@ from lms.envs.common import ( SUPPORT_SITE_LINK, CONTACT_EMAIL, + + DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH, ) from path import Path as path from warnings import simplefilter diff --git a/lms/djangoapps/commerce/api/v1/permissions.py b/lms/djangoapps/commerce/api/v1/permissions.py index 4c7679285a..629f5835dc 100644 --- a/lms/djangoapps/commerce/api/v1/permissions.py +++ b/lms/djangoapps/commerce/api/v1/permissions.py @@ -1,6 +1,10 @@ """ Custom API permissions. """ +from django.conf import settings +from django.contrib.auth.models import User from rest_framework.permissions import BasePermission, DjangoModelPermissions +from commerce.utils import is_account_activation_requirement_disabled +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.lib.api.permissions import ApiKeyHeaderPermission @@ -10,3 +14,14 @@ class ApiKeyOrModelPermission(BasePermission): def has_permission(self, request, view): return ApiKeyHeaderPermission().has_permission(request, view) or DjangoModelPermissions().has_permission( request, view) + + +class IsAuthenticatedOrActivationOverridden(BasePermission): + """ Considers the account activation override switch when determining the authentication status of the user """ + def has_permission(self, request, view): + if not request.user.is_authenticated() and is_account_activation_requirement_disabled(): + try: + request.user = User.objects.get(id=request.session._session_cache['_auth_user_id']) + except DoesNotExist: + pass + return request.user.is_authenticated() diff --git a/lms/djangoapps/commerce/api/v1/views.py b/lms/djangoapps/commerce/api/v1/views.py index fce30ca96c..1f2aa3a96c 100644 --- a/lms/djangoapps/commerce/api/v1/views.py +++ b/lms/djangoapps/commerce/api/v1/views.py @@ -1,6 +1,8 @@ """ API v1 views. """ import logging +from django.conf import settings +from django.contrib.auth.models import User from django.http import Http404 from edx_rest_api_client import exceptions from rest_framework.authentication import SessionAuthentication @@ -10,10 +12,12 @@ from rest_framework.permissions import IsAuthenticated from rest_framework_oauth.authentication import OAuth2Authentication from commerce.api.v1.models import Course -from commerce.api.v1.permissions import ApiKeyOrModelPermission +from commerce.api.v1.permissions import ApiKeyOrModelPermission, IsAuthenticatedOrActivationOverridden from commerce.api.v1.serializers import CourseSerializer +from commerce.utils import is_account_activation_requirement_disabled from course_modes.models import CourseMode from openedx.core.djangoapps.commerce.utils import ecommerce_api_client +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.lib.api.mixins import PutAsCreateMixin from util.json_request import JsonResponse @@ -64,10 +68,17 @@ class OrderView(APIView): """ Retrieve order details. """ authentication_classes = (SessionAuthentication,) - permission_classes = (IsAuthenticated,) + permission_classes = (IsAuthenticatedOrActivationOverridden,) def get(self, request, number): """ HTTP handler. """ + # If the account activation requirement is disabled for this installation, override the + # anonymous user object attached to the request with the actual user object (if it exists) + if not request.user.is_authenticated() and is_account_activation_requirement_disabled(): + try: + request.user = User.objects.get(id=request.session._session_cache['_auth_user_id']) + except DoesNotExist: + return JsonResponse(status=403) try: order = ecommerce_api_client(request.user).orders(number).get() return JsonResponse(order) diff --git a/lms/djangoapps/commerce/tests/test_utils.py b/lms/djangoapps/commerce/tests/test_utils.py index f2c646ae82..d8ded5b616 100644 --- a/lms/djangoapps/commerce/tests/test_utils.py +++ b/lms/djangoapps/commerce/tests/test_utils.py @@ -4,6 +4,7 @@ from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings from mock import patch +from waffle.testutils import override_switch from commerce.models import CommerceConfiguration from commerce.utils import EcommerceService @@ -55,6 +56,14 @@ class EcommerceServiceTests(TestCase): is_not_enabled = EcommerceService().is_enabled(self.user) self.assertFalse(is_not_enabled) + @override_switch(settings.DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH, active=True) + def test_is_enabled_activation_requirement_disabled(self): + """Verify that is_enabled() returns True when ecomm checkout is enabled. """ + self.user.is_active = False + self.user.save() + is_enabled = EcommerceService().is_enabled(self.user) + self.assertTrue(is_enabled) + @patch('openedx.core.djangoapps.theming.helpers.is_request_in_themed_site') def test_is_enabled_for_microsites(self, is_microsite): """Verify that is_enabled() returns True if used for a microsite.""" diff --git a/lms/djangoapps/commerce/utils.py b/lms/djangoapps/commerce/utils.py index 188ea4d659..f35181fbd0 100644 --- a/lms/djangoapps/commerce/utils.py +++ b/lms/djangoapps/commerce/utils.py @@ -2,11 +2,26 @@ from urlparse import urljoin from django.conf import settings +import waffle from commerce.models import CommerceConfiguration from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers +def is_account_activation_requirement_disabled(): + """ + Checks to see if the django-waffle switch for disabling the account activation requirement is active + + Returns: + Boolean value representing switch status + """ + switch_name = configuration_helpers.get_value( + 'DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH', + settings.DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH + ) + return waffle.switch_is_active(switch_name) + + class EcommerceService(object): """ Helper class for ecommerce service integration. """ def __init__(self): @@ -49,7 +64,8 @@ class EcommerceService(object): Returns: Boolean """ - allow_user = user.is_active or user.is_anonymous() + user_is_active = user.is_active or is_account_activation_requirement_disabled() + allow_user = user_is_active or user.is_anonymous() return allow_user and self.config.checkout_on_ecommerce_service def payment_page_url(self): diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 542cd3597b..03bed48f58 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -53,7 +53,6 @@ from lms.djangoapps.verify_student.models import ( ) from lms.djangoapps.verify_student.views import ( checkout_with_ecommerce_service, render_to_response, PayAndVerifyView, - DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH, ) @@ -676,7 +675,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): PayAndVerifyView.WEBCAM_REQ, ]) - @override_switch(DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH, active=True) + @override_switch(settings.DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH, active=True) @ddt.data("verify_student_start_flow", "verify_student_begin_flow") def test_disable_account_activation_requirement_flag_active(self, payment_flow): """ diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index c1351d28d4..c69c998d5f 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -29,7 +29,7 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey import waffle -from commerce.utils import EcommerceService +from commerce.utils import EcommerceService, is_account_activation_requirement_disabled from course_modes.models import CourseMode from edx_rest_api_client.exceptions import SlumberBaseException from edxmako.shortcuts import render_to_response, render_to_string @@ -57,8 +57,6 @@ from xmodule.modulestore.django import modulestore log = logging.getLogger(__name__) -DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH = 'verify_student_disable_account_activation_requirement' - class PayAndVerifyView(View): """ @@ -205,10 +203,7 @@ class PayAndVerifyView(View): Arguments: user (User): Current user involved in the onboarding/verification flow """ - user_is_active = user.is_active - if waffle.switch_is_active(DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH): - user_is_active = True - return user_is_active + return user.is_active or is_account_activation_requirement_disabled() @method_decorator(login_required) def get( @@ -614,7 +609,7 @@ class PayAndVerifyView(View): } # Remove the account activation requirement if disabled via waffle - if waffle.switch_is_active(DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH): + if is_account_activation_requirement_disabled(): all_requirements.pop(self.ACCOUNT_ACTIVATION_REQ) display_steps = set(step['name'] for step in display_steps) diff --git a/lms/envs/aws.py b/lms/envs/aws.py index 29b7584d54..684819de48 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -629,6 +629,10 @@ TRACKING_SEGMENTIO_SOURCE_MAP = ENV_TOKENS.get("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) diff --git a/lms/envs/common.py b/lms/envs/common.py index e8833077af..a67449c721 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2396,6 +2396,7 @@ VERIFY_STUDENT = { # The variable represents the window within which a verification is considered to be "expiring soon." "EXPIRING_SOON_WINDOW": 28, } +DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH = "verify_student_disable_account_activation_requirement" ### This enables the Metrics tab for the Instructor dashboard ########### FEATURES['CLASS_DASHBOARD'] = False diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py index 783dc9afc9..a7dcf6c429 100644 --- a/lms/envs/devstack.py +++ b/lms/envs/devstack.py @@ -199,6 +199,7 @@ VERIFY_STUDENT["SOFTWARE_SECURE"] = { "API_ACCESS_KEY": "BBBBBBBBBBBBBBBBBBBB", "API_SECRET_KEY": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", } +DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH = "verify_student_disable_account_activation_requirement" # Skip enrollment start date filtering SEARCH_SKIP_ENROLLMENT_START_DATE_FILTERING = True