Merge pull request #17920 from edx/bmedx/fix_retirement_apis

Update retirement APIs to match spec
This commit is contained in:
Brian Mesick
2018-04-25 10:39:26 -04:00
committed by GitHub
9 changed files with 201 additions and 177 deletions

View File

@@ -28,6 +28,7 @@ from discussion_api.tests.utils import (
)
from django_comment_client.tests.utils import ForumsEnableMixin
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_storage
from openedx.core.djangoapps.user_api.models import RetirementState, UserRetirementStatus
from openedx.core.lib.token_utils import JwtBuilder
from student.models import get_retired_username_by_username
from student.tests.factories import CourseEnrollmentFactory, UserFactory, SuperuserFactory
@@ -163,12 +164,16 @@ class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for CourseView"""
def setUp(self):
super(RetireViewTest, self).setUp()
RetirementState.objects.create(state_name='PENDING', state_execution_order=1)
self.retire_forums_state = RetirementState.objects.create(state_name='RETIRE_FORUMS', state_execution_order=11)
self.retirement = UserRetirementStatus.create_retirement(self.user)
self.retirement.current_state = self.retire_forums_state
self.retirement.save()
self.superuser = SuperuserFactory()
self.retired_username = get_retired_username_by_username(self.user.username)
self.url = reverse(
"retire_discussion_user",
kwargs={"username": text_type(self.user.username)}
)
self.url = reverse("retire_discussion_user")
def assert_response_correct(self, response, expected_status, expected_content):
"""
@@ -193,24 +198,18 @@ class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""
self.register_get_user_retire_response(self.user)
headers = self.build_jwt_headers(self.superuser)
response = self.client.post(self.url, {'retired_username': self.retired_username}, **headers)
data = {'username': self.user.username}
response = self.client.post(self.url, data, **headers)
self.assert_response_correct(response, 204, "")
def test_bad_hash(self):
"""
Check that we fail on a hash mismatch with an appropriate error
"""
headers = self.build_jwt_headers(self.superuser)
response = self.client.post(self.url, {'retired_username': "this will never match"}, **headers)
self.assert_response_correct(response, 500, '"Mismatched hashed_username, bad salt?"')
def test_downstream_forums_error(self):
"""
Check that we bubble up errors from the comments service
"""
self.register_get_user_retire_response(self.user, status=500, body="Server error")
headers = self.build_jwt_headers(self.superuser)
response = self.client.post(self.url, {'retired_username': self.retired_username}, **headers)
data = {'username': self.user.username}
response = self.client.post(self.url, data, **headers)
self.assert_response_correct(response, 500, '"Server error"')
def test_nonexistent_user(self):
@@ -218,19 +217,15 @@ class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
Check that we handle unknown users appropriately
"""
nonexistent_username = "nonexistent user"
self.url = reverse(
"retire_discussion_user",
kwargs={"username": nonexistent_username}
)
self.retired_username = get_retired_username_by_username(nonexistent_username)
data = {'username': nonexistent_username}
headers = self.build_jwt_headers(self.superuser)
response = self.client.post(self.url, {'retired_username': self.retired_username}, **headers)
response = self.client.post(self.url, data, **headers)
self.assert_response_correct(response, 404, None)
def test_not_authenticated(self):
"""
Override the parent implementation of this, we auth differently
Override the parent implementation of this, we JWT auth for this API
"""
pass

View File

@@ -17,7 +17,7 @@ urlpatterns = [
CourseView.as_view(),
name="discussion_course"
),
url(r"^v1/users/{}".format(settings.USERNAME_PATTERN), RetireUserView.as_view(), name="retire_discussion_user"),
url(r"^v1/accounts/retire_forum", RetireUserView.as_view(), name="retire_discussion_user"),
url(
r"^v1/course_topics/{}".format(settings.COURSE_ID_PATTERN),
CourseTopicsView.as_view(),

View File

@@ -33,7 +33,7 @@ from discussion_api.forms import CommentGetForm, CommentListGetForm, ThreadListG
from openedx.core.lib.api.parsers import MergePatchParser
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser
from student.models import get_potentially_retired_user_by_username_and_hash
from openedx.core.djangoapps.user_api.models import UserRetirementStatus
from xmodule.modulestore.django import modulestore
@@ -543,23 +543,22 @@ class RetireUserView(APIView):
authentication_classes = (JwtAuthentication,)
permission_classes = (permissions.IsAuthenticated, CanRetireUser)
def post(self, request, username):
def post(self, request):
"""
Implements the retirement endpoint.
"""
user_model = get_user_model()
retired_username = request.data['retired_username']
username = request.data['username']
try:
user = get_potentially_retired_user_by_username_and_hash(username, retired_username)
cc_user = comment_client.User.from_django_user(user)
retirement = UserRetirementStatus.get_retirement_for_retirement_action(username)
cc_user = comment_client.User.from_django_user(retirement.user)
# We can't count on the LMS username being un-retired at this point,
# so we pass the old username as a parameter to describe which
# user to retire. This will either succeed or throw an error which
# should be good to raise from here.
cc_user.retire(username)
except user_model.DoesNotExist:
except UserRetirementStatus.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception as exc: # pylint: disable=broad-except
return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)