From 799199ece1bb962aea29e52a6d64d5bc300e6a2c Mon Sep 17 00:00:00 2001 From: Wille Marcel Date: Wed, 26 Dec 2018 22:30:24 -0300 Subject: [PATCH 1/2] add endpoint that allows to delete user metadata --- osmchadjango/users/tests/test_views.py | 92 ++++++++++++++++++++++++++ osmchadjango/users/urls.py | 11 ++- osmchadjango/users/views.py | 42 +++++++++++- 3 files changed, 143 insertions(+), 2 deletions(-) diff --git a/osmchadjango/users/tests/test_views.py b/osmchadjango/users/tests/test_views.py index 0352b37b..b57a9b28 100644 --- a/osmchadjango/users/tests/test_views.py +++ b/osmchadjango/users/tests/test_views.py @@ -4,6 +4,8 @@ from social_django.models import UserSocialAuth from ..models import User +from ...changeset.tests.modelfactories import ChangesetFactory +from ...changeset.models import Changeset class TestCurrentUserDetailAPIView(APITestCase): @@ -100,3 +102,93 @@ def test_receive_oauth_token(self): self.assertEqual(response.status_code, 200) self.assertIn('oauth_token', response.data.keys()) self.assertIn('oauth_token_secret', response.data.keys()) + + +class TestUpdateDeletedUsersView(APITestCase): + def setUp(self): + self.url = reverse('users:update-deleted-users') + ChangesetFactory.create_batch(50, uid="1769", user="test_user") + ChangesetFactory.create_batch(50, uid="1234", user="old_user") + self.user = User.objects.create_user( + username='test', + password='password', + email='a@a.com' + ) + UserSocialAuth.objects.create( + user=self.user, + provider='openstreetmap', + uid='123123', + ) + self.staff_user = User.objects.create_user( + username='staff', + password='password', + email='a@a.com', + is_staff=True + ) + UserSocialAuth.objects.create( + user=self.staff_user, + provider='openstreetmap', + uid='123456', + ) + + def test_unauthenticated(self): + request = self.client.post(self.url, data={'uids': [1769, 1234]}) + self.assertEqual(request.status_code, 401) + + def test_non_staff_user(self): + self.client.login(username=self.user.username, password='password') + request = self.client.post(self.url, data={'uids': [1769, 1234]}) + self.assertEqual(request.status_code, 403) + + def test_bad_request(self): + self.client.login(username=self.staff_user.username, password='password') + request = self.client.post(self.url) + self.assertEqual(request.status_code, 400) + request = self.client.post(self.url, data={'uid': [1769, 1234]}) + self.assertEqual(request.status_code, 400) + + def test_view(self): + user = User.objects.create_user( + username='test_user', + password='password', + email='a@a.com' + ) + UserSocialAuth.objects.create( + user=user, + provider='openstreetmap', + uid='1769', + ) + user_2 = User.objects.create_user( + username='old_user', + password='password', + email='a@a.com' + ) + UserSocialAuth.objects.create( + user=user_2, + provider='openstreetmap', + uid='1234', + ) + self.client.login(username=self.staff_user.username, password='password') + request = self.client.post(self.url, data={'uids': [1769, 1234]}) + self.assertEqual(request.status_code, 200) + self.assertEqual(Changeset.objects.filter(uid='1769').count(), 50) + self.assertEqual(Changeset.objects.filter(user='user_1769').count(), 50) + self.assertEqual(Changeset.objects.filter(user='test_user').count(), 0) + self.assertEqual(Changeset.objects.filter(uid='1234').count(), 50) + self.assertEqual(Changeset.objects.filter(user='user_1234').count(), 50) + self.assertEqual(Changeset.objects.filter(user='old_user').count(), 0) + self.assertEqual(User.objects.filter(username='old_user').count(), 0) + self.assertEqual(User.objects.filter(username='test_user').count(), 0) + self.assertEqual(User.objects.filter(username='user_1234').count(), 1) + self.assertEqual(User.objects.filter(username='user_1769').count(), 1) + + def test_view_as_strings(self): + self.client.login(username=self.staff_user.username, password='password') + request = self.client.post(self.url, data={'uids': ['1769', '1234']}) + self.assertEqual(request.status_code, 200) + self.assertEqual(Changeset.objects.filter(uid='1769').count(), 50) + self.assertEqual(Changeset.objects.filter(user='user_1769').count(), 50) + self.assertEqual(Changeset.objects.filter(user='test_user').count(), 0) + self.assertEqual(Changeset.objects.filter(uid='1234').count(), 50) + self.assertEqual(Changeset.objects.filter(user='user_1234').count(), 50) + self.assertEqual(Changeset.objects.filter(user='old_user').count(), 0) diff --git a/osmchadjango/users/urls.py b/osmchadjango/users/urls.py index f0bd20a9..9d7a5e5f 100644 --- a/osmchadjango/users/urls.py +++ b/osmchadjango/users/urls.py @@ -13,5 +13,14 @@ view=views.CurrentUserDetailAPIView.as_view(), name='detail' ), - re_path(r'^social-auth/$', views.SocialAuthAPIView.as_view(), name="social-auth"), + re_path( + r'^social-auth/$', + view=views.SocialAuthAPIView.as_view(), + name="social-auth" + ), + re_path( + r'^update-deleted-users/$', + view=views.update_deleted_users, + name="update-deleted-users" + ), ] diff --git a/osmchadjango/users/views.py b/osmchadjango/users/views.py index ba70d1bd..fdd46f88 100644 --- a/osmchadjango/users/views.py +++ b/osmchadjango/users/views.py @@ -5,12 +5,18 @@ from django.conf import settings from rest_framework.authtoken.models import Token +from rest_framework import status from rest_framework.generics import RetrieveUpdateAPIView, GenericAPIView -from rest_framework.permissions import IsAuthenticated +from rest_framework.decorators import ( + api_view, parser_classes, permission_classes + ) +from rest_framework.parsers import JSONParser, MultiPartParser, FormParser +from rest_framework.permissions import IsAuthenticated, IsAdminUser from rest_framework.response import Response from social_django.utils import load_strategy, load_backend from requests_oauthlib import OAuth1Session +from ..changeset.models import Changeset from .serializers import UserSerializer, SocialSignUpSerializer User = get_user_model() @@ -95,3 +101,37 @@ def post(self, request, *args, **kwargs): request.data['oauth_verifier'] ) return Response(self.get_user_token(request, access_token)) + + +@api_view(['POST']) +@parser_classes((JSONParser, MultiPartParser, FormParser)) +@permission_classes((IsAuthenticated, IsAdminUser)) +def update_deleted_users(request): + """Receive a list of user ids and remove the related user metadata. It will + replace the username in the changesets by the string 'user_' and also + rename it on the User model. It's intended to receive the list of uids of + the users that deleted themselves in the OpenStreetMap website. Only staff + users have permissions to use this endpoint. + """ + + if request.data and request.data.get('uids'): + uids = [str(uid) for uid in request.data.get('uids')] + for uid in uids: + Changeset.objects.filter(uid=uid).update( + user='user_{}'.format(uid) + ) + try: + user = User.objects.get(social_auth__uid=uid) + user.username = 'user_{}'.format(uid) + user.save() + except User.DoesNotExist: + pass + return Response( + {'detail': 'Changesets updated.'}, + status=status.HTTP_200_OK + ) + else: + return Response( + {'detail': 'Changeset was already checked.'}, + status=status.HTTP_400_BAD_REQUEST + ) From 7931c2242673d40dad521cbfa865b85d3cd5204b Mon Sep 17 00:00:00 2001 From: Wille Marcel Date: Wed, 2 Jan 2019 11:02:49 -0300 Subject: [PATCH 2/2] improve return messages --- osmchadjango/users/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osmchadjango/users/views.py b/osmchadjango/users/views.py index fdd46f88..30de67bc 100644 --- a/osmchadjango/users/views.py +++ b/osmchadjango/users/views.py @@ -127,11 +127,11 @@ def update_deleted_users(request): except User.DoesNotExist: pass return Response( - {'detail': 'Changesets updated.'}, + {'detail': 'Changesets updated and user renamed.'}, status=status.HTTP_200_OK ) else: return Response( - {'detail': 'Changeset was already checked.'}, + {'detail': 'Payload is missing the `uids` field or it has an incorrect value.'}, status=status.HTTP_400_BAD_REQUEST )