diff --git a/dj_rest_auth/tests/test_api.py b/dj_rest_auth/tests/test_api.py index 4519186..f0607b5 100644 --- a/dj_rest_auth/tests/test_api.py +++ b/dj_rest_auth/tests/test_api.py @@ -144,46 +144,293 @@ class APIBasicTests(TestsMixin, TestCase): self.post(self.login_url, data=payload, status_code=200) @override_settings(REST_USE_JWT=True) - @override_settings(JWT_AUTH_COOKIE='jwt-auth') - def test_login_jwt_sets_cookie(self): + def test_login_jwt(self): payload = { "username": self.USERNAME, "password": self.PASS } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - resp = self.post(self.login_url, data=payload, status_code=200) - self.assertTrue('jwt-auth' in resp.cookies.keys()) - @override_settings(REST_USE_JWT=True) - @override_settings(JWT_AUTH_COOKIE='jwt-auth') - def test_logout_jwt_deletes_cookie(self): - payload = { - "username": self.USERNAME, - "password": self.PASS - } - get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=payload, status_code=200) - resp = self.post(self.logout_url, status=200) - self.assertEqual('', resp.cookies.get('jwt-auth').value) + self.assertEqual('access_token' in self.response.json.keys(), True) + self.token = self.response.json['access_token'] + + def test_login_by_email(self): + # starting test without allauth app + settings.INSTALLED_APPS.remove('allauth') - @override_settings(REST_USE_JWT=True) - @override_settings(JWT_AUTH_COOKIE='jwt-auth') - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.utils.JWTCookieAuthentication' - ] - )) - @override_settings(REST_SESSION_LOGIN=False) - def test_cookie_authentication(self): payload = { + "email": self.EMAIL.lower(), + "password": self.PASS + } + # there is no users in db so it should throw error (400) + self.post(self.login_url, data=payload, status_code=400) + + self.post(self.password_change_url, status_code=403) + + # create user + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + + # test auth by email + self.post(self.login_url, data=payload, status_code=200) + self.assertEqual('key' in self.response.json.keys(), True) + self.token = self.response.json['key'] + + # test auth by email in different case + payload = { + "email": self.EMAIL.upper(), + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=200) + self.assertEqual('key' in self.response.json.keys(), True) + self.token = self.response.json['key'] + + # test inactive user + user.is_active = False + user.save() + self.post(self.login_url, data=payload, status_code=400) + + # test wrong email/password + payload = { + "email": 't' + self.EMAIL, + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=400) + + # test empty payload + self.post(self.login_url, data={}, status_code=400) + + # bring back allauth + settings.INSTALLED_APPS.append('allauth') + + def test_password_change(self): + login_payload = { "username": self.USERNAME, "password": self.PASS } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - resp = self.post(self.login_url, data=payload, status_code=200) - self.assertEqual(['jwt-auth'], list(resp.cookies.keys())) - resp = self.get('/protected-view/') - self.assertEquals(resp.status_code, 200) + self.post(self.login_url, data=login_payload, status_code=200) + self.token = self.response.json['key'] + + new_password_payload = { + "new_password1": "new_person", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=200 + ) + + # user should not be able to login using old password + self.post(self.login_url, data=login_payload, status_code=400) + + # new password should work + login_payload['password'] = new_password_payload['new_password1'] + self.post(self.login_url, data=login_payload, status_code=200) + + # pass1 and pass2 are not equal + new_password_payload = { + "new_password1": "new_person1", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=400 + ) + + # send empty payload + self.post(self.password_change_url, data={}, status_code=400) + + @override_settings(OLD_PASSWORD_FIELD_ENABLED=True) + def test_password_change_with_old_password(self): + login_payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + self.post(self.login_url, data=login_payload, status_code=200) + self.token = self.response.json['key'] + + new_password_payload = { + "old_password": "%s!" % self.PASS, # wrong password + "new_password1": "new_person", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=400 + ) + + new_password_payload = { + "old_password": self.PASS, + "new_password1": "new_person", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=200 + ) + + # user should not be able to login using old password + self.post(self.login_url, data=login_payload, status_code=400) + + # new password should work + login_payload['password'] = new_password_payload['new_password1'] + self.post(self.login_url, data=login_payload, status_code=200) + + def test_password_reset(self): + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + + # call password reset + mail_count = len(mail.outbox) + payload = {'email': self.EMAIL} + self.post(self.password_reset_url, data=payload, status_code=200) + self.assertEqual(len(mail.outbox), mail_count + 1) + + url_kwargs = self._generate_uid_and_token(user) + url = reverse('rest_password_reset_confirm') + + # wrong token + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': force_text(url_kwargs['uid']), + 'token': '-wrong-token-' + } + self.post(url, data=data, status_code=400) + + # wrong uid + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': '-wrong-uid-', + 'token': url_kwargs['token'] + } + self.post(url, data=data, status_code=400) + + # wrong token and uid + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': '-wrong-uid-', + 'token': '-wrong-token-' + } + self.post(url, data=data, status_code=400) + + # valid payload + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': force_text(url_kwargs['uid']), + 'token': url_kwargs['token'] + } + url = reverse('rest_password_reset_confirm') + self.post(url, data=data, status_code=200) + + payload = { + "username": self.USERNAME, + "password": self.NEW_PASS + } + self.post(self.login_url, data=payload, status_code=200) + + def test_password_reset_with_email_in_different_case(self): + get_user_model().objects.create_user(self.USERNAME, self.EMAIL.lower(), self.PASS) + + # call password reset in upper case + mail_count = len(mail.outbox) + payload = {'email': self.EMAIL.upper()} + self.post(self.password_reset_url, data=payload, status_code=200) + self.assertEqual(len(mail.outbox), mail_count + 1) + + def test_password_reset_with_invalid_email(self): + """ + Invalid email should not raise error, as this would leak users + """ + get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + + # call password reset + mail_count = len(mail.outbox) + payload = {'email': 'nonexisting@email.com'} + self.post(self.password_reset_url, data=payload, status_code=200) + self.assertEqual(len(mail.outbox), mail_count) + + def test_user_details(self): + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + payload = { + "username": self.USERNAME, + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=200) + self.token = self.response.json['key'] + self.get(self.user_url, status_code=200) + + self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) + user = get_user_model().objects.get(pk=user.pk) + self.assertEqual(user.first_name, self.response.json['first_name']) + self.assertEqual(user.last_name, self.response.json['last_name']) + self.assertEqual(user.email, self.response.json['email']) + + @override_settings(REST_USE_JWT=True) + def test_user_details_using_jwt(self): + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + payload = { + "username": self.USERNAME, + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=200) + self.token = self.response.json['access_token'] + self.get(self.user_url, status_code=200) + + self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) + user = get_user_model().objects.get(pk=user.pk) + self.assertEqual(user.email, self.response.json['email']) + + def test_registration(self): + user_count = get_user_model().objects.all().count() + + # test empty payload + self.post(self.register_url, data={}, status_code=400) + + result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) + self.assertIn('key', result.data) + self.assertEqual(get_user_model().objects.all().count(), user_count + 1) + + new_user = get_user_model().objects.latest('id') + self.assertEqual(new_user.username, self.REGISTRATION_DATA['username']) + + self._login() + self._logout() + + @override_settings(REST_AUTH_REGISTER_PERMISSION_CLASSES=(CustomPermissionClass,)) + def test_registration_with_custom_permission_class(self): + + class CustomRegisterView(RegisterView): + permission_classes = register_permission_classes() + authentication_classes = () + + factory = APIRequestFactory() + request = factory.post('/customer/details', self.REGISTRATION_DATA, format='json') + + response = CustomRegisterView.as_view()(request) + self.assertEqual(response.data['detail'], CustomPermissionClass.message) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + @override_settings(REST_USE_JWT=True) + def test_registration_with_jwt(self): + user_count = get_user_model().objects.all().count() + + self.post(self.register_url, data={}, status_code=400) + + result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) + self.assertIn('access_token', result.data) + self.assertEqual(get_user_model().objects.all().count(), user_count + 1) + + self._login() + self._logout() def test_registration_with_invalid_password(self): data = self.REGISTRATION_DATA.copy() @@ -267,3 +514,47 @@ class APIBasicTests(TestsMixin, TestCase): self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK) self.get(self.logout_url, status_code=status.HTTP_405_METHOD_NOT_ALLOWED) + + @override_settings(REST_USE_JWT=True) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + def test_login_jwt_sets_cookie(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + resp = self.post(self.login_url, data=payload, status_code=200) + self.assertTrue('jwt-auth' in resp.cookies.keys()) + + + @override_settings(REST_USE_JWT=True) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + def test_logout_jwt_deletes_cookie(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + self.post(self.login_url, data=payload, status_code=200) + resp = self.post(self.logout_url, status=200) + self.assertEqual('', resp.cookies.get('jwt-auth').value) + + + @override_settings(REST_USE_JWT=True) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + @override_settings(REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.utils.JWTCookieAuthentication' + ] + )) + @override_settings(REST_SESSION_LOGIN=False) + def test_cookie_authentication(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + resp = self.post(self.login_url, data=payload, status_code=200) + self.assertEqual(['jwt-auth'], list(resp.cookies.keys())) + resp = self.get('/protected-view/') + self.assertEquals(resp.status_code, 200) \ No newline at end of file diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index a31e879..c5ba7fc 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -138,7 +138,7 @@ class LogoutView(APIView): response = Response({"detail": _("Successfully logged out.")}, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): - cookie_name = getattr(settings, 'JWT_AUTH_COOKIE') + cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None) if cookie_name: response.delete_cookie(cookie_name) return response