def __init__(self, settings, strategy=first_realm_successful_strategy): self.authc_settings = AuthenticationSettings(settings) self.authentication_strategy = strategy if self.authc_settings.mfa_challenger: self.mfa_challenger = self.authc_settings.mfa_challenger() self.realms = None self.token_realm_resolver = None self.locking_realm = None self.locking_limit = None self.event_bus = None
def __init__(self, settings): authc_settings = AuthenticationSettings(settings) self.password_cc = self.create_password_crypt_context(authc_settings) self.totp_cc = self.create_totp_crypt_context(authc_settings) self.cc_token_resolver = {UsernamePasswordToken: self.password_cc, TOTPToken: self.totp_cc} self.supported_tokens = self.cc_token_resolver.keys()
def create_totp_factory(env_var=None, file_path=None, authc_settings=None): if not authc_settings: yosai_settings = LazySettings(env_var=env_var, file_path=file_path) authc_settings = AuthenticationSettings(yosai_settings) totp_context = authc_settings.totp_context return TOTP.using(**totp_context)
def __init__(self, settings, strategy=first_realm_successful_strategy): self.authc_settings = AuthenticationSettings(settings) self.authentication_strategy = strategy try: self.mfa_dispatcher = self.authc_settings.\ mfa_dispatcher(self.authc_settings.mfa_dispatcher_config) except TypeError: self.mfa_dispatcher = None self.realms = None self.token_realm_resolver = None self.locking_realm = None self.locking_limit = None self.event_bus = None
class DefaultAuthenticator(authc_abcs.Authenticator): # Unlike Shiro, Yosai injects the strategy and the eventbus def __init__(self, settings, strategy=first_realm_successful_strategy): self.authc_settings = AuthenticationSettings(settings) self.authentication_strategy = strategy if self.authc_settings.mfa_challenger: self.mfa_challenger = self.authc_settings.mfa_challenger() self.realms = None self.token_realm_resolver = None self.locking_realm = None self.locking_limit = None self.event_bus = None def init_realms(self, realms): """ :type realms: Tuple """ self.realms = tuple( realm for realm in realms if isinstance(realm, realm_abcs.AuthenticatingRealm)) self.register_cache_clear_listener() self.token_realm_resolver = self.init_token_resolution() self.init_locking() def init_locking(self): locking_limit = self.authc_settings.account_lock_threshold if locking_limit: self.locking_realm = self.locate_locking_realm( ) # for account locking self.locking_limit = locking_limit def init_token_resolution(self): token_resolver = defaultdict(list) for realm in self.realms: if isinstance(realm, realm_abcs.AuthenticatingRealm): for token_class in realm.supported_authc_tokens: token_resolver[token_class].append(realm) return token_resolver def locate_locking_realm(self): """ the first realm that is identified as a LockingRealm will be used to lock all accounts """ for realm in self.realms: if hasattr(realm, 'lock_account'): return realm return None def authenticate_single_realm_account(self, realm, authc_token): return realm.authenticate_account(authc_token) def authenticate_multi_realm_account(self, realms, authc_token): attempt = DefaultAuthenticationAttempt(authc_token, realms) return self.authentication_strategy(attempt) def authenticate_account(self, identifiers, authc_token): """ :type identifiers: SimpleIdentifierCollection or None :returns: account_id (identifiers) if the account authenticates :rtype: SimpleIdentifierCollection """ msg = ("Authentication submission received for authentication " "token [" + str(authc_token) + "]") logger.debug(msg) # the following conditions verify correct authentication sequence if not getattr(authc_token, 'identifier', None): if not identifiers: msg = "Authentication must be performed in expected sequence." raise InvalidAuthenticationSequenceException(msg) authc_token.identifier = identifiers.primary_identifier # add token metadata before sending it onward: authc_token.token_info = token_info[authc_token.__class__] try: account = self.do_authenticate_account(authc_token) if (account is None): msg2 = ( "No account returned by any configured realms for " "submitted authentication token [{0}]".format(authc_token)) raise AccountException(msg2) except AdditionalAuthenticationRequired as exc: self.notify_event(authc_token.identifier, 'AUTHENTICATION.PROGRESS') try: self.mfa_challenger.send_challenge(authc_token.identifier) except AttributeError: # implies no multi-factor authc challenger is set pass raise exc # the security_manager saves subject identifiers except AccountException: self.notify_event(authc_token.identifier, 'AUTHENTICATION.ACCOUNT_NOT_FOUND') raise except LockedAccountException: self.notify_event(authc_token.identifier, 'AUTHENTICATION.FAILED') self.notify_event(authc_token.identifier, 'AUTHENTICATION.ACCOUNT_LOCKED') raise except IncorrectCredentialsException as exc: self.notify_event(authc_token.identifier, 'AUTHENTICATION.FAILED') self.validate_locked(authc_token, exc.failed_attempts) # this won't be called if the Account is locked: raise IncorrectCredentialsException self.notify_event(account['account_id'].primary_identifier, 'AUTHENTICATION.SUCCEEDED') return account['account_id'] def do_authenticate_account(self, authc_token): """ Returns an account object only when the current token authenticates AND the authentication process is complete, raising otherwise :returns: Account :raises AdditionalAuthenticationRequired: when additional tokens are required, passing the account object """ try: realms = self.token_realm_resolver[authc_token.__class__] except KeyError: raise KeyError('Unsupported Token Type Provided: ', authc_token.__class__.__name__) if (len(self.realms) == 1): account = self.authenticate_single_realm_account( realms[0], authc_token) else: account = self.authenticate_multi_realm_account( self.realms, authc_token) cred_type = authc_token.token_info['cred_type'] attempts = account['authc_info'][cred_type].get('failed_attempts', []) self.validate_locked(authc_token, attempts) # the following condition verifies whether the account uses MFA: if len(account['authc_info']) > authc_token.token_info['tier']: # the token authenticated but additional authentication is required self.notify_event(authc_token.identifier, 'AUTHENTICATION.PROGRESS') raise AdditionalAuthenticationRequired(account['account_id']) return account # -------------------------------------------------------------------------- # Event Communication # -------------------------------------------------------------------------- def clear_cache(self, items=None, topic=EVENT_TOPIC): """ expects event object to be in the format of a session-stop or session-expire event, whose results attribute is a namedtuple(identifiers, session_key) """ try: for realm in self.realms: identifier = items.identifiers.from_source(realm.name) if identifier: realm.clear_cached_authc_info(identifier) except AttributeError: msg = ('Could not clear authc_info from cache after event. ' 'items: ' + str(items)) logger.warn(msg) def register_cache_clear_listener(self): try: self.event_bus.subscribe(self.clear_cache, 'SESSION.EXPIRE') self.event_bus.isSubscribed(self.clear_cache, 'SESSION.EXPIRE') self.event_bus.subscribe(self.clear_cache, 'SESSION.STOP') self.event_bus.isSubscribed(self.clear_cache, 'SESSION.STOP') except AttributeError: msg = "Authenticator failed to register listeners to event bus" logger.debug(msg) def notify_event(self, identifier, topic): try: self.event_bus.sendMessage(topic, identifier=identifier) except AttributeError: msg = "Could not publish {} event".format(topic) raise AttributeError(msg) def validate_locked(self, authc_token, failed_attempts): """ :param failed_attempts: the failed attempts for this type of credential """ if self.locking_limit and len(failed_attempts) > self.locking_limit: msg = ('Authentication attempts breached threshold. Account' ' is now locked for: ' + str(authc_token.identifier)) self.locking_realm.lock_account(authc_token.identifier) self.notify_event(authc_token.identifier, 'AUTHENTICATION.ACCOUNT_LOCKED') raise LockedAccountException(msg) def __repr__(self): return "<DefaultAuthenticator(event_bus={0}, strategy={0})>".\ format(self.event_bus, self.authentication_strategy)
def __init__(self, settings): authc_settings = AuthenticationSettings(settings) self.password_cc = self.create_password_crypt_context(authc_settings) self.totp_factory = create_totp_factory(authc_settings=authc_settings) self.supported_tokens = [UsernamePasswordToken, TOTPToken]
def patched_authc_settings(authc_config, monkeypatch): monkeypatch.setattr(settings, 'AUTHC_CONFIG', authc_config) return AuthenticationSettings()
def cryptcontext_factory(): authc_settings = AuthenticationSettings() return CryptContextFactory(authc_settings)
def authc_settings(core_settings): return AuthenticationSettings(core_settings)