Skip to content

Commit

Permalink
[change!] Improved REST API to change password #289
Browse files Browse the repository at this point in the history
Inherited PasswordChangeView of openwisp-users to add support for
the current-password in password change view.

This change is backward incompatible for consumer applications that were using this API endpoint.

Closes #289
  • Loading branch information
codesankalp authored Nov 1, 2021
1 parent 2235fe1 commit c8e628d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 12 deletions.
1 change: 1 addition & 0 deletions openwisp_radius/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = '__all__'
ref_name = 'radius_user_group_serializer'


class UserSerializer(serializers.ModelSerializer):
Expand Down
10 changes: 8 additions & 2 deletions openwisp_radius/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from allauth.account.utils import url_str_to_user_pk, user_pk_to_url_str
from dj_rest_auth import app_settings as rest_auth_settings
from dj_rest_auth.registration.views import RegisterView as BaseRegisterView
from dj_rest_auth.views import PasswordChangeView as BasePasswordChangeView
from dj_rest_auth.views import PasswordResetConfirmView as BasePasswordResetConfirmView
from dj_rest_auth.views import PasswordResetView as BasePasswordResetView
from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -43,6 +42,7 @@
from openwisp_radius.api.serializers import RadiusUserSerializer
from openwisp_users.api.authentication import BearerAuthentication
from openwisp_users.api.permissions import IsOrganizationManager
from openwisp_users.api.views import ChangePasswordView as BasePasswordChangeView

from .. import settings as app_settings
from ..exceptions import PhoneTokenException, UserAlreadyVerified
Expand Down Expand Up @@ -424,6 +424,12 @@ def get_queryset(self):
class PasswordChangeView(ThrottledAPIMixin, DispatchOrgMixin, BasePasswordChangeView):
authentication_classes = (BearerAuthentication,)

def get_permissions(self):
return [IsAuthenticated()]

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

@swagger_auto_schema(responses={200: '`{"detail":"New password has been saved."}`'})
def post(self, request, *args, **kwargs):
"""
Expand All @@ -432,7 +438,7 @@ def post(self, request, *args, **kwargs):
the `Reset password` endpoint.
"""
self.validate_membership(request.user)
return super().post(request, *args, **kwargs)
return super().update(request, *args, **kwargs)


password_change = PasswordChangeView.as_view()
Expand Down
57 changes: 47 additions & 10 deletions openwisp_radius/tests/test_api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,9 @@ def test_api_password_change(self):
'radius:rest_password_change', args=['random-valid-slug'],
)
new_password_payload = {
'new_password1': 'test_new_password',
'new_password2': 'test_new_password',
'current_password': 'test_password',
'new_password': 'test_new_password',
'confirm_password': 'test_new_password',
}
response = client.post(password_change_url, data=new_password_payload)
self.assertEqual(response.status_code, 404)
Expand All @@ -591,31 +592,67 @@ def test_api_password_change(self):
'radius:rest_password_change', args=[self.default_org.slug]
)
new_password_payload = {
'new_password1': 'test_new_password',
'new_password2': 'test_new_password_different',
'current_password': 'test_password',
'new_password': 'test_new_password',
'confirm_password': 'test_new_password_different',
}
response = client.post(password_change_url, data=new_password_payload)
self.assertEqual(response.status_code, 400)
self.assertIn(
'New password and Confirm password do not match.',
str(response.data['confirm_password']),
)

# current password is not the actual password
client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
password_change_url = reverse(
'radius:rest_password_change', args=[self.default_org.slug]
)
new_password_payload = {
'current_password': 'wrong_password',
'new_password': 'test_new_password',
'confirm_password': 'test_new_password',
}
response = client.post(password_change_url, data=new_password_payload)
self.assertEqual(response.status_code, 400)
self.assertIn(
'The two password fields didn’t match.',
str(response.data['new_password2']).replace("'", "’"),
'Current password was entered incorrectly. Please enter it again.',
str(response.data['current_password']),
)

# new password is same as the current password
client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
password_change_url = reverse(
'radius:rest_password_change', args=[self.default_org.slug]
)
new_password_payload = {
'current_password': 'test_password',
'new_password': 'test_password',
'confirm_password': 'test_password',
}
response = client.post(password_change_url, data=new_password_payload)
self.assertEqual(response.status_code, 400)
self.assertIn(
'New Password and Current Password cannot be same.',
str(response.data['new_password']),
)

# Password successfully changed
new_password_payload = {
'new_password1': 'test_new_password',
'new_password2': 'test_new_password',
'current_password': 'test_password',
'new_password': 'test_new_password',
'confirm_password': 'test_new_password',
}
response = client.post(password_change_url, data=new_password_payload)
self.assertEqual(response.status_code, 200)
self.assertIn('New password has been saved.', str(response.data['detail']))
self.assertIn('Password updated successfully', str(response.data['message']))

# user should not be able to login using old password
login_response = self.client.post(login_url, data=login_payload)
self.assertEqual(login_response.status_code, 400)

# new password should work
login_payload['password'] = new_password_payload['new_password1']
login_payload['password'] = new_password_payload['new_password']
login_response = self.client.post(login_url, data=login_payload)
token = login_response.json()['key']
self.assertEqual(login_response.status_code, 200)
Expand Down

0 comments on commit c8e628d

Please sign in to comment.