Beispiel #1
0
    def clean_email(self):
        """
        email field validation to check if the username is already in Okta

        """
        readCount = 0
        try:
            usersClient = UsersClient(''.join(['https://', settings.OKTA_ORG]), settings.OKTA_API_TOKEN)
            result = usersClient.get_users(filter_string='profile.login eq "' + self.cleaned_data['email'] + '"')
            readCount = len(result)
        except Exception as e:
            return self.cleaned_data['email']

        if readCount > 0:
            raise forms.ValidationError(_("The username already exists."))

        return self.cleaned_data['email']
Beispiel #2
0
class AuthClient(BaseAuthClient):
    def __init__(self, connection_config, reauth_time, auth_attrib) -> None:
        '''
        Args:
            connection_config (Dict):
                Parameters required to connect to the Okta API
            reauth_time (int):
                The min time in seconds to cache auth requests
            auth_attrib (str):
                The attribute of the user record that will be used to
                authenticate them.
        '''
        super().__init__(reauth_time, auth_attrib)
        connection_config['pathname'] = '/api/v1/users'

        self.usersclient = UsersClient(**connection_config)
        self.factorsclient = FactorsClient(**connection_config)
        self.apiclient = ApiClient(**connection_config)

        # Maintain a per user lookup for poll URL
        self.poll_url = {}

    def _get_okta_userid(self, username):
        user = self.usersclient.get_users(query=username, limit=1)

        try:
            return user[0].id
        except Exception as error:
            logging.error('Error getting username {}'.format(error))
            return None

    def _get_factors(self, userid):
        try:
            return self.factorsclient.get_lifecycle_factors(userid)
        except Exception as error:
            logging.error('Error getting factors {}'.format(error))
            return None

    def can_auth(self, user):
        # type: () -> bool
        # Check Okta user for a push factor.
        # Returns false is not available
        # Returns factor Id if it is
        # TODO: Add support for other types of auth (TOTP, etc).
        username = self._auth_attribute(user)
        if username is not False:
            logging.debug('Checking auth capabilities for {}'.format(username))

            okta_user_id = self._get_okta_userid(username)
            factors = self._get_factors(okta_user_id)
            if factors is not None:
                for factor in factors:
                    if factor.factorType == 'push':
                        return factor.id

        return False

    def auth(self, user, reason=None):
        # type: (str) -> None
        logging.debug('Sending Okta Push request for {}'.format(
            self._auth_attribute(user)))

        # Okta's SDK is broken!
        # https://github.com/okta/okta-sdk-python/issues/66
        # res = self.factorsclient.verify_factor(
        #    user_id=self.okta_user_id,
        #    user_factor_id=self.okta_push_factor_id
        # )
        # Implement our own call which actually works
        okta_user_id = self._get_okta_userid(self._auth_attribute(user))
        res = self.apiclient.post_path('/{0}/factors/{1}/verify'.format(
            okta_user_id, user._factor_id))
        try:
            res_obj = json.loads(res.text)
        except Exception as error:
            raise AuthException(error)

        self.poll_url[okta_user_id] = res_obj['_links']['poll']['href']
        user._last_auth_state = AuthStates.PENDING

    def auth_status(self, user):
        # type: () -> int
        okta_user_id = self._get_okta_userid(self._auth_attribute(user))

        if user._last_auth_state == AuthStates.PENDING:
            response = self.apiclient.get(self.poll_url[okta_user_id])
            response_obj = json.loads(response.text)
            res = response_obj['factorResult']
            if res != 'WAITING':
                if res == 'SUCCESS':
                    user._last_auth_state = AuthStates.AUTHORIZED
                    user._last_auth_time = datetime.now(tz=pytz.utc)
                else:
                    user._last_auth_state = AuthStates.DENIED
                    user._last_auth_time = datetime.min
        elif user._last_auth_state == AuthStates.AUTHORIZED:
            if not self._recently_authed(user):
                user._last_auth_state = AuthStates.NONE
        return user._last_auth_state

    def reset(self, user):
        okta_user_id = self._get_okta_userid(self._auth_attribute(user))
        self.poll_url.pop(okta_user_id, None)
        user._last_auth_state = AuthStates.NONE