Commit fd00d29d by ayesha-baig Committed by GitHub

Merge pull request #14452 from edx/ayeshabaig/YONK-513

[YONK-513]: Add feature flag which allows for disabling of account cr…
parents 881970a2 61f20679
...@@ -14,9 +14,9 @@ from django.contrib.auth.models import User ...@@ -14,9 +14,9 @@ from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from contentstore.models import PushNotificationConfig from contentstore.models import PushNotificationConfig
from contentstore.tests.test_course_settings import CourseTestCase
from contentstore.tests.utils import parse_json, user, registration, AjaxEnabledTestClient from contentstore.tests.utils import parse_json, user, registration, AjaxEnabledTestClient
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from contentstore.tests.test_course_settings import CourseTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
import datetime import datetime
from pytz import UTC from pytz import UTC
...@@ -302,6 +302,34 @@ class AuthTestCase(ContentStoreTestCase): ...@@ -302,6 +302,34 @@ class AuthTestCase(ContentStoreTestCase):
# re-request, and we should get a redirect to login page # re-request, and we should get a redirect to login page
self.assertRedirects(resp, settings.LOGIN_REDIRECT_URL + '?next=/home/') self.assertRedirects(resp, settings.LOGIN_REDIRECT_URL + '?next=/home/')
@mock.patch.dict(settings.FEATURES, {"ALLOW_PUBLIC_ACCOUNT_CREATION": False})
def test_signup_button_index_page(self):
"""
Navigate to the home page and check the Sign Up button is hidden when ALLOW_PUBLIC_ACCOUNT_CREATION flag
is turned off
"""
response = self.client.get(reverse('homepage'))
self.assertNotIn('<a class="action action-signup" href="/signup">Sign Up</a>', response.content)
@mock.patch.dict(settings.FEATURES, {"ALLOW_PUBLIC_ACCOUNT_CREATION": False})
def test_signup_button_login_page(self):
"""
Navigate to the login page and check the Sign Up button is hidden when ALLOW_PUBLIC_ACCOUNT_CREATION flag
is turned off
"""
response = self.client.get(reverse('login'))
self.assertNotIn('<a class="action action-signup" href="/signup">Sign Up</a>', response.content)
@mock.patch.dict(settings.FEATURES, {"ALLOW_PUBLIC_ACCOUNT_CREATION": False})
def test_signup_link_login_page(self):
"""
Navigate to the login page and check the Sign Up link is hidden when ALLOW_PUBLIC_ACCOUNT_CREATION flag
is turned off
"""
response = self.client.get(reverse('login'))
self.assertNotIn('<a href="/signup" class="action action-signin">Don&#39;t have a Studio Account? Sign up!</a>',
response.content)
class ForumTestCase(CourseTestCase): class ForumTestCase(CourseTestCase):
def setUp(self): def setUp(self):
......
...@@ -222,6 +222,9 @@ FEATURES = { ...@@ -222,6 +222,9 @@ FEATURES = {
# Set this to False to facilitate cleaning up invalid xml from your modulestore. # Set this to False to facilitate cleaning up invalid xml from your modulestore.
'ENABLE_XBLOCK_XML_VALIDATION': True, 'ENABLE_XBLOCK_XML_VALIDATION': True,
# Allow public account creation
'ALLOW_PUBLIC_ACCOUNT_CREATION': True,
} }
ENABLE_JASMINE = False ENABLE_JASMINE = False
......
<%namespace name='static' file='/static_content.html'/>
<%page expression_filter="h"/> <%page expression_filter="h"/>
<%inherit file="base.html" /> <%inherit file="base.html" />
<%def name="online_help_token()"><% return "login" %></%def> <%def name="online_help_token()"><% return "login" %></%def>
...@@ -15,7 +16,9 @@ from openedx.core.djangolib.js_utils import js_escaped_string ...@@ -15,7 +16,9 @@ from openedx.core.djangolib.js_utils import js_escaped_string
<section class="content"> <section class="content">
<header> <header>
<h1 class="title title-1">${_("Sign In to {studio_name}").format(studio_name=settings.STUDIO_NAME)}</h1> <h1 class="title title-1">${_("Sign In to {studio_name}").format(studio_name=settings.STUDIO_NAME)}</h1>
<a href="${reverse('signup')}" class="action action-signin">${_("Don't have a {studio_name} Account? Sign up!").format(studio_name=settings.STUDIO_SHORT_NAME)}</a> % if static.get_value('ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION')):
<a href="${reverse('signup')}" class="action action-signin">${_("Don't have a {studio_name} Account? Sign up!").format(studio_name=settings.STUDIO_SHORT_NAME)}</a>
% endif
</header> </header>
<article class="content-primary" role="main"> <article class="content-primary" role="main">
......
...@@ -230,9 +230,11 @@ ...@@ -230,9 +230,11 @@
<li class="nav-item nav-not-signedin-help"> <li class="nav-item nav-not-signedin-help">
<a href="${get_online_help_info(online_help_token)['doc_url']}" title="${_('Contextual Online Help')}" target="_blank">${_("Help")}</a> <a href="${get_online_help_info(online_help_token)['doc_url']}" title="${_('Contextual Online Help')}" target="_blank">${_("Help")}</a>
</li> </li>
<li class="nav-item nav-not-signedin-signup"> % if static.get_value('ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION')):
<a class="action action-signup" href="${reverse('signup')}">${_("Sign Up")}</a> <li class="nav-item nav-not-signedin-signup">
</li> <a class="action action-signup" href="${reverse('signup')}">${_("Sign Up")}</a>
</li>
% endif
<li class="nav-item nav-not-signedin-signin"> <li class="nav-item nav-not-signedin-signin">
<a class="action action-signin" href="${reverse('login')}">${_("Sign In")}</a> <a class="action action-signin" href="${reverse('login')}">${_("Sign In")}</a>
</li> </li>
......
...@@ -9,7 +9,7 @@ from student.models import anonymous_id_for_user, CourseEnrollment, UserProfile ...@@ -9,7 +9,7 @@ from student.models import anonymous_id_for_user, CourseEnrollment, UserProfile
from util.testing import UrlResetMixin from util.testing import UrlResetMixin
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys.edx.locator import CourseLocator from opaque_keys.edx.locator import CourseLocator
from mock import patch from mock import patch, Mock
import ddt import ddt
import json import json
...@@ -261,6 +261,14 @@ class AutoAuthEnabledTestCase(AutoAuthTestCase): ...@@ -261,6 +261,14 @@ class AutoAuthEnabledTestCase(AutoAuthTestCase):
return response return response
@patch("openedx.core.djangoapps.site_configuration.helpers.get_value", Mock(return_value=False))
def test_create_account_not_allowed(self):
"""
Test case to check user creation is forbidden when ALLOW_PUBLIC_ACCOUNT_CREATION feature flag is turned off
"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 403)
class AutoAuthDisabledTestCase(AutoAuthTestCase): class AutoAuthDisabledTestCase(AutoAuthTestCase):
""" """
......
...@@ -4,6 +4,7 @@ import json ...@@ -4,6 +4,7 @@ import json
import unittest import unittest
import ddt import ddt
from mock import patch
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User, AnonymousUser
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -404,6 +405,14 @@ class TestCreateAccount(TestCase): ...@@ -404,6 +405,14 @@ class TestCreateAccount(TestCase):
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_CREATED_AT) UserAttribute.get_user_attribute(user, REGISTRATION_UTM_CREATED_AT)
) )
@patch("openedx.core.djangoapps.site_configuration.helpers.get_value", mock.Mock(return_value=False))
def test_create_account_not_allowed(self):
"""
Test case to check user creation is forbidden when ALLOW_PUBLIC_ACCOUNT_CREATION feature flag is turned off
"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 403)
@ddt.ddt @ddt.ddt
class TestCreateAccountValidation(TestCase): class TestCreateAccountValidation(TestCase):
......
...@@ -23,6 +23,7 @@ from django.contrib.auth.views import password_reset_confirm ...@@ -23,6 +23,7 @@ from django.contrib.auth.views import password_reset_confirm
from django.contrib import messages from django.contrib import messages
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.core import mail from django.core import mail
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse, NoReverseMatch, reverse_lazy from django.core.urlresolvers import reverse, NoReverseMatch, reverse_lazy
from django.core.validators import validate_email, ValidationError from django.core.validators import validate_email, ValidationError
from django.db import IntegrityError, transaction from django.db import IntegrityError, transaction
...@@ -1549,6 +1550,13 @@ def _do_create_account(form, custom_form=None): ...@@ -1549,6 +1550,13 @@ def _do_create_account(form, custom_form=None):
Note: this function is also used for creating test users. Note: this function is also used for creating test users.
""" """
# Check if ALLOW_PUBLIC_ACCOUNT_CREATION flag turned off to restrict user account creation
if not configuration_helpers.get_value(
'ALLOW_PUBLIC_ACCOUNT_CREATION',
settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)
):
raise PermissionDenied()
errors = {} errors = {}
errors.update(form.errors) errors.update(form.errors)
if custom_form: if custom_form:
...@@ -1970,6 +1978,13 @@ def create_account(request, post_override=None): ...@@ -1970,6 +1978,13 @@ def create_account(request, post_override=None):
JSON call to create new edX account. JSON call to create new edX account.
Used by form in signup_modal.html, which is included into navigation.html Used by form in signup_modal.html, which is included into navigation.html
""" """
# Check if ALLOW_PUBLIC_ACCOUNT_CREATION flag turned off to restrict user account creation
if not configuration_helpers.get_value(
'ALLOW_PUBLIC_ACCOUNT_CREATION',
settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)
):
return HttpResponseForbidden(_("Account creation not allowed."))
warnings.warn("Please use RegistrationView instead.", DeprecationWarning) warnings.warn("Please use RegistrationView instead.", DeprecationWarning)
try: try:
...@@ -2074,6 +2089,8 @@ def auto_auth(request): ...@@ -2074,6 +2089,8 @@ def auto_auth(request):
user.save() user.save()
profile = UserProfile.objects.get(user=user) profile = UserProfile.objects.get(user=user)
reg = Registration.objects.get(user=user) reg = Registration.objects.get(user=user)
except PermissionDenied:
return HttpResponseForbidden(_("Account creation not allowed."))
# Set the user's global staff bit # Set the user's global staff bit
if is_staff is not None: if is_staff is not None:
......
...@@ -40,6 +40,7 @@ from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin ...@@ -40,6 +40,7 @@ from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH
from openedx.core.djangolib.js_utils import dump_js_escaped_json from openedx.core.djangolib.js_utils import dump_js_escaped_json
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from student_account.views import account_settings_context, get_user_orders from student_account.views import account_settings_context, get_user_orders
...@@ -735,3 +736,30 @@ class MicrositeLogistrationTests(TestCase): ...@@ -735,3 +736,30 @@ class MicrositeLogistrationTests(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertNotIn('<div id="login-and-registration-container"', resp.content) self.assertNotIn('<div id="login-and-registration-container"', resp.content)
class AccountCreationTestCaseWithSiteOverrides(SiteMixin, TestCase):
"""
Test cases for Feature flag ALLOW_PUBLIC_ACCOUNT_CREATION which when
turned off disables the account creation options in lms
"""
def setUp(self):
"""Set up the tests"""
super(AccountCreationTestCaseWithSiteOverrides, self).setUp()
# Set the feature flag ALLOW_PUBLIC_ACCOUNT_CREATION to False
self.site_configuration_values = {
'ALLOW_PUBLIC_ACCOUNT_CREATION': False
}
self.site_domain = 'testserver1.com'
self.set_up_site(self.site_domain, self.site_configuration_values)
def test_register_option_login_page(self):
"""
Navigate to the login page and check the Register option is hidden when
ALLOW_PUBLIC_ACCOUNT_CREATION flag is turned off
"""
response = self.client.get(reverse('signin_user'))
self.assertNotIn('<a class="btn-neutral" href="/register?next=%2Fdashboard">Register</a>',
response.content)
...@@ -124,6 +124,8 @@ def login_and_registration_form(request, initial_mode="login"): ...@@ -124,6 +124,8 @@ def login_and_registration_form(request, initial_mode="login"):
'login_form_desc': json.loads(form_descriptions['login']), 'login_form_desc': json.loads(form_descriptions['login']),
'registration_form_desc': json.loads(form_descriptions['registration']), 'registration_form_desc': json.loads(form_descriptions['registration']),
'password_reset_form_desc': json.loads(form_descriptions['password_reset']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']),
'account_creation_allowed': configuration_helpers.get_value(
'ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True))
}, },
'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header
'responsive': True, 'responsive': True,
......
...@@ -368,6 +368,9 @@ FEATURES = { ...@@ -368,6 +368,9 @@ FEATURES = {
# Set this to False to facilitate cleaning up invalid xml from your modulestore. # Set this to False to facilitate cleaning up invalid xml from your modulestore.
'ENABLE_XBLOCK_XML_VALIDATION': True, 'ENABLE_XBLOCK_XML_VALIDATION': True,
# Allow public account creation
'ALLOW_PUBLIC_ACCOUNT_CREATION': True,
} }
# Ignore static asset files on import which match this pattern # Ignore static asset files on import which match this pattern
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
), ),
THIRD_PARTY_COMPLETE_URL = '/auth/complete/provider/'; THIRD_PARTY_COMPLETE_URL = '/auth/complete/provider/';
var ajaxSpyAndInitialize = function(that, mode, nextUrl, finishAuthUrl) { var ajaxSpyAndInitialize = function(that, mode, nextUrl, finishAuthUrl, createAccountOption) {
var options = { var options = {
initial_mode: mode, initial_mode: mode,
third_party_auth: { third_party_auth: {
...@@ -66,7 +66,8 @@ ...@@ -66,7 +66,8 @@
platform_name: 'edX', platform_name: 'edX',
login_form_desc: FORM_DESCRIPTION, login_form_desc: FORM_DESCRIPTION,
registration_form_desc: FORM_DESCRIPTION, registration_form_desc: FORM_DESCRIPTION,
password_reset_form_desc: FORM_DESCRIPTION password_reset_form_desc: FORM_DESCRIPTION,
account_creation_allowed: createAccountOption
}, },
$logistrationElement = $('#login-and-registration-container'); $logistrationElement = $('#login-and-registration-container');
...@@ -225,6 +226,20 @@ ...@@ -225,6 +226,20 @@
// Expect that we ignore the external URL and redirect to the dashboard // Expect that we ignore the external URL and redirect to the dashboard
expect(view.redirect).toHaveBeenCalledWith('/dashboard'); expect(view.redirect).toHaveBeenCalledWith('/dashboard');
}); });
it('hides create an account section', function() {
ajaxSpyAndInitialize(this, 'login', '', '', false);
// Expect the Create an account section is hidden
expect((view.$el.find('.toggle-form')).length).toEqual(0);
});
it('shows create an account section', function() {
ajaxSpyAndInitialize(this, 'login', '', '', true);
// Expect the Create an account section is visible
expect((view.$el.find('.toggle-form')).length).toEqual(1);
});
}); });
}); });
}).call(this, define || RequireJS.define); }).call(this, define || RequireJS.define);
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
this.platformName = options.platform_name; this.platformName = options.platform_name;
this.supportURL = options.support_link; this.supportURL = options.support_link;
this.createAccountOption = options.account_creation_allowed;
// The login view listens for 'sync' events from the reset model // The login view listens for 'sync' events from the reset model
this.resetModel = new PasswordResetModel({}, { this.resetModel = new PasswordResetModel({}, {
...@@ -119,7 +120,8 @@ ...@@ -119,7 +120,8 @@
resetModel: this.resetModel, resetModel: this.resetModel,
thirdPartyAuth: this.thirdPartyAuth, thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName, platformName: this.platformName,
supportURL: this.supportURL supportURL: this.supportURL,
createAccountOption: this.createAccountOption
}); });
// Listen for 'password-help' event to toggle sub-views // Listen for 'password-help' event to toggle sub-views
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
this.platformName = data.platformName; this.platformName = data.platformName;
this.resetModel = data.resetModel; this.resetModel = data.resetModel;
this.supportURL = data.supportURL; this.supportURL = data.supportURL;
this.createAccountOption = data.createAccountOption;
this.listenTo(this.model, 'sync', this.saveSuccess); this.listenTo(this.model, 'sync', this.saveSuccess);
this.listenTo(this.resetModel, 'sync', this.resetEmail); this.listenTo(this.resetModel, 'sync', this.resetEmail);
...@@ -53,7 +54,8 @@ ...@@ -53,7 +54,8 @@
currentProvider: this.currentProvider, currentProvider: this.currentProvider,
providers: this.providers, providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders, hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName platformName: this.platformName,
createAccountOption: this.createAccountOption
} }
})); }));
......
...@@ -150,7 +150,7 @@ site_status_msg = get_site_status_msg(course_id) ...@@ -150,7 +150,7 @@ site_status_msg = get_site_status_msg(course_id)
<li class="item nav-global-04"> <li class="item nav-global-04">
<a class="btn-neutral" href="${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}">${_("Register")}</a> <a class="btn-neutral" href="${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}">${_("Register")}</a>
</li> </li>
% else: % elif static.get_value('ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION')):
<li class="item nav-global-04"> <li class="item nav-global-04">
<a class="btn-neutral" href="/register${login_query()}">${_("Register")}</a> <a class="btn-neutral" href="/register${login_query()}">${_("Register")}</a>
</li> </li>
......
...@@ -53,11 +53,13 @@ ...@@ -53,11 +53,13 @@
<% } %> <% } %>
</form> </form>
<div class="toggle-form"> <% if ( context.createAccountOption !== false ) { %>
<div class="section-title"> <div class="toggle-form">
<h2> <div class="section-title">
<span class="text"><%- _.sprintf( gettext("New to %(platformName)s?"), context ) %></span> <h2>
</h2> <span class="text"><%- _.sprintf( gettext("New to %(platformName)s?"), context ) %></span>
</h2>
</div>
<button class="nav-btn form-toggle" data-type="register"><%- gettext("Create an account") %></button>
</div> </div>
<button class="nav-btn form-toggle" data-type="register"><%- gettext("Create an account") %></button> <% } %>
</div>
...@@ -8,6 +8,7 @@ from pytz import UTC ...@@ -8,6 +8,7 @@ from pytz import UTC
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings from django.conf import settings
from django.core.validators import validate_email, validate_slug, ValidationError from django.core.validators import validate_email, validate_slug, ValidationError
from django.http import HttpResponseForbidden
from openedx.core.djangoapps.user_api.preferences.api import update_user_preferences from openedx.core.djangoapps.user_api.preferences.api import update_user_preferences
from openedx.core.djangoapps.user_api.errors import PreferenceValidationError from openedx.core.djangoapps.user_api.errors import PreferenceValidationError
...@@ -292,6 +293,13 @@ def create_account(username, password, email): ...@@ -292,6 +293,13 @@ def create_account(username, password, email):
AccountPasswordInvalid AccountPasswordInvalid
UserAPIInternalError: the operation failed due to an unexpected error. UserAPIInternalError: the operation failed due to an unexpected error.
""" """
# Check if ALLOW_PUBLIC_ACCOUNT_CREATION flag turned off to restrict user account creation
if not configuration_helpers.get_value(
'ALLOW_PUBLIC_ACCOUNT_CREATION',
settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)
):
return HttpResponseForbidden(_("Account creation not allowed."))
# Validate the username, password, and email # Validate the username, password, and email
# This will raise an exception if any of these are not in a valid format. # This will raise an exception if any of these are not in a valid format.
_validate_username(username) _validate_username(username)
......
...@@ -442,3 +442,11 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase): ...@@ -442,3 +442,11 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
return False return False
else: else:
return True return True
@patch("openedx.core.djangoapps.site_configuration.helpers.get_value", Mock(return_value=False))
def test_create_account_not_allowed(self):
"""
Test case to check user creation is forbidden when ALLOW_PUBLIC_ACCOUNT_CREATION feature flag is turned off
"""
response = create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
self.assertEqual(response.status_code, 403)
...@@ -19,6 +19,7 @@ from pytz import common_timezones_set, UTC ...@@ -19,6 +19,7 @@ from pytz import common_timezones_set, UTC
from social.apps.django_app.default.models import UserSocialAuth from social.apps.django_app.default.models import UserSocialAuth
from django_comment_common import models from django_comment_common import models
from openedx.core.djangoapps.site_configuration.helpers import get_value
from openedx.core.lib.api.test_utils import ApiTestCase, TEST_API_KEY from openedx.core.lib.api.test_utils import ApiTestCase, TEST_API_KEY
from openedx.core.lib.time_zone_utils import get_display_time_zone from openedx.core.lib.time_zone_utils import get_display_time_zone
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
...@@ -1774,6 +1775,24 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, UserAPITestCase): ...@@ -1774,6 +1775,24 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, UserAPITestCase):
self.assertContains(response, 'Kosovo') self.assertContains(response, 'Kosovo')
def test_create_account_not_allowed(self):
"""
Test case to check user creation is forbidden when ALLOW_PUBLIC_ACCOUNT_CREATION feature flag is turned off
"""
def _side_effect_for_get_value(value, default=None):
"""
returns a side_effect with given return value for a given value
"""
if value == 'ALLOW_PUBLIC_ACCOUNT_CREATION':
return False
else:
return get_value(value, default)
with mock.patch('openedx.core.djangoapps.site_configuration.helpers.get_value') as mock_get_value:
mock_get_value.side_effect = _side_effect_for_get_value
response = self.client.post(self.url, {"email": self.EMAIL, "username": self.USERNAME})
self.assertEqual(response.status_code, 403)
@httpretty.activate @httpretty.activate
@ddt.ddt @ddt.ddt
......
...@@ -4,9 +4,9 @@ import copy ...@@ -4,9 +4,9 @@ import copy
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseForbidden
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import ImproperlyConfigured, NON_FIELD_ERRORS, ValidationError from django.core.exceptions import ImproperlyConfigured, NON_FIELD_ERRORS, ValidationError, PermissionDenied
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect, csrf_exempt from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect, csrf_exempt
...@@ -302,6 +302,7 @@ class RegistrationView(APIView): ...@@ -302,6 +302,7 @@ class RegistrationView(APIView):
HttpResponse: 400 if the request is not valid. HttpResponse: 400 if the request is not valid.
HttpResponse: 409 if an account with the given username or email HttpResponse: 409 if an account with the given username or email
address already exists address already exists
HttpResponse: 403 operation not allowed
""" """
data = request.POST.copy() data = request.POST.copy()
...@@ -352,6 +353,8 @@ class RegistrationView(APIView): ...@@ -352,6 +353,8 @@ class RegistrationView(APIView):
for field, error_list in err.message_dict.items() for field, error_list in err.message_dict.items()
} }
return JsonResponse(errors, status=400) return JsonResponse(errors, status=400)
except PermissionDenied:
return HttpResponseForbidden(_("Account creation not allowed."))
response = JsonResponse({"success": True}) response = JsonResponse({"success": True})
set_logged_in_cookies(request, response, user) set_logged_in_cookies(request, response, user)
......
...@@ -135,7 +135,7 @@ site_status_msg = get_site_status_msg(course_id) ...@@ -135,7 +135,7 @@ site_status_msg = get_site_status_msg(course_id)
<div class="item nav-courseware-02"> <div class="item nav-courseware-02">
<a class="btn-neutral btn-register" href="${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}">${_("Register")}</a> <a class="btn-neutral btn-register" href="${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}">${_("Register")}</a>
</div> </div>
% else: % elif static.get_value('ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION')):
<div class="item nav-courseware-02"> <div class="item nav-courseware-02">
<a class="btn-neutral btn-register" href="/register">${_("Register")}</a> <a class="btn-neutral btn-register" href="/register">${_("Register")}</a>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment