def test_detokenize_with_expired_token_fails(self): """Test token decoding with no token""" with pytest.raises(CustomError) as e_info: token = Encryption.tokenize(USER_DATA, subject='Testing', minutes=-10) Encryption.detokenize(token)
def test_password_verify_succeeds(self): """Test that the password verify works""" hashed_password = Encryption.hash('password123') is_verify = Encryption.verify(hashed_password, 'password123') assert type(hashed_password) == str assert is_verify
def test_password_verify_fails(self): """Test that the password verify fails when a wrong password is provided""" hashed_password = Encryption.hash('password123') is_verify = Encryption.verify(hashed_password, 'password1234') assert type(hashed_password) == str assert not is_verify
def test_detokenize_succeeds(self): """Test token decoding""" token = Encryption.tokenize(USER_DATA, subject='Testing', minutes=10) decoded_token = Encryption.detokenize(token) assert decoded_token['data'] == DECODED_TOKEN['data'] assert decoded_token['aud'] == DECODED_TOKEN['aud'] assert decoded_token['iss'] == DECODED_TOKEN['iss'] assert decoded_token['sub'] == DECODED_TOKEN['sub']
def generate_link(request, data, type='email_verification'): """Generate the link based on the type provided. Args: request (object): Request object data (dict): User data type (str): The type of token to be generated Examples: 1. email_verification 2. password_reset Returns: Tuple: A link and the token. """ # gets the root url url_root = request.url_root token_payload = { 'email': data['email'], } # generates a token token = Encryption.tokenize(token_payload, subject=type, minutes=10) # maps the methods with link type mapper = { 'password_reset': password_reset_link(url_root, token), 'email_verification': email_verification_link(url_root, token) } return mapper[type]
def post(self): """Post request for user login""" # initialize the schema schema = UserSchema() # get the request details as json request_json = request.get_json() # serialize and find the user data in the database user_details = schema.load_into_schema(request_json, partial=True) found_user = User.query_(email=user_details['email'], deleted=False)\ .first() # throw an error if not found if not found_user: return {'status': 'error', 'message': MESSAGES['NOT_FOUND']}, 404 # deserialize the user data if found, and verify the password user = schema.dump(found_user).data is_match = Encryption.verify(user['password'], user_details['password']) # if password did not match throw an error if not is_match: return { 'status': 'error', 'message': MESSAGES['UNAUTHORIZED'] }, 401 else: # format the data and generate a JWT token. formatted_data = format_user(user) token = Encryption.tokenize( formatted_data, subject='User_Login', days=14) return { 'status': 'success', 'message': MESSAGES['LOGIN'], 'data': { 'token': token, } }, 200
def patch(self): """patch request to reset the user's password""" # instantiate the user schema user_schema = UserSchema() # get the verification_token from the request params reset_token = request.args.get('token') # get the request data in json format request_json = request.get_json() # check if the token is valid # serialize the request data and # look for the token in the database Encryption.detokenize(reset_token) user_details = user_schema.load_into_schema(request_json, partial=True) found_token = User.query_(password_reset=reset_token).first() # throw an error if not found if not found_token: return { 'status': 'error', 'message': MESSAGES['RESET_LINK_RESEND'] }, 404 else: # set the password reset column to none user_details['password_reset'] = None # hash the new password and update the password user_details['password'] = Encryption.hash( user_details['password']) found_token.update_(**user_details) return { 'status': 'success', 'message': MESSAGES['PROCEED_TO_LOGIN'].format( 'Your password has been changed') }, 200
def test_generate_link_for_password_reset_succeeds(self): """Test the generate link method""" data = {'email': USER_DATA['email']} actual_behaviour = generate_link(Request, data, type='password_reset') decoded_token = Encryption.detokenize(actual_behaviour[1]) assert isinstance(actual_behaviour, tuple) assert len(actual_behaviour) == 2 assert decoded_token['sub'] == 'password_reset'
def get(self): """Get request to verify the user's emails""" # get the verification_token from the request params verification_token = request.args.get('token') # check if the token is valid and look for the token in the database Encryption.detokenize(verification_token) found_token = User.query_(token=verification_token).first() # throw an error if not found else update and return a success message if not found_token: return {'status': 'error', 'message': MESSAGES['VERIFIED']}, 409 else: found_token.update_(token=None, verified=True) return { 'status': 'success', 'message': MESSAGES['PROCEED_TO_LOGIN'].format( 'Your account has been successfully verified'), }, 200
def post(self): """Post request for user registration""" # instantiate the user schema user_schema = UserSchema() # get the request data in json format request_json = request.get_json() # serialize and validate the request user_details = user_schema.load_into_schema(request_json, partial=True) user_details['first_name'] = user_details['first_name'].strip() user_details['last_name'] = user_details['last_name'].strip() # check if the user already exist found_user = User.query_(email=user_details['email']).first() if found_user: return { 'status': 'error', 'message': MESSAGES['DUPLICATES'].format(found_user.email) }, 409 # hash password user_details['password'] = Encryption.hash(user_details['password']) # generate a random unique username user_details['username'] = push_id.next_id()[8:] link, user_details['token'] = generate_link(request, user_details) # save to database and serialize the returned user data saved_user = User(**user_details).save() # if the save was successful, send out the verification email and # return a success message. if saved_user: MailGun.with_api( saved_user.email, email_verification.format(style, link, 'Verification Link'), 'Email verification') return { 'status': 'success', 'message': MESSAGES['REGISTER'], }, 201
from api.utilities.encryption import Encryption from api.utilities.push_id import PushID USER_DATA = { 'firstname': 'Test', 'lastname': 'User', 'email': '*****@*****.**', 'password': '******', } FIXTURE_NEW_USER = { 'first_name': 'Test', 'last_name': 'User', 'username': PushID().next_id()[8:], 'email': '*****@*****.**', 'password': Encryption.hash('Password@1234'), 'verified': True, 'token': None, 'password_reset': None } FIXTURE_NEW_USER_TWO = { 'first_name': 'Test', 'last_name': 'User', 'username': PushID().next_id()[8:], 'email': '*****@*****.**', 'password':
def _encrypt(payload, subject, **kwargs): return Encryption.tokenize(payload, subject=subject, **kwargs)
def test_detokenize_with_no_token_fails(self): """Test token decoding with no token""" with pytest.raises(CustomError) as e_info: Encryption.detokenize(None)
def test_tokenize_succeeds(self): """Test token generation""" token = Encryption.tokenize(USER_DATA, subject='Testing', minutes=10) assert type(token) == str
class TestEncryptionClass: """Tests encryption class""" def test_password_hash_succeeds(self): """Test that passwords are hashed""" hashed_password = Encryption.hash('password123') assert type(hashed_password) == str assert len(hashed_password) == 192 def test_password_verify_succeeds(self): """Test that the password verify works""" hashed_password = Encryption.hash('password123') is_verify = Encryption.verify(hashed_password, 'password123') assert type(hashed_password) == str assert is_verify def test_password_verify_fails(self): """Test that the password verify fails when a wrong password is provided""" hashed_password = Encryption.hash('password123') is_verify = Encryption.verify(hashed_password, 'password1234') assert type(hashed_password) == str assert not is_verify def test_tokenize_succeeds(self): """Test token generation""" token = Encryption.tokenize(USER_DATA, subject='Testing', minutes=10) assert type(token) == str def test_detokenize_succeeds(self): """Test token decoding""" token = Encryption.tokenize(USER_DATA, subject='Testing', minutes=10) decoded_token = Encryption.detokenize(token) assert decoded_token['data'] == DECODED_TOKEN['data'] assert decoded_token['aud'] == DECODED_TOKEN['aud'] assert decoded_token['iss'] == DECODED_TOKEN['iss'] assert decoded_token['sub'] == DECODED_TOKEN['sub'] def test_detokenize_with_no_token_fails(self): """Test token decoding with no token""" with pytest.raises(CustomError) as e_info: Encryption.detokenize(None) def test_detokenize_with_expired_token_fails(self): """Test token decoding with no token""" with pytest.raises(CustomError) as e_info: token = Encryption.tokenize(USER_DATA, subject='Testing', minutes=-10) Encryption.detokenize(token)