def test_login_with_email_password_inactive_user(self): """Inactive user logging in with username and password.""" db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' # Inactive user add_user( db=db, new_user=NewUser( email=email, full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=False, password=password, password_repeat=password, activation_url='https://activate.com/activate', ), ) with pytest.raises(McAuthLoginException) as ex: login_with_email_password(db=db, email=email, password=password) # Make sure the error message explicitly states that login failed due to user not being active assert 'not active' in str(ex)
def test_send_password_reset_token(self): db = connect_to_db() email = '*****@*****.**' password = '******' password_reset_link = 'http://password-reset.com/' add_user( db=db, new_user=NewUser( email=email, full_name='Test user login', has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ), ) # Existing user send_password_reset_token(db=db, email=email, password_reset_link=password_reset_link) # Nonexisting user (call shouldn't fail because we don't want to reveal which users are in the system so we # pretend that we've sent the email) send_password_reset_token(db=db, email='*****@*****.**', password_reset_link=password_reset_link)
def test_login_with_api_key_inactive_user(self): """Inactive user logging in with API key.""" db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' ip_address = '1.2.3.4' add_user( db=db, new_user=NewUser( email=email, full_name=full_name, notes='Test test test', role_ids=[1], active=False, password=password, password_repeat=password, activation_url='https://activate.com/activate', ), ) user = user_info(db=db, email=email) assert user global_api_key = user.global_api_key() with pytest.raises(McAuthLoginException) as ex: login_with_api_key(db=db, api_key=global_api_key, ip_address=ip_address) # Make sure the error message explicitly states that login failed due to user not being active assert 'not active' in str(ex)
def test_regenerate_api_key(self): db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' ip_address = '1.2.3.4' add_user( db=db, new_user=NewUser( email=email, full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ), ) # Get sample API keys user = login_with_email_password(db=db, email=email, password=password, ip_address=ip_address) assert user before_global_api_key = user.global_api_key() assert before_global_api_key before_per_ip_api_key = user.api_key_for_ip_address( ip_address=ip_address) assert before_per_ip_api_key assert before_global_api_key != before_per_ip_api_key # Regenerate API key, purge per-IP API keys regenerate_api_key(db=db, email=email) # Get sample API keys again user = login_with_email_password(db=db, email=email, password=password, ip_address=ip_address) assert user after_global_api_key = user.global_api_key() assert after_global_api_key after_per_ip_api_key = user.api_key_for_ip_address( ip_address=ip_address) assert after_per_ip_api_key # Make sure API keys are different assert before_global_api_key != after_global_api_key assert before_per_ip_api_key != after_per_ip_api_key
def test_login_with_api_key(): db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' ip_address = '1.2.3.4' add_user( db=db, new_user=NewUser( email=email, full_name=full_name, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ), ) # Get sample API keys user = login_with_email_password(db=db, email=email, password=password, ip_address=ip_address) assert user global_api_key = user.global_api_key() assert global_api_key per_ip_api_key = user.api_key_for_ip_address(ip_address=ip_address) assert per_ip_api_key assert global_api_key != per_ip_api_key # Non-existent API key with pytest.raises(McAuthLoginException): login_with_api_key(db=db, api_key='Non-existent API key', ip_address=ip_address) # Global API key user = login_with_api_key(db=db, api_key=global_api_key, ip_address=ip_address) assert user assert user.email() == email assert user.global_api_key() == global_api_key # Per-IP API key user = login_with_api_key(db=db, api_key=per_ip_api_key, ip_address=ip_address) assert user assert user.email() == email
def test_all_users(): db = connect_to_db() email = '*****@*****.**' full_name = 'Test user info' notes = 'Test test test' weekly_requests_limit = 123 weekly_requested_items_limit = 456 max_topic_stories = 789 add_user( db=db, new_user=NewUser( email=email, full_name=full_name, has_consented=True, notes=notes, role_ids=[1], active=True, password='******', password_repeat='user_info', activation_url='', # user is active, no need for activation URL resource_limits=Resources( weekly_requests=weekly_requests_limit, weekly_requested_items=weekly_requested_items_limit, max_topic_stories=max_topic_stories, ), ), ) users = all_users(db=db) assert len(users) == 1 user = users[0] assert isinstance(user, CurrentUser) assert user.user_id() assert user.email() == email assert user.full_name() == full_name assert user.notes() == notes assert user.resource_limits() assert user.resource_limits().weekly_requests() == weekly_requests_limit assert user.resource_limits().weekly_requested_items( ) == weekly_requested_items_limit assert user.resource_limits().max_topic_stories() == max_topic_stories assert user.active() assert user.has_consented() assert user.global_api_key() assert user.password_hash() assert user.has_role('admin')
def test_login_with_email_password(): db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' add_user( db=db, new_user=NewUser( email=email, full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ), ) # Successful login user = login_with_email_password(db=db, email=email, password=password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Unsuccessful login with pytest.raises(McAuthLoginException): login_with_email_password(db=db, email=email, password='******') # Subsequent login attempt after a failed one should be delayed by 1 second with pytest.raises(McAuthLoginException): login_with_email_password(db=db, email=email, password=password) # Successful login after waiting out the delay time.sleep(2) user = login_with_email_password(db=db, email=email, password=password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name
def test_send_user_activation_token(self): db = connect_to_db() email = '*****@*****.**' password = '******' activation_url = 'http://activate.com/' subscribe_to_newsletter = True add_user( db=db, new_user=NewUser( email=email, full_name='Test user login', notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL subscribe_to_newsletter=subscribe_to_newsletter, ), ) # Existing user send_user_activation_token( db=db, email=email, activation_link=activation_url, subscribe_to_newsletter=subscribe_to_newsletter, ) # Nonexistent user (call shouldn't fail because we don't want to reveal which users are in the system so we # pretend that we've sent the email) send_user_activation_token( db=db, email='*****@*****.**', activation_link=activation_url, subscribe_to_newsletter=subscribe_to_newsletter, )
def test_user_info(): db = connect_to_db() email = '*****@*****.**' full_name = 'Test user info' notes = 'Test test test' weekly_requests_limit = 123 weekly_requested_items_limit = 456 add_user( db=db, new_user=NewUser( email=email, full_name=full_name, notes=notes, role_ids=[1], active=True, password='******', password_repeat='user_info', activation_url='', # user is active, no need for activation URL weekly_requests_limit=weekly_requests_limit, weekly_requested_items_limit=weekly_requested_items_limit, ), ) user = user_info(db=db, email=email) assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name assert user.notes() == notes assert user.weekly_requests_limit() == weekly_requests_limit assert user.weekly_requested_items_limit() == weekly_requested_items_limit assert user.active() assert user.created_date() assert __looks_like_iso8601_date(user.created_date()) assert user.global_api_key() assert user.password_hash() assert user.has_role('admin')
def test_add_user(self): db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' new_user = NewUser( email=email, full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ) # Add user add_user(db=db, new_user=new_user) # Test logging in user = login_with_email_password(db=db, email=email, password=password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Faulty input with pytest.raises(McAuthRegisterException): # noinspection PyTypeChecker add_user(db=db, new_user=None) # Existing user with pytest.raises(McAuthRegisterException): add_user(db=db, new_user=new_user) # Existing user with uppercase email with pytest.raises(McAuthRegisterException): add_user( db=db, new_user=NewUser( email=email.upper(), full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url= '', # user is active, no need for activation URL ), ) # Invalid password with pytest.raises(McAuthUserException): add_user( db=db, new_user=NewUser( email='*****@*****.**', full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password='******', password_repeat='def', activation_url= '', # user is active, no need for activation URL ), ) # Nonexistent roles with pytest.raises(McAuthRegisterException): add_user( db=db, new_user=NewUser( email='*****@*****.**', full_name=full_name, has_consented=True, notes='Test test test', role_ids=[42], active=True, password=password, password_repeat=password, activation_url= '', # user is active, no need for activation URL ), ) # Both the user is set as active and the activation URL is set with pytest.raises(McAuthUserException): add_user( db=db, new_user=NewUser( email='*****@*****.**', full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='https://activate-user.com/activate', ), ) # User is neither active not the activation URL is set with pytest.raises(McAuthUserException): add_user( db=db, new_user=NewUser( email='*****@*****.**', full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=False, password=password, password_repeat=password, activation_url='', ), )
def test_change_password_with_reset_token(self): # FIXME test changing password for user A using reset token from user B db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' add_user( db=db, new_user=NewUser( email=email, full_name=full_name, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ), ) # Successful login user = login_with_email_password(db=db, email=email, password=password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Make sure password reset token is not set password_reset_token_hash = db.query(""" SELECT password_reset_token_hash FROM auth_users WHERE email = %(email)s """, {'email': email}).flat() assert password_reset_token_hash[0] is None # Send password reset link password_reset_url = 'https://reset-password.com/reset' final_password_reset_url = _generate_password_reset_token( db=db, email=email, password_reset_link=password_reset_url, ) assert final_password_reset_url assert password_reset_url in final_password_reset_url final_password_reset_uri = furl(final_password_reset_url) assert final_password_reset_uri.args['email'] assert final_password_reset_uri.args['password_reset_token'] # Make sure password reset token is set password_reset_token_hash = db.query(""" SELECT password_reset_token_hash FROM auth_users WHERE email = %(email)s """, {'email': email}).flat() assert password_reset_token_hash[0] is not None # Change password new_password = '******' change_password_with_reset_token( db=db, email=email, password_reset_token=final_password_reset_uri.args['password_reset_token'], new_password=new_password, new_password_repeat=new_password, ) # Make sure password reset token has been reset after changing password password_reset_token_hash = db.query(""" SELECT password_reset_token_hash FROM auth_users WHERE email = %(email)s """, {'email': email}).flat() assert password_reset_token_hash[0] is None # Unsuccessful login with old password with pytest.raises(McAuthLoginException): login_with_email_password(db=db, email=email, password=password) # Imposed delay after unsuccessful login time.sleep(2) # Successful login with new password user = login_with_email_password(db=db, email=email, password=new_password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Incorrect password reset token _generate_password_reset_token( db=db, email=email, password_reset_link=password_reset_url, ) with pytest.raises(McAuthChangePasswordException): change_password_with_reset_token( db=db, email=email, password_reset_token='incorrect password reset token', new_password=new_password, new_password_repeat=new_password, ) # Changing for nonexistent user final_password_reset_url = _generate_password_reset_token( db=db, email=email, password_reset_link=password_reset_url, ) with pytest.raises(McAuthChangePasswordException): change_password_with_reset_token( db=db, email='*****@*****.**', password_reset_token=furl(final_password_reset_url).args['password_reset_token'], new_password=new_password, new_password_repeat=new_password, ) # Passwords don't match final_password_reset_url = _generate_password_reset_token( db=db, email=email, password_reset_link=password_reset_url, ) with pytest.raises(McAuthChangePasswordException): change_password_with_reset_token( db=db, email=email, password_reset_token=furl(final_password_reset_url).args['password_reset_token'], new_password='******', new_password_repeat='not match', )
def test_change_password(): db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' add_user( db=db, new_user=NewUser( email=email, full_name=full_name, has_consented=True, notes='Test test test', role_ids=[1], active=True, password=password, password_repeat=password, activation_url='', # user is active, no need for activation URL ), ) # Successful login user = login_with_email_password(db=db, email=email, password=password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Change password new_password = '******' change_password( db=db, email=email, new_password=new_password, new_password_repeat=new_password, do_not_inform_via_email=True, ) # Unsuccessful login with old password with pytest.raises(McAuthLoginException): login_with_email_password(db=db, email=email, password=password) # Imposed delay after unsuccessful login time.sleep(2) # Successful login with new password user = login_with_email_password(db=db, email=email, password=new_password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Changing for nonexistent user with pytest.raises(McAuthChangePasswordException): change_password( db=db, email='*****@*****.**', new_password=new_password, new_password_repeat=new_password, do_not_inform_via_email=True, ) # Passwords don't match with pytest.raises(McAuthChangePasswordException): change_password( db=db, email=email, new_password='******', new_password_repeat='not match', do_not_inform_via_email=True, )
def test_activate_user_via_token(self): db = connect_to_db() email = '*****@*****.**' password = '******' full_name = 'Test user login' activation_url = 'https://activate.com/activate' # Add inactive user add_user( db=db, new_user=NewUser( email=email, full_name=full_name, notes='Test test test', role_ids=[1], active=False, # not active, needs to be activated password=password, password_repeat=password, activation_url=activation_url, ), ) # Test logging in with pytest.raises(McAuthLoginException) as ex: login_with_email_password(db=db, email=email, password=password) # Make sure the error message explicitly states that login failed due to user not being active assert 'not active' in str(ex) # Make sure activation token is set activation_token_hash = db.query( """ SELECT password_reset_token_hash FROM auth_users WHERE email = %(email)s """, { 'email': email }).flat() assert activation_token_hash assert len(activation_token_hash) == 1 assert len(activation_token_hash[0]) > 0 # Send password reset link final_activation_url = _generate_user_activation_token( db=db, email=email, activation_link=activation_url, ) final_activation_uri = furl(final_activation_url) assert final_activation_uri.args['email'] activation_token = final_activation_uri.args['activation_token'] assert activation_token # Make sure activation token is (still) set activation_token_hash = db.query( """ SELECT password_reset_token_hash FROM auth_users WHERE email = %(email)s """, { 'email': email }).flat() assert activation_token_hash assert len(activation_token_hash) == 1 assert len(activation_token_hash[0]) > 0 # Activate user activate_user_via_token(db=db, email=email, activation_token=activation_token) # Imposed delay after unsuccessful login time.sleep(2) # Test logging in user = login_with_email_password(db=db, email=email, password=password) assert user assert isinstance(user, CurrentUser) assert user.email() == email assert user.full_name() == full_name # Make sure activation token is not set anymore activation_token_hash = db.query( """ SELECT password_reset_token_hash FROM auth_users WHERE email = %(email)s """, { 'email': email }).flat() assert activation_token_hash assert len(activation_token_hash) == 1 assert activation_token_hash[0] is None # Incorrect activation token _generate_user_activation_token(db=db, email=email, activation_link=activation_url) with pytest.raises(McAuthRegisterException): activate_user_via_token( db=db, email=email, activation_token='incorrect activation token') # Activating nonexistent user final_activation_url = _generate_user_activation_token( db=db, email=email, activation_link=activation_url, ) final_activation_uri = furl(final_activation_url) activation_token = final_activation_uri.args['activation_token'] with pytest.raises(McAuthRegisterException): activate_user_via_token(db=db, email='*****@*****.**', activation_token=activation_token)
def add_user(db: DatabaseHandler, new_user: NewUser) -> None: """Add new user.""" if not new_user: raise McAuthRegisterException("New user is undefined.") # Check if user already exists user_exists = db.query( """ SELECT auth_users_id FROM auth_users WHERE email = %(email)s LIMIT 1 """, { 'email': new_user.email() }).hash() if user_exists is not None and 'auth_users_id' in user_exists: raise McAuthRegisterException("User with email '%s' already exists." % new_user.email()) # Hash + validate the password try: password_hash = generate_secure_hash(password=new_user.password()) if not password_hash: raise McAuthRegisterException("Password hash is empty.") except Exception as ex: log.error("Unable to hash a new password: {}".format(ex)) raise McAuthRegisterException('Unable to hash a new password.') db.begin() # Create the user db.create(table='auth_users', insert_hash={ 'email': new_user.email(), 'password_hash': password_hash, 'full_name': new_user.full_name(), 'notes': new_user.notes(), 'active': bool(int(new_user.active())), }) # Fetch the user's ID try: user = user_info(db=db, email=new_user.email()) except Exception as ex: db.rollback() raise McAuthRegisterException( "I've attempted to create the user but it doesn't exist: %s" % str(ex)) # Create roles try: for auth_roles_id in new_user.role_ids(): db.create(table='auth_users_roles_map', insert_hash={ 'auth_users_id': user.user_id(), 'auth_roles_id': auth_roles_id, }) except Exception as ex: raise McAuthRegisterException("Unable to create roles: %s" % str(ex)) # Update limits (if they're defined) if new_user.weekly_requests_limit() is not None: db.query( """ UPDATE auth_user_limits SET weekly_requests_limit = %(weekly_requests_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'auth_users_id': user.user_id(), 'weekly_requests_limit': new_user.weekly_requests_limit(), }) if new_user.weekly_requested_items_limit() is not None: db.query( """ UPDATE auth_user_limits SET weekly_requested_items_limit = %(weekly_requested_items_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'auth_users_id': user.user_id(), 'weekly_requested_items_limit': new_user.weekly_requested_items_limit(), }) # Subscribe to newsletter if new_user.subscribe_to_newsletter(): db.create(table='auth_users_subscribe_to_newsletter', insert_hash={'auth_users_id': user.user_id()}) if not new_user.active(): send_user_activation_token( db=db, email=new_user.email(), activation_link=new_user.activation_url(), subscribe_to_newsletter=new_user.subscribe_to_newsletter(), ) db.commit()