From c156244a69457c7466af17e87eddee7825ac2b01 Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Fri, 23 Mar 2018 09:41:11 -0700 Subject: [PATCH 1/7] Add missing SECRET_HASH key when responding to the NEW_PASSWORD_REQUIRED_CHALLENGE --- warrant/aws_srp.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/warrant/aws_srp.py b/warrant/aws_srp.py index 3b53f34e..6878c9d0 100644 --- a/warrant/aws_srp.py +++ b/warrant/aws_srp.py @@ -239,6 +239,10 @@ def set_new_password_challenge(self, new_password, client=None): 'USERNAME': auth_params['USERNAME'], 'NEW_PASSWORD': new_password } + + if self.client_secret is not None: + challenge_response['SECRET_HASH'] = self.get_secret_hash(self.username, self.client_id, self.client_secret) + new_password_response = boto_client.respond_to_auth_challenge( ClientId=self.client_id, ChallengeName=self.NEW_PASSWORD_REQUIRED_CHALLENGE, From 3dee8aa58cffee6f5b554df76767edd83152d959 Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Mon, 26 Mar 2018 20:38:43 -0700 Subject: [PATCH 2/7] For challenge responses to username alias, use the USER_ID_FOR_SRP attribute as a challenge response paramter and to calculate the SECRET_HASH. --- warrant/__init__.py | 6 ++++++ warrant/aws_srp.py | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/warrant/__init__.py b/warrant/__init__.py index 2a0c2313..e785b25d 100644 --- a/warrant/__init__.py +++ b/warrant/__init__.py @@ -365,6 +365,12 @@ def admin_authenticate(self, password): AuthParameters=auth_params, ) + if tokens.get('ChallengeName') is not None: + if tokens.get('ChallengeName') == aws_srp.AWSSRP.NEW_PASSWORD_REQUIRED_CHALLENGE: + raise aws_srp.ForceChangePasswordException('Change password before authenticating') + else: + raise NotImplementedError('The %s challenge is not supported' % tokens.get('ChallengeName')) + self.verify_token(tokens['AuthenticationResult']['IdToken'], 'id_token','id') self.refresh_token = tokens['AuthenticationResult']['RefreshToken'] self.verify_token(tokens['AuthenticationResult']['AccessToken'], 'access_token','access') diff --git a/warrant/aws_srp.py b/warrant/aws_srp.py index 6878c9d0..686092f2 100644 --- a/warrant/aws_srp.py +++ b/warrant/aws_srp.py @@ -194,7 +194,7 @@ def process_challenge(self, challenge_parameters): if self.client_secret is not None: response.update({ "SECRET_HASH": - self.get_secret_hash(self.username, self.client_id, self.client_secret)}) + self.get_secret_hash(user_id_for_srp, self.client_id, self.client_secret)}) return response def authenticate_user(self, client=None): @@ -235,13 +235,19 @@ def set_new_password_challenge(self, new_password, client=None): ChallengeResponses=challenge_response) if tokens['ChallengeName'] == self.NEW_PASSWORD_REQUIRED_CHALLENGE: + challenge_parameters = response['ChallengeParameters'] + user_id_for_srp = challenge_parameters.get('USER_ID_FOR_SRP') + + if user_id_for_srp is None: + user_id_for_srp = self.username + challenge_response = { - 'USERNAME': auth_params['USERNAME'], + 'USERNAME': user_id_for_srp, 'NEW_PASSWORD': new_password } if self.client_secret is not None: - challenge_response['SECRET_HASH'] = self.get_secret_hash(self.username, self.client_id, self.client_secret) + challenge_response['SECRET_HASH'] = self.get_secret_hash(user_id_for_srp, self.client_id, self.client_secret) new_password_response = boto_client.respond_to_auth_challenge( ClientId=self.client_id, From 341ad3875f581341746da3423897990ef64af35b Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Wed, 28 Mar 2018 00:03:17 -0700 Subject: [PATCH 3/7] Allow use of delivery methods. Allow for optional password. --- warrant/__init__.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/warrant/__init__.py b/warrant/__init__.py index e785b25d..8eb7f6de 100644 --- a/warrant/__init__.py +++ b/warrant/__init__.py @@ -503,7 +503,7 @@ def admin_get_user(self, attr_map=None): attribute_list=user.get('UserAttributes'), metadata=user_metadata,attr_map=attr_map) - def admin_create_user(self, username, temporary_password='', attr_map=None, **kwargs): + def admin_create_user(self, username, temporary_password='', attr_map=None, email_delivery=False, sms_delivery=False, **kwargs): """ Create a user using admin super privileges. :param username: User Pool username @@ -513,12 +513,20 @@ def admin_create_user(self, username, temporary_password='', attr_map=None, **kw :param kwargs: Additional User Pool attributes :return response: Response from Cognito """ - response = self.client.admin_create_user( - UserPoolId=self.user_pool_id, - Username=username, - UserAttributes=dict_to_cognito(kwargs, attr_map), - TemporaryPassword=temporary_password, - ) + delivery_mediums = [] + if email_delivery: + delivery_mediums.append("EMAIL") + if sms_delivery: + delivery_mediums.append("SMS") + args = { + 'UserPoolId': self.user_pool_id, + 'Username': username, + 'UserAttributes': dict_to_cognito(kwargs, attr_map), + 'DesiredDeliveryMediums': delivery_mediums + } + if temporary_password is not None: + args['TemporaryPassword'] = temporary_password + response = self.client.admin_create_user(**args) kwargs.update(username=username) self._set_attributes(response, kwargs) From a50e3e18dda2abd8a2e1d6640d2c2ceadc7733ce Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Thu, 12 Apr 2018 09:45:38 -0700 Subject: [PATCH 4/7] Add admin reset password --- warrant/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/warrant/__init__.py b/warrant/__init__.py index 8eb7f6de..b26d0fd3 100644 --- a/warrant/__init__.py +++ b/warrant/__init__.py @@ -533,6 +533,20 @@ def admin_create_user(self, username, temporary_password='', attr_map=None, emai response.pop('ResponseMetadata') return response + def admin_reset_password(self, username): + """ + Reset a password using admin super privileges. + :param username: User Pool username + :return response: Response from Cognito + """ + args = { + 'UserPoolId': self.user_pool_id, + 'Username': username + } + response = self.client.admin_reset_user_password(**args) + response.pop('ResponseMetadata') + return response + def send_verification(self, attribute='email'): """ Sends the user an attribute verification code for the specified attribute name. From 6496b67b4815f6b7bf40412cf2fca309204c9f43 Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Fri, 11 May 2018 14:11:43 -0700 Subject: [PATCH 5/7] Fix for refreshing tokens without failing the token check. --- warrant/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/warrant/__init__.py b/warrant/__init__.py index b26d0fd3..cc65c768 100644 --- a/warrant/__init__.py +++ b/warrant/__init__.py @@ -258,6 +258,7 @@ def check_token(self, renew=True): expired = True if renew: self.renew_access_token() + expired = self.check_token(renew=False) else: expired = False return expired @@ -576,7 +577,9 @@ def renew_access_token(self): Sets a new access token on the User using the refresh token. """ auth_params = {'REFRESH_TOKEN': self.refresh_token} + self._add_secret_hash(auth_params, 'SECRET_HASH') + refresh_response = self.client.initiate_auth( ClientId=self.client_id, AuthFlow='REFRESH_TOKEN', From a44677b9220cf7aa1ba1eebc5a0eac2ef6dd7211 Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Mon, 16 Jul 2018 12:27:03 -0700 Subject: [PATCH 6/7] Update requirements to use latest python-jose --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 48b028b2..e6b73d46 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.4.3 envs>=0.3.0 -python-jose-cryptodome>=1.3.2 +python-jose[pycryptodome]>=3.0.0 requests>=2.13.0 From a7cc6f13ee3dc92a4d62653eff4f88ba3cbf8c32 Mon Sep 17 00:00:00 2001 From: Jose Rojas Date: Thu, 6 Dec 2018 14:06:17 -0800 Subject: [PATCH 7/7] Fixes for pip >= 10.0 --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f51d154c..90f61cc5 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,11 @@ import os from setuptools import setup, find_packages -from pip.req import parse_requirements + +try: # for pip >= 10 + from pip._internal.req import parse_requirements +except ImportError: # for pip <= 9.0.3 + from pip.req import parse_requirements install_reqs = parse_requirements('requirements.txt', session=False) test_reqs = parse_requirements('requirements_test.txt', session=False)