views.py 12.8 KB
Newer Older
1
from django.contrib.auth import get_user_model
2
from django.contrib.auth.tokens import default_token_generator
3
from django.urls.exceptions import NoReverseMatch
4
from django.utils.timezone import now
5 6
from rest_framework import generics, permissions, status, views, viewsets
from rest_framework.decorators import list_route
7 8
from rest_framework.response import Response
from rest_framework.reverse import reverse
9

10 11
from djoser import utils, signals
from djoser.compat import get_user_email, get_user_email_field_name
12
from djoser.conf import settings
13 14 15 16 17 18 19 20

User = get_user_model()


class RootView(views.APIView):
    """
    Root endpoint - use one of sub endpoints.
    """
21 22
    permission_classes = [permissions.AllowAny]

23 24 25 26 27 28 29 30 31
    def _get_url_names(self, urllist):
        names = []
        for entry in urllist:
            if hasattr(entry, 'url_patterns'):
                names.extend(self._get_url_names(entry.url_patterns))
            else:
                names.append(entry.name)
        return names

32 33
    def aggregate_djoser_urlpattern_names(self):
        from djoser.urls import base, authtoken
34 35
        urlpattern_names = self._get_url_names(base.urlpatterns)
        urlpattern_names += self._get_url_names(authtoken.urlpatterns)
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
        urlpattern_names += self._get_jwt_urlpatterns()

        return urlpattern_names

    def get_urls_map(self, request, urlpattern_names, fmt):
        urls_map = {}
        for urlpattern_name in urlpattern_names:
            try:
                url = reverse(urlpattern_name, request=request, format=fmt)
            except NoReverseMatch:
                url = ''
            urls_map[urlpattern_name] = url
        return urls_map

    def get(self, request, fmt=None):
        urlpattern_names = self.aggregate_djoser_urlpattern_names()
        urls_map = self.get_urls_map(request, urlpattern_names, fmt)
        return Response(urls_map)

    def _get_jwt_urlpatterns(self):
        try:
            from djoser.urls import jwt
58
            return self._get_url_names(jwt.urlpatterns)
59 60 61 62 63
        except ImportError:
            return []


class UserCreateView(generics.CreateAPIView):
64 65 66
    """
    Use this endpoint to register new user.
    """
67 68
    serializer_class = settings.SERIALIZERS.user_create
    permission_classes = [permissions.AllowAny]
69 70

    def perform_create(self, serializer):
71
        user = serializer.save()
72 73 74
        signals.user_registered.send(
            sender=self.__class__, user=user, request=self.request
        )
75 76 77

        context = {'user': user}
        to = [get_user_email(user)]
78
        if settings.SEND_ACTIVATION_EMAIL:
79
            settings.EMAIL.activation(self.request, context).send(to)
80
        elif settings.SEND_CONFIRMATION_EMAIL:
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
            settings.EMAIL.confirmation(self.request, context).send(to)


class UserDeleteView(generics.CreateAPIView):
    """
    Use this endpoint to remove actually authenticated user
    """
    serializer_class = settings.SERIALIZERS.user_delete
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self):
        return self.request.user

    def post(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)
        serializer.is_valid(raise_exception=True)

        utils.logout_user(self.request)
        instance.delete()
101

102
        return Response(status=status.HTTP_204_NO_CONTENT)
103 104


105
class TokenCreateView(utils.ActionViewMixin, generics.GenericAPIView):
106 107 108
    """
    Use this endpoint to obtain user authentication token.
    """
109 110
    serializer_class = settings.SERIALIZERS.token_create
    permission_classes = [permissions.AllowAny]
111

112
    def _action(self, serializer):
113
        token = utils.login_user(self.request, serializer.user)
114
        token_serializer_class = settings.SERIALIZERS.token
115
        return Response(
116
            data=token_serializer_class(token).data,
117 118 119 120
            status=status.HTTP_200_OK,
        )


121
class TokenDestroyView(views.APIView):
122 123 124
    """
    Use this endpoint to logout user (remove user authentication token).
    """
125
    permission_classes = [permissions.IsAuthenticated]
126 127

    def post(self, request):
128
        utils.logout_user(request)
129
        return Response(status=status.HTTP_204_NO_CONTENT)
130 131


132
class PasswordResetView(utils.ActionViewMixin, generics.GenericAPIView):
133 134 135
    """
    Use this endpoint to send email to user with password reset link.
    """
136
    serializer_class = settings.SERIALIZERS.password_reset
137
    permission_classes = [permissions.AllowAny]
138 139

    _users = None
140

141
    def _action(self, serializer):
142
        for user in self.get_users(serializer.data['email']):
143
            self.send_password_reset_email(user)
144
        return Response(status=status.HTTP_204_NO_CONTENT)
145 146

    def get_users(self, email):
147
        if self._users is None:
148 149 150 151 152 153 154
            email_field_name = get_user_email_field_name(User)
            users = User._default_manager.filter(**{
                email_field_name + '__iexact': email
            })
            self._users = [
                u for u in users if u.is_active and u.has_usable_password()
            ]
155
        return self._users
156

157
    def send_password_reset_email(self, user):
158 159 160
        context = {'user': user}
        to = [get_user_email(user)]
        settings.EMAIL.password_reset(self.request, context).send(to)
161 162 163 164 165 166


class SetPasswordView(utils.ActionViewMixin, generics.GenericAPIView):
    """
    Use this endpoint to change user password.
    """
167
    permission_classes = [permissions.IsAuthenticated]
168 169

    def get_serializer_class(self):
170 171 172
        if settings.SET_PASSWORD_RETYPE:
            return settings.SERIALIZERS.set_password_retype
        return settings.SERIALIZERS.set_password
173

174
    def _action(self, serializer):
175 176
        self.request.user.set_password(serializer.data['new_password'])
        self.request.user.save()
177

178
        if settings.LOGOUT_ON_PASSWORD_CHANGE:
179 180
            utils.logout_user(self.request)

181
        return Response(status=status.HTTP_204_NO_CONTENT)
182 183 184 185 186 187


class PasswordResetConfirmView(utils.ActionViewMixin, generics.GenericAPIView):
    """
    Use this endpoint to finish reset password process.
    """
188
    permission_classes = [permissions.AllowAny]
189 190 191
    token_generator = default_token_generator

    def get_serializer_class(self):
192 193 194
        if settings.PASSWORD_RESET_CONFIRM_RETYPE:
            return settings.SERIALIZERS.password_reset_confirm_retype
        return settings.SERIALIZERS.password_reset_confirm
195

196
    def _action(self, serializer):
197
        serializer.user.set_password(serializer.data['new_password'])
198 199
        if hasattr(serializer.user, 'last_login'):
            serializer.user.last_login = now()
200
        serializer.user.save()
201
        return Response(status=status.HTTP_204_NO_CONTENT)
202 203 204 205 206 207


class ActivationView(utils.ActionViewMixin, generics.GenericAPIView):
    """
    Use this endpoint to activate user account.
    """
208
    serializer_class = settings.SERIALIZERS.activation
209
    permission_classes = [permissions.AllowAny]
210 211
    token_generator = default_token_generator

212
    def _action(self, serializer):
213 214 215 216
        user = serializer.user
        user.is_active = True
        user.save()

217
        signals.user_activated.send(
218 219
            sender=self.__class__, user=user, request=self.request
        )
220 221

        if settings.SEND_CONFIRMATION_EMAIL:
222 223 224 225
            context = {'user': user}
            to = [get_user_email(user)]
            settings.EMAIL.confirmation(self.request, context).send(to)

226
        return Response(status=status.HTTP_204_NO_CONTENT)
227 228 229 230 231 232


class SetUsernameView(utils.ActionViewMixin, generics.GenericAPIView):
    """
    Use this endpoint to change user username.
    """
233
    permission_classes = [permissions.IsAuthenticated]
234 235

    def get_serializer_class(self):
236 237 238
        if settings.SET_USERNAME_RETYPE:
            return settings.SERIALIZERS.set_username_retype
        return settings.SERIALIZERS.set_username
239

240
    def _action(self, serializer):
241 242 243 244 245 246
        user = self.request.user
        new_username = serializer.data['new_' + User.USERNAME_FIELD]

        setattr(user, User.USERNAME_FIELD, new_username)
        if settings.SEND_ACTIVATION_EMAIL:
            user.is_active = False
247 248 249
            context = {'user': user}
            to = [get_user_email(user)]
            settings.EMAIL.activation(self.request, context).send(to)
250 251
        user.save()

252
        return Response(status=status.HTTP_204_NO_CONTENT)
253 254 255 256 257 258 259


class UserView(generics.RetrieveUpdateAPIView):
    """
    Use this endpoint to retrieve/update user.
    """
    model = User
260
    serializer_class = settings.SERIALIZERS.user
261
    permission_classes = [permissions.IsAuthenticated]
262 263 264

    def get_object(self, *args, **kwargs):
        return self.request.user
265 266 267 268 269

    def perform_update(self, serializer):
        super(UserView, self).perform_update(serializer)
        user = serializer.instance
        if settings.SEND_ACTIVATION_EMAIL and not user.is_active:
270 271 272
            context = {'user': user}
            to = [get_user_email(user)]
            settings.EMAIL.activation(self.request, context).send(to)
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288


class UserViewSet(UserCreateView, viewsets.ModelViewSet):
    serializer_class = settings.SERIALIZERS.user
    queryset = User.objects.all()
    permission_classes = [permissions.IsAuthenticated]
    token_generator = default_token_generator

    def get_permissions(self):
        if self.action in ['create', 'confirm']:
            self.permission_classes = [permissions.AllowAny]
        elif self.action == 'list':
            self.permission_classes = [permissions.IsAdminUser]
        return super(UserViewSet, self).get_permissions()

    def get_serializer_class(self):
289 290 291 292
        if self.action == 'me':
            # Use the current user serializer on 'me' endpoints
            self.serializer_class = settings.SERIALIZERS.current_user

293 294
        if self.action == 'create':
            return settings.SERIALIZERS.user_create
295

296
        elif self.action == 'remove' or (
297 298 299
                self.action == 'me' and self.request and
                self.request.method == 'DELETE'
        ):
300
            return settings.SERIALIZERS.user_delete
301

302 303
        elif self.action == 'confirm':
            return settings.SERIALIZERS.activation
304

305 306 307
        elif self.action == 'change_username':
            if settings.SET_USERNAME_RETYPE:
                return settings.SERIALIZERS.set_username_retype
308

309
            return settings.SERIALIZERS.set_username
310

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
        return self.serializer_class

    def get_instance(self):
        return self.request.user

    def perform_update(self, serializer):
        super(UserViewSet, self).perform_update(serializer)
        user = serializer.instance
        if settings.SEND_ACTIVATION_EMAIL and not user.is_active:
            context = {'user': user}
            to = [get_user_email(user)]
            settings.EMAIL.activation(self.request, context).send(to)

    def perform_destroy(self, instance):
        utils.logout_user(self.request)
        super(UserViewSet, self).perform_destroy(instance)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)
        serializer.is_valid(raise_exception=True)

        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    @list_route(['get', 'put', 'patch', 'delete'])
    def me(self, request, *args, **kwargs):
        self.get_object = self.get_instance
        if request.method == 'GET':
            return self.retrieve(request, *args, **kwargs)
        elif request.method in ['PUT', 'PATCH']:
            return self.update(request, *args, **kwargs)
        elif request.method == 'DELETE':
            return self.destroy(request, *args, **kwargs)

    @list_route(['post'])
    def confirm(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.user
        user.is_active = True
        user.save()

        signals.user_activated.send(
            sender=self.__class__, user=user, request=self.request
        )

        if settings.SEND_CONFIRMATION_EMAIL:
            context = {'user': user}
            to = [get_user_email(user)]
            settings.EMAIL.confirmation(self.request, context).send(to)

        return Response(status=status.HTTP_204_NO_CONTENT)

    @list_route(['post'])
    def change_username(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.request.user
        new_username = serializer.data['new_' + User.USERNAME_FIELD]

        setattr(user, User.USERNAME_FIELD, new_username)
        if settings.SEND_ACTIVATION_EMAIL:
            user.is_active = False
            context = {'user': user}
            to = [get_user_email(user)]
            settings.EMAIL.activation(self.request, context).send(to)
        user.save()

        return Response(status=status.HTTP_204_NO_CONTENT)