Merge pull request #17561 from edx/jmbowman/PLAT-1976
PLAT-1976 Add waffle switch to block auth_user write attempts
This commit is contained in:
@@ -11,8 +11,6 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.conf import settings
|
||||
from django.core.validators import validate_email, ValidationError
|
||||
from django.http import HttpResponseForbidden
|
||||
from openedx.core.djangoapps.user_api.preferences.api import update_user_preferences
|
||||
from openedx.core.djangoapps.user_api.errors import PreferenceValidationError, AccountValidationError
|
||||
from six import text_type
|
||||
|
||||
from student.models import User, UserProfile, Registration
|
||||
@@ -20,15 +18,21 @@ from student import forms as student_forms
|
||||
from student import views as student_views
|
||||
from util.model_utils import emit_setting_changed_event
|
||||
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangoapps.user_api import errors, accounts, forms, helpers
|
||||
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
|
||||
from openedx.core.djangoapps.user_api.errors import (
|
||||
AccountUpdateError,
|
||||
AccountValidationError,
|
||||
PreferenceValidationError,
|
||||
)
|
||||
from openedx.core.djangoapps.user_api.preferences.api import update_user_preferences
|
||||
from openedx.core.lib.api.view_utils import add_serializer_errors
|
||||
|
||||
from .serializers import (
|
||||
AccountLegacyProfileSerializer, AccountUserSerializer,
|
||||
UserReadOnlySerializer, _visible_fields # pylint: disable=invalid-name
|
||||
)
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangoapps.user_api import errors, accounts, forms, helpers
|
||||
|
||||
|
||||
# Public access point for this function.
|
||||
visible_fields = _visible_fields
|
||||
@@ -242,21 +246,21 @@ def update_account_settings(requesting_user, update, username=None):
|
||||
|
||||
except PreferenceValidationError as err:
|
||||
raise AccountValidationError(err.preference_errors)
|
||||
except AccountValidationError as err:
|
||||
except (AccountUpdateError, AccountValidationError) as err:
|
||||
raise err
|
||||
except Exception as err:
|
||||
raise errors.AccountUpdateError(
|
||||
raise AccountUpdateError(
|
||||
u"Error thrown when saving account updates: '{}'".format(text_type(err))
|
||||
)
|
||||
|
||||
# And try to send the email change request if necessary.
|
||||
if changing_email:
|
||||
if not settings.FEATURES['ALLOW_EMAIL_ADDRESS_CHANGE']:
|
||||
raise errors.AccountUpdateError(u"Email address changes have been disabled by the site operators.")
|
||||
raise AccountUpdateError(u"Email address changes have been disabled by the site operators.")
|
||||
try:
|
||||
student_views.do_email_change_request(existing_user, new_email)
|
||||
except ValueError as err:
|
||||
raise errors.AccountUpdateError(
|
||||
raise AccountUpdateError(
|
||||
u"Error thrown from do_email_change_request: '{}'".format(text_type(err)),
|
||||
user_message=text_type(err)
|
||||
)
|
||||
@@ -310,6 +314,9 @@ def create_account(username, password, email):
|
||||
):
|
||||
return HttpResponseForbidden(_("Account creation not allowed."))
|
||||
|
||||
if waffle().is_enabled(PREVENT_AUTH_USER_WRITES):
|
||||
raise errors.UserAPIInternalError(SYSTEM_MAINTENANCE_MSG)
|
||||
|
||||
# Validate the username, password, and email
|
||||
# This will raise an exception if any of these are not in a valid format.
|
||||
_validate_username(username)
|
||||
@@ -383,6 +390,8 @@ def activate_account(activation_key):
|
||||
errors.UserAPIInternalError: the operation failed due to an unexpected error.
|
||||
|
||||
"""
|
||||
if waffle().is_enabled(PREVENT_AUTH_USER_WRITES):
|
||||
raise errors.UserAPIInternalError(SYSTEM_MAINTENANCE_MSG)
|
||||
try:
|
||||
registration = Registration.objects.get(activation_key=activation_key)
|
||||
except Registration.DoesNotExist:
|
||||
|
||||
@@ -33,6 +33,7 @@ from openedx.core.djangoapps.user_api.accounts.tests.testutils import (
|
||||
INVALID_USERNAMES,
|
||||
VALID_USERNAMES_UNICODE
|
||||
)
|
||||
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
|
||||
from openedx.core.djangoapps.user_api.errors import (
|
||||
AccountEmailInvalid,
|
||||
AccountPasswordInvalid,
|
||||
@@ -41,6 +42,7 @@ from openedx.core.djangoapps.user_api.errors import (
|
||||
AccountUserAlreadyExists,
|
||||
AccountUsernameInvalid,
|
||||
AccountValidationError,
|
||||
UserAPIInternalError,
|
||||
UserNotAuthorized,
|
||||
UserNotFound
|
||||
)
|
||||
@@ -408,10 +410,21 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
|
||||
def test_create_account_invalid_username(self, invalid_username):
|
||||
create_account(invalid_username, self.PASSWORD, self.EMAIL)
|
||||
|
||||
def test_create_account_prevent_auth_user_writes(self):
|
||||
with pytest.raises(UserAPIInternalError, message=SYSTEM_MAINTENANCE_MSG):
|
||||
with waffle().override(PREVENT_AUTH_USER_WRITES, True):
|
||||
create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
||||
|
||||
@raises(UserNotAuthorized)
|
||||
def test_activate_account_invalid_key(self):
|
||||
activate_account(u'invalid')
|
||||
|
||||
def test_activate_account_prevent_auth_user_writes(self):
|
||||
activation_key = create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
||||
with pytest.raises(UserAPIInternalError, message=SYSTEM_MAINTENANCE_MSG):
|
||||
with waffle().override(PREVENT_AUTH_USER_WRITES, True):
|
||||
activate_account(activation_key)
|
||||
|
||||
@skip_unless_lms
|
||||
def test_request_password_change(self):
|
||||
# Create and activate an account
|
||||
|
||||
0
openedx/core/djangoapps/user_api/config/__init__.py
Normal file
0
openedx/core/djangoapps/user_api/config/__init__.py
Normal file
21
openedx/core/djangoapps/user_api/config/waffle.py
Normal file
21
openedx/core/djangoapps/user_api/config/waffle.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Waffle flags and switches to change user API functionality.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace
|
||||
|
||||
SYSTEM_MAINTENANCE_MSG = _(u'System maintenance in progress. Please try again later.')
|
||||
WAFFLE_NAMESPACE = u'user_api'
|
||||
|
||||
# Switches
|
||||
PREVENT_AUTH_USER_WRITES = u'prevent_auth_user_writes'
|
||||
|
||||
|
||||
def waffle():
|
||||
"""
|
||||
Returns the namespaced, cached, audited Waffle class for user_api.
|
||||
"""
|
||||
return WaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'UserAPI: ')
|
||||
Reference in New Issue
Block a user