def test_session_manager(): """ SessionManager class tests """ cfg = Mock() cfg.secrets_store = "cleartext" user_id = 'test%user' user_secret = 'secret_for_test' user_token = '1234567890-0987654321' session_manager = SessionManager(cfg) # Have requested backend assert isinstance(session_manager.secure_store, ClearTextStoreAdapter) # create a new user s = session_manager.create(user_id, user_secret) assert s.user_id == user_id # 1 session in list assert len(session_manager.list_sessions()) == 1 # create same user raise exception with pytest.raises(SessionExistsError): session_manager.create(user_id, user_secret) # get an existing session s1 = session_manager.get_by_uuid(s.id()) assert s1.id() == s.id() # get a non-existent session with pytest.raises(SessionInvalidError): session_manager.get_by_uuid(s.id() + "no_uuid") # get an existing session by user s2 = session_manager.get_by_userid(user_id) assert s2.id() == s.id() # get a non-existent session by user with pytest.raises(SessionInvalidError): session_manager.get_by_userid(user_id + "no_user") # confirm existence assert session_manager.exists(user_id) is True # confirm non-existence assert session_manager.exists(user_id + "no_user") is False # Set value in secret store assert session_manager.put_secret(s.id(), user_token) is True # Get value in secret store assert session_manager.get_secret(s.id()) == user_token
class AuthenticationController(object): def __init__(self, bot): self.bot = bot self.sessions = SessionManager(bot.cfg) def to_userid(self, user): """ Convert BotIdentity, Identifier to plain text string suitable for use as the key with Sessions and cached tokens. param: user: may be one of BotIdentity, errbot.backend.base.Identifier or string. """ if isinstance(user, BotPluginIdentity): user_id = user.name elif isinstance(user, Identifier): user_id = self.bot.chatbackend.normalise_user_id(user) else: user_id = user LOG.info("User ID is '{}'".format(user_id)) return user_id def pre_execution_authentication(self, chat_user): """ Look up the chat_user to confirm they are authenticated. param: chat_user: the chat back end user. return: A valid St2 Token or False in the case of an error """ user_id = self.to_userid(chat_user) return self.bot.cfg.auth_handler.pre_execution_authentication(self, user_id) def consume_session(self, session_id): """ Fetch the session and unseal it to mark it as consumed. """ session = self.sessions.get_by_uuid(session_id) if session is False: LOG.debug("Invalid session id '{}'.".format(session_id)) raise SessionInvalidError session.unseal() return True def list_sessions(self): """ Returns a list of sessions. """ return self.sessions.list_sessions() def session_url(self, session_id, url_path="/"): """ Return a URL formatted with the UUID query string attached. """ return "{}{}?uuid={}".format( self.bot.cfg.auth_handler.url, url_path, session_id ) def delete_session(self, session_id): """ Delete a session from the store. """ session = self.sessions.get_by_uuid(session_id) if session is False: LOG.debug("Session '{}' doesn't exist to be deleted".format(session_id)) raise SessionInvalidError else: self.sessions.delete(session.user_id) def get_session_userid(self, session_id): session = self.sessions.get_by_uuid(session_id) session.is_expired() return session.user_id def get_token_by_userid(self, user): """ Get the associated StackStorm token/key given chat backend username. Return StackStorm token/key associated with the user or False if session isn't valid or secret is missing. """ secret = False session = self.sessions.get_by_userid(self.to_userid(user)) if session: secret = self.get_token_by_session(session.id()) return secret def get_token_by_session(self, session_id): """ Get the associated StackStorm token/key given session id. """ LOG.debug("Get token for session id {}".format(session_id)) return self.sessions.get_secret(session_id) def set_token_by_session(self, session_id, token): """ Stores a StackStorm user token or api key in the secrets store using the session id. """ return self.sessions.put_secret(session_id, token) def set_token_by_userid(self, user_id, token): """ Store StackStorm user token or api key in the secrets store. """ session = self.sessions.get_by_userid(self.to_userid(user_id)) if session: ret = self.set_token_by_session(session.id(), token) else: LOG.debug("Failed to lookup session for user id '{}'".format(user_id)) return ret def create_session(self, user, user_secret): """ Handle an initial request to establish a session. If a session already exists, return it. """ user_id = self.to_userid(user) return self.sessions.create(user_id, user_secret) def get_session(self, user): """ Returns the session associated with the user. """ user_id = self.to_userid(user) session = self.sessions.get_by_userid(user_id) if session is False: raise SessionInvalidError return session def match_secret(self, session_id, user_secret): """ Fetch session and compare user_secret. """ session = self.sessions.get_by_uuid(session_id) if session is False: LOG.debug("Session '{}' doesn't exist.".format(session_id)) raise SessionInvalidError if session.is_sealed(): LOG.warning("Attempting to check secret while session is sealed.") return False return session.match_secret(user_secret) def associate_credentials(self, user, creds, bot_creds): """ Verify credentials against stackstorm and if successful, store them using the user id. param: user: the normalised chat_user account. param: creds: the stackstorm user credentials to validate against StackStorm API. param: bot_creds: the bot credentials to use when authenticating user credentials. Return true if credentials were valid or False if they were not. """ # get the configured authentication handler. token = self.bot.cfg.auth_handler.authenticate(user, creds, bot_creds) LOG.debug("Token for {} was {}".format(user, token)) # pass credentials to authentication handler verify credentials if token: self.set_token_by_userid(user, token) else: LOG.warning("Failed to validate StackStorm credentials for {}.".format(user)) # Explicitly test not false to avoid returning tokens value. return token is not False
class AuthenticationController(object): def __init__(self, bot): self.bot = bot self.sessions = SessionManager(bot.cfg) def to_userid(self, user): """ Convert BotIdentity, Identifier to plain text string suitable for use as the key with Sessions and cached tokens. param: user: may be one of BotIdentity, errbot.backend.base.Identifier or string. """ if isinstance(user, BotPluginIdentity): user_id = user.name elif isinstance(user, Identifier): user_id = self.bot.chatbackend.normalise_user_id(user) else: user_id = user LOG.info("User ID is '{}'".format(user_id)) return user_id def pre_execution_authentication(self, chat_user): """ Look up the chat_user to confirm they are authenticated. param: chat_user: the chat back end user. return: A valid St2 Token or False in the case of an error """ user_id = self.to_userid(chat_user) return self.bot.cfg.auth_handler.pre_execution_authentication(self, user_id) def consume_session(self, session_id): """ Fetch the session and unseal it to mark it as consumed. """ session = self.sessions.get_by_uuid(session_id) if session is False: LOG.debug("Invalid session id '{}'.".format(session_id)) raise SessionInvalidError session.unseal() return True def list_sessions(self): """ Returns a list of sessions. """ return self.sessions.list_sessions() def session_url(self, session_id, url_path="/"): """ Return a URL formatted with the UUID query string attached. """ return "{}{}?uuid={}".format( self.bot.cfg.auth_handler.url, url_path, session_id ) def delete_session(self, session_id): """ Delete a session from the store. """ session = self.sessions.get_by_uuid(session_id) if session is False: LOG.debug("Session '{}' doesn't exist to be deleted".format(session_id)) raise SessionInvalidError else: self.sessions.delete(session.user_id) def get_session_userid(self, session_id): session = self.sessions.get_by_uuid(session_id) session.is_expired() return session.user_id def get_token_by_userid(self, user): """ Get the associated StackStorm token/key given chat backend username. Return StackStorm token/key associated with the user or False if session isn't valid or secret is missing. """ secret = False session = self.sessions.get_by_userid(self.to_userid(user)) if session: secret = self.get_token_by_session(session.id()) return secret def get_token_by_session(self, session_id): """ Get the associated StackStorm token/key given session id. """ LOG.debug("Get token for session id {}".format(session_id)) return self.sessions.get_secret(session_id) def set_token_by_session(self, session_id, token): """ Stores a StackStorm user token or api key in the secrets store using the session id. """ return self.sessions.put_secret(session_id, token) def set_token_by_userid(self, user_id, token): """ Store StackStorm user token or api key in the secrets store. """ session = self.sessions.get_by_userid(self.to_userid(user_id)) if session: ret = self.set_token_by_session(session.id(), token) else: LOG.debug("Failed to lookup session for user id '{}'".format(user_id)) return ret def create_session(self, user, user_secret): """ Handle an initial request to establish a session. If a session already exists, return it. """ user_id = self.to_userid(user) return self.sessions.create(user_id, user_secret) def get_session(self, user): """ Returns the session associated with the user. """ user_id = self.to_userid(user) session = self.sessions.get_by_userid(user_id) if session is False: raise SessionInvalidError return session def match_secret(self, session_id, user_secret): """ Fetch session and compare user_secret. """ session = self.sessions.get_by_uuid(session_id) if session is False: LOG.debug("Session '{}' doesn't exist.".format(session_id)) raise SessionInvalidError if session.is_sealed(): LOG.warning("Attempting to check secret while session is sealed.") return False return session.match_secret(user_secret) def associate_credentials(self, user, creds, bot_creds): """ Verify credentials against stackstorm and if successful, store them using the user id. param: user: the normalised chat_user account. param: creds: the stackstorm user credentials to validate against StackStorm API. param: bot_creds: the bot credentials to use when authenticating user credentials. Return true if credentials were valid or False if they were not. """ # get the configured authentication handler. token = self.bot.cfg.auth_handler.authenticate(user, creds, bot_creds) # WARNING: Sensitive security information will be loggged, uncomment only when necessary. # LOG.debug("Token for {} was {}".format(user, token)) # pass credentials to authentication handler verify credentials if token: self.set_token_by_userid(user, token) else: LOG.warning("Failed to validate StackStorm credentials for {}.".format(user)) # Explicitly test not false to avoid returning tokens value. return token is not False