コード例 #1
0
ファイル: authc.py プロジェクト: awesome-python/yosai
    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
コード例 #2
0
ファイル: credential.py プロジェクト: awesome-python/yosai
 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()
コード例 #3
0
ファイル: credential.py プロジェクト: zhill/yosai
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)
コード例 #4
0
    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
コード例 #5
0
ファイル: authc.py プロジェクト: awesome-python/yosai
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)
コード例 #6
0
ファイル: credential.py プロジェクト: zhill/yosai
 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]
コード例 #7
0
ファイル: conftest.py プロジェクト: hoatle/yosai
def patched_authc_settings(authc_config, monkeypatch):
    monkeypatch.setattr(settings, 'AUTHC_CONFIG', authc_config)
    return AuthenticationSettings()
コード例 #8
0
ファイル: conftest.py プロジェクト: hoatle/yosai
def cryptcontext_factory():
    authc_settings = AuthenticationSettings()
    return CryptContextFactory(authc_settings)
コード例 #9
0
def authc_settings(core_settings):
    return AuthenticationSettings(core_settings)