def test_activate_user(database): """Test creating inactive users and activating them.""" # -- Test creating an inactive user --------------------------------------- username = '******' password = '******' with database.session() as session: users = UserManager(session) user = users.register_user(username, password, verify=True) user_id = user.user_id # Attempt to login will raise error with pytest.raises(err.UnknownUserError): users.login_user(username, password) # -- Test activate user --------------------------------------------------- with database.session() as session: # After activating the user login should succeed. users = UserManager(session) auth = OpenAccessAuth(session) active_user = users.activate_user(user_id) assert active_user.user_id == user_id assert active_user.name == username assert active_user.api_key is None user = users.login_user(username, password) assert user.name == username assert user.api_key is not None assert auth.authenticate(user.api_key.value).user_id == user_id # Activate the same user twice should not raise an error active_user = users.activate_user(user_id) assert active_user.user_id == user_id assert active_user.name == username assert active_user.api_key is not None
def test_user_handle_serialization(database): """Test serialization of user handles.""" schema = validator('User') view = UserSerializer() with database.session() as session: manager = UserManager(session) user = manager.register_user('alice', 'mypwd') doc = view.user(user) schema.validate(doc) assert doc[labels.USER_NAME] == 'alice' assert labels.USER_TOKEN not in doc user = manager.login_user('alice', 'mypwd') doc = view.user(user, include_token=True) schema.validate(doc) assert doc[labels.USER_NAME] == 'alice' assert labels.USER_TOKEN in doc
def test_login_after_timeout(database, authcls): """Test login after key expired.""" # -- Setup ---------------------------------------------------------------- # # Create a database with a single active user. with database.session() as session: user_1 = model.create_user(session, active=True) # Test login after timeout ----------------------------------------- with database.session() as session: # Create user manager with TTL for login tokens of 1 sec. users = UserManager(session, token_timeout=1) # Authenticate user 1. Then sleep for 1.5 sec. When logging in again an # new api-key is generated. api_key = users.login_user(user_1, user_1).api_key.value time.sleep(1.5) assert users.login_user(user_1, user_1).api_key.value != api_key
def test_authenticate_after_timeout(database, authcls): """Test authentication after key expired.""" # -- Setup ---------------------------------------------------------------- # # Create a database with a single active user. with database.session() as session: user_1 = model.create_user(session, active=True) # Test authenticate after timeout ----------------------------------------- with database.session() as session: # Create user manager with TTL for login tokens of 1 sec. users = UserManager(session, token_timeout=1) auth = authcls(session) # Authenticate user 1. Then sleep for 1.5 sec. Trying to authenticate # the user after the sleep period should raise an error. api_key = users.login_user(user_1, user_1).api_key.value time.sleep(1.5) with pytest.raises(err.UnauthenticatedAccessError): auth.authenticate(api_key)
def test_user_listing_serialization(database): """Test serialization of user listings.""" schema = validator('UserListing') view = UserSerializer() with database.session() as session: manager = UserManager(session) manager.register_user('alice', 'mypwd') manager.register_user('bob', 'mypwd') doc = view.user_listing(manager.list_users()) schema.validate(doc) assert len(doc[labels.USER_LIST]) == 2
def test_reset_request_timeout(database): """Test resetting a user password using a request identifier that has timed out. """ # -- Setup ---------------------------------------------------------------- # # Create database with single active user. username = '******' with database.session() as session: users = UserManager(session) users.register_user(username, 'pwd1') # -- Test password reset after timed-out ---------------------------------- with database.session() as session: # Request a password reset and then sleep for a period of time that is # lonker than the token timeout period. users = UserManager(session, token_timeout=1) request_id = users.request_password_reset(username) time.sleep(2) with pytest.raises(err.UnknownRequestError): users.reset_password(request_id=request_id, password='******')
def __init__(self, session: Session, fs: FileStore, users: Optional[UserManager] = None): """Initialize the connection to the underlying database and the file system helper to access group files. Parameters ---------- session: sqlalchemy.orm.session.Session Database session. fs: flowserv.model.files.FileStore File store for uploaded group files. users: flowserv.model.user.UserManager, default=None Manager to access user objects. """ self.session = session self.fs = fs self.users = users if users is not None else UserManager(session=session)
def __enter__(self) -> API: """Create a new instance of the local API when the context manager is entered. """ # Open a new database session. self._session = self._db.session() session = self._session.open() # Shortcuts for local variables. env = self._env fs = self._fs engine = self._engine # Start by creating the authorization component and setting the # identifier for and authenticated user. user_id = self._user_id username = None if env[AUTH] == config.AUTH_OPEN: auth = OpenAccessAuth(session) user_id = config.DEFAULT_USER if user_id is None else user_id else: auth = DefaultAuthPolicy(session) access_token = self._access_token if self._access_token is not None else env.get( ACCESS_TOKEN) if access_token and user_id is None: # If an access token is given we retrieve the user that is # associated with the token. Authentication may raise an error. # Here, we ignore that error since the token may be an outdated # token that is stored in the environment. try: user = auth.authenticate(access_token) # Set the user name for the authenticated user (to be # included in the service descriptor). username = user.name user_id = user.user_id except err.UnauthenticatedAccessError: pass # Create the individual components of the API. ttl = env.get(config.FLOWSERV_AUTH_LOGINTTL, config.DEFAULT_LOGINTTL) user_manager = UserManager(session=session, token_timeout=ttl) run_manager = RunManager(session=session, fs=fs) group_manager = WorkflowGroupManager(session=session, fs=fs, users=user_manager) ranking_manager = RankingManager(session=session) workflow_repo = WorkflowManager(session=session, fs=fs) return API( service=ServiceDescriptor.from_config(env=env, username=username), workflow_service=LocalWorkflowService( workflow_repo=workflow_repo, ranking_manager=ranking_manager, group_manager=group_manager, run_manager=run_manager, user_id=user_id), group_service=LocalWorkflowGroupService( group_manager=group_manager, workflow_repo=workflow_repo, backend=engine, run_manager=run_manager, auth=auth, user_id=user_id), upload_service=LocalUploadFileService(group_manager=group_manager, auth=auth, user_id=user_id), run_service=LocalRunService(run_manager=run_manager, group_manager=group_manager, ranking_manager=ranking_manager, backend=engine, auth=auth, user_id=user_id), user_service=LocalUserService(manager=user_manager, auth=auth))
def test_authenticate_user(database, authcls): """Test user login and logout. Uses a database with two active and one inactive user to validate that active users can login and logout while inactive users cannot login. """ # -- Setup ---------------------------------------------------------------- # # Create a database with two active and one inactive users. with database.session() as session: user_1 = model.create_user(session, active=True) user_2 = model.create_user(session, active=True) user_3 = model.create_user(session, active=False) # -- Test login ----------------------------------------------------------- with database.session() as session: users = UserManager(session) token_1 = users.login_user(user_1, user_1).api_key.value token_2 = users.login_user(user_2, user_2).api_key.value # -- Test authentication -------------------------------------------------- with database.session() as session: auth = authcls(session) # Authentication of logged-in users using the API key should return the # respective user identifier. assert auth.authenticate(token_1).user_id == user_1 assert auth.authenticate(token_2).user_id == user_2 # -- Test logout ---------------------------------------------------------- with database.session() as session: # Logout user 1. User 2 should still be able to authenticate while a # user that is logged out cannot. users = UserManager(session) auth = authcls(session) assert users.logout_user(token_1).user_id == user_1 # Authenticating user 1 will raise exception. with pytest.raises(err.UnauthenticatedAccessError): auth.authenticate(token_1) # Logging out a user that is not logged in will not raise error> assert users.logout_user(token_1) is None # User 2 can still authenticate. assert auth.authenticate(token_2).user_id == user_2 # -- Test re-login -------------------------------------------------------- with database.session() as session: # Login user 1 again. users = UserManager(session) auth = authcls(session) token_1 = users.login_user(user_1, user_1).api_key.value # User 1 and 2 can now be authenticated again. assert auth.authenticate(token_1).user_id == user_1 assert auth.authenticate(token_2).user_id == user_2 # If a logged in user logs in the previous key does not become invalid. token_3 = users.login_user(user_1, user_1).api_key.value assert auth.authenticate(token_1).user_id == user_1 assert auth.authenticate(token_3).user_id == user_1 # -- Error cases ---------------------------------------------------------- with database.session() as session: users = UserManager(session) auth = authcls(session) # Attempt to authenticate unknown user raises error with pytest.raises(err.UnknownUserError): users.login_user('unknown', user_1) # Attempt to authenticate with invalid password will raises error with pytest.raises(err.UnknownUserError): users.login_user(user_1, user_2) # Inactive user 3 should not be able to login. with pytest.raises(err.UnknownUserError): users.login_user(user_3, user_3) # An error is raised when using an invalid API key. with pytest.raises(err.UnauthenticatedAccessError): assert auth.authenticate('UNKNOWN')
def test_register_user(database): """Test registering a new user.""" # -- Test creating an active user ----------------------------------------- with database.session() as session: users = UserManager(session) auth = OpenAccessAuth(session) reg_user = users.register_user('*****@*****.**', 'pwd1') assert reg_user.api_key is None user_1 = users.login_user('*****@*****.**', 'pwd1') assert user_1.name == '*****@*****.**' user_id_1 = auth.authenticate(user_1.api_key.value).user_id assert user_1.user_id == user_id_1 # -- Error cases ---------------------------------------------------------- with database.session() as session: users = UserManager(session) # Register user with existing email address raises error with pytest.raises(err.DuplicateUserError): users.register_user('*****@*****.**', 'pwd1') # Providing invalid email or password will raise error with pytest.raises(err.ConstraintViolationError): users.register_user(None, 'pwd1') with pytest.raises(err.ConstraintViolationError): users.register_user(' \t', 'pwd1') with pytest.raises(err.ConstraintViolationError): users.register_user('a' * 513, 'pwd1') with pytest.raises(err.ConstraintViolationError): users.register_user('*****@*****.**', ' ')
def test_list_users(database): """Test listing and searching for users.""" # -- Setup ---------------------------------------------------------------- # # Create a database with four active and one inactive user. with database.session() as session: users = UserManager(session) users.register_user('*****@*****.**', 'pwd1', verify=False) users.register_user('*****@*****.**', 'pwd1', verify=False) users.register_user('*****@*****.**', 'pwd1', verify=False) users.register_user('*****@*****.**', 'pwd1', verify=False) u = users.register_user('*****@*****.**', 'pwd1', verify=True) inactive_user_id = u.user_id # -- Test user listing ---------------------------------------------------- with database.session() as session: # There should be four users in the returned list. users = UserManager(session) assert len(users.list_users()) == 4 with database.session() as session: # After activating the inactive user, the listing contains five users. users = UserManager(session) users.activate_user(inactive_user_id) assert len(users.list_users()) == 5 # -- Test query prefixes -------------------------------------------------- with database.session() as session: # Query users by name prefix. users = UserManager(session) assert len(users.list_users(prefix='a')) == 3 assert len(users.list_users(prefix='ab')) == 2 assert len(users.list_users(prefix='ade')) == 1
def test_reset_password(database): """Test resetting a user password.""" # -- Setup ---------------------------------------------------------------- # # Create one active user. username = '******' password = '******' with database.session() as session: # After a reset request has been send the previous password should # still be valid. users = UserManager(session) auth = OpenAccessAuth(session) user_id = users.register_user(username, password).user_id # -- Test reset password -------------------------------------------------- with database.session() as session: users = UserManager(session) auth = OpenAccessAuth(session) # Ensure login works prior to reset request. token = users.login_user(username, password).api_key.value assert auth.authenticate(token).user_id == user_id request_id = users.request_password_reset(username) password = '******' user = users.reset_password(request_id=request_id, password=password) assert user.user_id == user_id # After resetting the password the previous API key for the user is # invalid with pytest.raises(err.UnauthenticatedAccessError): auth.authenticate(token) token = users.login_user('*****@*****.**', 'mypwd').api_key.value assert auth.authenticate(token).user_id == user_id # -- Test login after request --------------------------------------------- with database.session() as session: # After a reset request has been send the previous password should # still be valid. users = UserManager(session) auth = OpenAccessAuth(session) users.request_password_reset(username) token = users.login_user(username, password).api_key.value assert auth.authenticate(token).user_id == user_id # -- Test request reset for unknown user ---------------------------------- with database.session() as session: users = UserManager(session) assert users.request_password_reset('*****@*****.**') is not None # --Error cases ----------------------------------------------------------- with database.session() as session: # An error is raised when (i) trying to use a request for an unknown # user, (ii) a previously completed reset request, or (iii) an unknown # request identifier to reset a user password users = UserManager(session) with pytest.raises(err.UnknownRequestError): users.reset_password(request_id=request_id, password=password) with pytest.raises(err.UnknownRequestError): users.reset_password(request_id='UNKNOWN', password=password) with pytest.raises(err.UnknownRequestError): users.reset_password(request_id='unknown', password=password)