Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ DJANGO_REST_LOOKUP_FIELD = 'custom_email_field'
```
into Django settings.py file.

## Configurable Response

To configurable response messages need to add following properties into settings.py

```python
PASSWORD_CHANGED = 'Your password change message'
TOKEN_EXPIRED = 'Your token expired'
PASSWORD_REQUEST_ACCEPT = 'Your password valid request message'
TOKEN_NOT_FOUND = 'Your token not found message'
TOKEN_VALID = 'Your token valid message'
```

## Custom Remote IP Address and User Agent Header Lookup

If your setup demands that the IP adress of the user is in another header (e.g., 'X-Forwarded-For'), you can configure that (using Django Request Headers):
Expand Down Expand Up @@ -334,6 +346,8 @@ Apparently, the following piece of code in the Django Model prevents MongodB fro

See issue #49 for details.



## Contributions

This library tries to follow the unix philosophy of "do one thing and do it well" (which is providing a basic password reset endpoint for Django Rest Framework). Contributions are welcome in the form of pull requests and issues! If you create a pull request, please make sure that you are not introducing breaking changes.
Expand Down
50 changes: 41 additions & 9 deletions django_rest_passwordreset/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,31 @@ def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
token = serializer.validated_data['token']

response_dict = dict({"status_code": None, "status":None, "message": None})
# get token validation time
password_reset_token_validation_time = get_password_reset_token_expiry_time()

# find token
reset_password_token = ResetPasswordToken.objects.filter(key=token).first()

if reset_password_token is None:
return Response({'status': 'notfound'}, status=status.HTTP_404_NOT_FOUND)
message = get_response_message("TOKEN_NOT_FOUND")
response_dict.update({"status_code": 404, "status": "notfound", "message": message})
return Response(response_dict, status=status.HTTP_404_NOT_FOUND)

# check expiry date
expiry_date = reset_password_token.created_at + timedelta(hours=password_reset_token_validation_time)

if timezone.now() > expiry_date:
# delete expired token
reset_password_token.delete()
return Response({'status': 'expired'}, status=status.HTTP_404_NOT_FOUND)

return Response({'status': 'OK'})
message = get_response_message("TOKEN_EXPIRED")
response_dict.update({"status_code": 401, 'status': 'expired', "message": message})
return Response(response_dict, status=status.HTTP_404_NOT_FOUND)

message = get_response_message("TOKEN_VALID")
response_dict.update({"status_code": 200, 'status': 'OK', "message": message})
return Response(response_dict)


class ResetPasswordConfirm(GenericAPIView):
Expand All @@ -75,6 +81,7 @@ def post(self, request, *args, **kwargs):
serializer.is_valid(raise_exception=True)
password = serializer.validated_data['password']
token = serializer.validated_data['token']
response_dict = dict({"status_code": None, "message": None, "status": None})

# get token validation time
password_reset_token_validation_time = get_password_reset_token_expiry_time()
Expand All @@ -83,15 +90,20 @@ def post(self, request, *args, **kwargs):
reset_password_token = ResetPasswordToken.objects.filter(key=token).first()

if reset_password_token is None:
return Response({'status': 'notfound'}, status=status.HTTP_404_NOT_FOUND)
message = get_response_message("TOKEN_NOT_FOUND")
response_dict.update({"status_code": 404, 'status': 'notfound', "message": message})
return Response(response_dict, status=status.HTTP_404_NOT_FOUND)

# check expiry date
expiry_date = reset_password_token.created_at + timedelta(hours=password_reset_token_validation_time)

if timezone.now() > expiry_date:
# delete expired token
reset_password_token.delete()
return Response({'status': 'expired'}, status=status.HTTP_404_NOT_FOUND)
message = get_response_message("TOKEN_EXPIRED")
response_dict.update({"status_code": 404, 'status': 'expired', "message": message})

return Response(response_dict, status=status.HTTP_404_NOT_FOUND)

# change users password (if we got to this code it means that the user is_active)
if reset_password_token.user.eligible_for_reset():
Expand All @@ -116,7 +128,11 @@ def post(self, request, *args, **kwargs):
# Delete all password reset tokens for this user
ResetPasswordToken.objects.filter(user=reset_password_token.user).delete()

return Response({'status': 'OK'})
# done
message = get_response_message("PASSWORD_CHANGED")
response_dict.update({"status_code": 200, "status": "OK", "message": message})

return Response(response_dict)


class ResetPasswordRequestToken(GenericAPIView):
Expand Down Expand Up @@ -159,6 +175,7 @@ def post(self, request, *args, **kwargs):
# but not if DJANGO_REST_PASSWORDRESET_NO_INFORMATION_LEAKAGE == True
if not active_user_found and not getattr(settings, 'DJANGO_REST_PASSWORDRESET_NO_INFORMATION_LEAKAGE', False):
raise exceptions.ValidationError({

'email': [_(
"There is no active user associated with this e-mail address or the password can not be changed")],
})
Expand All @@ -185,7 +202,22 @@ def post(self, request, *args, **kwargs):
# let whoever receives this signal handle sending the email for the password reset
reset_password_token_created.send(sender=self.__class__, instance=self, reset_password_token=token)
# done
return Response({'status': 'OK'})
message = get_response_message("PASSWORD_REQUEST_ACCEPT")
return Response({"status_code": 200, "status": "OK", "message": message})


def get_response_message(message_key):
"""
A function return status message based on conditions
conditions: if message attribute define in settings then it will
return user define message otherwise, return static defined message
params: message_key
return: status_message
"""

status_message = getattr(settings, message_key, message_key)

return status_message


reset_password_validate_token = ResetPasswordValidateToken.as_view()
Expand Down