From 3e9e157e3a0e4df829c008fda865e6b1353d05f4 Mon Sep 17 00:00:00 2001 From: "suraj.raktate" Date: Mon, 12 Aug 2019 12:26:53 +0530 Subject: [PATCH 01/10] Add Suraj Raktate Contributors list --- Contributors.md | 11 +++++++ django_rest_passwordreset/views.py | 50 ++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 Contributors.md diff --git a/Contributors.md b/Contributors.md new file mode 100644 index 0000000..293a214 --- /dev/null +++ b/Contributors.md @@ -0,0 +1,11 @@ +### Response messages configuration + +To cofigurable 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' +``` \ No newline at end of file diff --git a/django_rest_passwordreset/views.py b/django_rest_passwordreset/views.py index 829d94c..615a153 100644 --- a/django_rest_passwordreset/views.py +++ b/django_rest_passwordreset/views.py @@ -41,7 +41,7 @@ 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, "message": None}) # get token validation time password_reset_token_validation_time = get_password_reset_token_expiry_time() @@ -49,7 +49,9 @@ 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, "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) @@ -57,9 +59,13 @@ def post(self, request, *args, **kwargs): 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("TOEKN_EXPIRED") + response_dict.update({"status_code": 401, "message": message}) + return Response(response_dict, status=status.HTTP_404_NOT_FOUND) + + message = get_response_message("TOKEN_VALID") + response_dict.update({"status_code": 200, "message": message}) + return Response(response_dict) class ResetPasswordConfirm(GenericAPIView): @@ -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}) # get token validation time password_reset_token_validation_time = get_password_reset_token_expiry_time() @@ -83,7 +90,9 @@ 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, "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) @@ -91,7 +100,10 @@ def post(self, request, *args, **kwargs): 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("TOEKN_EXPIRED") + response_dict.update({"status_code": 401, "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(): @@ -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, "message": message}) + + return Response(response_dict) class ResetPasswordRequestToken(GenericAPIView): @@ -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")], }) @@ -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, "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() From d7429b68436b81ab65f95c22c767a795dc83d5cc Mon Sep 17 00:00:00 2001 From: "suraj.raktate" Date: Mon, 12 Aug 2019 14:07:05 +0530 Subject: [PATCH 02/10] re-introducing 'status' field in response and reame.md file changed --- README.md | 12 ++++++++++++ django_rest_passwordreset/views.py | 16 ++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 42a77a6..b140b6d 100644 --- a/README.md +++ b/README.md @@ -334,6 +334,18 @@ Apparently, the following piece of code in the Django Model prevents MongodB fro See issue #49 for details. +### cofigurable Response + +To cofigurable 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' +``` + ## 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. diff --git a/django_rest_passwordreset/views.py b/django_rest_passwordreset/views.py index 615a153..e243d98 100644 --- a/django_rest_passwordreset/views.py +++ b/django_rest_passwordreset/views.py @@ -50,7 +50,7 @@ def post(self, request, *args, **kwargs): if reset_password_token is None: message = get_response_message("TOKEN_NOT_FOUND") - response_dict.update({"status_code": 404, "message": message}) + response_dict.update({"status_code": 404, "status": "notfound", "message": message}) return Response(response_dict, status=status.HTTP_404_NOT_FOUND) # check expiry date @@ -60,11 +60,11 @@ def post(self, request, *args, **kwargs): # delete expired token reset_password_token.delete() message = get_response_message("TOEKN_EXPIRED") - response_dict.update({"status_code": 401, "message": message}) + 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, "message": message}) + response_dict.update({"status_code": 200, 'status': 'OK', "message": message}) return Response(response_dict) @@ -81,7 +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}) + 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() @@ -91,7 +91,7 @@ def post(self, request, *args, **kwargs): if reset_password_token is None: message = get_response_message("TOKEN_NOT_FOUND") - response_dict.update({"status_code": 404, "message": message}) + response_dict.update({"status_code": 404, 'status': 'notfound', "message": message}) return Response(response_dict, status=status.HTTP_404_NOT_FOUND) # check expiry date @@ -101,7 +101,7 @@ def post(self, request, *args, **kwargs): # delete expired token reset_password_token.delete() message = get_response_message("TOEKN_EXPIRED") - response_dict.update({"status_code": 401, "message": message}) + response_dict.update({"status_code": 404, 'status': 'expired', "message": message}) return Response(response_dict, status=status.HTTP_404_NOT_FOUND) @@ -130,7 +130,7 @@ def post(self, request, *args, **kwargs): # done message = get_response_message("PASSWORD_CHANGED") - response_dict.update({"status_code": 200, "message": message}) + response_dict.update({"status_code": 200, "status": "OK", "message": message}) return Response(response_dict) @@ -203,7 +203,7 @@ def post(self, request, *args, **kwargs): reset_password_token_created.send(sender=self.__class__, instance=self, reset_password_token=token) # done message = get_response_message("PASSWORD_REQUEST_ACCEPT") - return Response({"status_code": 200, "message": message}) + return Response({"status_code": 200, "status": "OK", "message": message}) def get_response_message(message_key): From dee6a6e98fbb332ff978f895ea00b94eebe225a5 Mon Sep 17 00:00:00 2001 From: "suraj.raktate" Date: Mon, 12 Aug 2019 14:13:42 +0530 Subject: [PATCH 03/10] typo fixed TOEKN -> TOKEN --- django_rest_passwordreset/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django_rest_passwordreset/views.py b/django_rest_passwordreset/views.py index e243d98..f9dda79 100644 --- a/django_rest_passwordreset/views.py +++ b/django_rest_passwordreset/views.py @@ -59,7 +59,7 @@ def post(self, request, *args, **kwargs): if timezone.now() > expiry_date: # delete expired token reset_password_token.delete() - message = get_response_message("TOEKN_EXPIRED") + 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) @@ -100,7 +100,7 @@ def post(self, request, *args, **kwargs): if timezone.now() > expiry_date: # delete expired token reset_password_token.delete() - message = get_response_message("TOEKN_EXPIRED") + 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) From 3a47142cc6eb2ff8afb760c94b2ae061d95f2585 Mon Sep 17 00:00:00 2001 From: "suraj.raktate" Date: Mon, 12 Aug 2019 14:18:50 +0530 Subject: [PATCH 04/10] Readme.md file changes done --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b140b6d..dd7c566 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,18 @@ DJANGO_REST_LOOKUP_FIELD = 'custom_email_field' ``` into Django settings.py file. +## Cofigurable Response + +To cofigurable 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): @@ -334,17 +346,7 @@ Apparently, the following piece of code in the Django Model prevents MongodB fro See issue #49 for details. -### cofigurable Response - -To cofigurable 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' -``` ## Contributions From c93b9890ad5b21e5f5d9a19d8795685dfde2334f Mon Sep 17 00:00:00 2001 From: "suraj.raktate" Date: Mon, 12 Aug 2019 14:19:12 +0530 Subject: [PATCH 05/10] Readme.md file changes done --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd7c566..b3d88eb 100644 --- a/README.md +++ b/README.md @@ -149,9 +149,9 @@ DJANGO_REST_LOOKUP_FIELD = 'custom_email_field' ``` into Django settings.py file. -## Cofigurable Response +## Configurable Response -To cofigurable response messages need to add following properties into settings.py +To configurable response messages need to add following properties into settings.py ```python PASSWORD_CHANGED = 'Your password change message' From 0846d8d51fb1ca586a9f6b1da4d1971c9a2f2c1a Mon Sep 17 00:00:00 2001 From: Suraj Raktate <34839549+surajraktate@users.noreply.github.com> Date: Fri, 16 Aug 2019 10:43:10 +0530 Subject: [PATCH 06/10] Delete Contributors.md --- Contributors.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 Contributors.md diff --git a/Contributors.md b/Contributors.md deleted file mode 100644 index 293a214..0000000 --- a/Contributors.md +++ /dev/null @@ -1,11 +0,0 @@ -### Response messages configuration - -To cofigurable 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' -``` \ No newline at end of file From 1e0431df0b85860152ad2c4ce5666faa8d015ad0 Mon Sep 17 00:00:00 2001 From: Suraj Raktate <34839549+surajraktate@users.noreply.github.com> Date: Fri, 16 Aug 2019 10:51:02 +0530 Subject: [PATCH 07/10] reintroduce status field at initialising dict --- django_rest_passwordreset/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_rest_passwordreset/views.py b/django_rest_passwordreset/views.py index f9dda79..262e44e 100644 --- a/django_rest_passwordreset/views.py +++ b/django_rest_passwordreset/views.py @@ -41,7 +41,7 @@ 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, "message": None}) + 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() From cc16d1015e7547152c50c017e803c62437da1ef7 Mon Sep 17 00:00:00 2001 From: surajraktate Date: Thu, 11 Feb 2021 10:08:17 +0530 Subject: [PATCH 08/10] configurable message text changes, get_response_message function intro changes --- README.md | 13 ++++++------- django_rest_passwordreset/views.py | 11 +++++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b3d88eb..47e10aa 100644 --- a/README.md +++ b/README.md @@ -151,14 +151,13 @@ into Django settings.py file. ## Configurable Response -To configurable response messages need to add following properties into settings.py - +For optional configurable response messages, add the following properties to 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' +PASSWORD_CHANGED = 'Your password has been changed' +TOKEN_EXPIRED = 'Password reset link is invalid or expired' +PASSWORD_REQUEST_ACCEPT = 'Please check your email for the reset password link' +TOKEN_NOT_FOUND = 'Requested token is invalid' +TOKEN_VALID = 'Requested token is valid' ``` ## Custom Remote IP Address and User Agent Header Lookup diff --git a/django_rest_passwordreset/views.py b/django_rest_passwordreset/views.py index 262e44e..9d69e93 100644 --- a/django_rest_passwordreset/views.py +++ b/django_rest_passwordreset/views.py @@ -41,7 +41,7 @@ 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}) + 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() @@ -208,11 +208,10 @@ def post(self, request, *args, **kwargs): 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 + If message_key exists in settings.py, the corresponding message will be returned. Otherwise message_key + is returned + :param message_key: Key of the message in settings.py + :return: status_message """ status_message = getattr(settings, message_key, message_key) From 9b706f2c095ae1175ffee08f72bc21cf9fb9fedd Mon Sep 17 00:00:00 2001 From: surajraktate Date: Thu, 11 Feb 2021 10:21:41 +0530 Subject: [PATCH 09/10] response messages changes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 47e10aa..fca932d 100644 --- a/README.md +++ b/README.md @@ -154,10 +154,10 @@ into Django settings.py file. For optional configurable response messages, add the following properties to settings.py ```python PASSWORD_CHANGED = 'Your password has been changed' -TOKEN_EXPIRED = 'Password reset link is invalid or expired' +TOKEN_EXPIRED = 'Your token has expired' PASSWORD_REQUEST_ACCEPT = 'Please check your email for the reset password link' -TOKEN_NOT_FOUND = 'Requested token is invalid' -TOKEN_VALID = 'Requested token is valid' +TOKEN_NOT_FOUND = 'Your token is invalid' +TOKEN_VALID = 'Your token is valid' ``` ## Custom Remote IP Address and User Agent Header Lookup From d0797655666201d0b9e8ad18b72b663a439275ed Mon Sep 17 00:00:00 2001 From: surajraktate Date: Thu, 11 Feb 2021 13:44:28 +0530 Subject: [PATCH 10/10] added missing password_reset_token_validation_time --- django_rest_passwordreset/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/django_rest_passwordreset/views.py b/django_rest_passwordreset/views.py index c14e900..d04ba58 100644 --- a/django_rest_passwordreset/views.py +++ b/django_rest_passwordreset/views.py @@ -83,6 +83,9 @@ def post(self, request, *args, **kwargs): 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() + # find token reset_password_token = ResetPasswordToken.objects.filter(key=token).first()