示例#1
0
    def test_class_properties(self):
        error_response = None
        try:
            raise duo.PasscodeRequired('factor', 'state_token')
        except duo.PasscodeRequired as err:
            error_response = err

        self.assertEqual(error_response.factor, 'factor')
        self.assertEqual(error_response.state_token, 'state_token')
示例#2
0
    def test_class_properties(self):
        error_response = None
        try:
            raise duo.PasscodeRequired("factor", "state_token")
        except duo.PasscodeRequired as err:
            error_response = err

        self.assertEqual(error_response.factor, "factor")
        self.assertEqual(error_response.state_token, "state_token")
示例#3
0
    def test_auth_okta_duo_mfa_passcode(self, _config_mock):
        keyman = Keyman(['foo', '-o', 'foo', '-u', 'bar', '-a', 'baz'])
        keyman.okta_client = mock.MagicMock()
        keyman.okta_client.auth.side_effect = duo.PasscodeRequired('a', 'b')
        keyman.okta_client.duo_auth.return_value = True
        keyman.user_input = mock.MagicMock()
        keyman.user_input.return_value = '000000'

        keyman.auth_okta()

        keyman.okta_client.duo_auth.assert_has_calls([
            mock.call('a', 'b', '000000'),
        ])
示例#4
0
    def test_auth_okta_duo_mfa_passcode(self, _config_mock):
        keyman = Keyman(["foo", "-o", "foo", "-u", "bar", "-a", "baz"])
        keyman.okta_client = mock.MagicMock()
        keyman.okta_client.auth.side_effect = duo.PasscodeRequired("a", "b")
        keyman.okta_client.duo_auth.return_value = True
        keyman.user_input = mock.MagicMock()
        keyman.user_input.return_value = "000000"

        keyman.auth_okta()

        keyman.okta_client.duo_auth.assert_has_calls(
            [
                mock.call("a", "b", "000000"),
            ],
        )
示例#5
0
    def duo_auth(self, fid, state_token, passcode=None):
        """Trigger a Duo Auth request.

        This method is meant to be called by self.auth() if a Login session
        requires MFA, and the users profile supports Duo.

        If web is requested we set up a local web server for web auth and then
        open a browser for the user. This is going to be left in place in case
        in the future Duo breaks the current method for getting around the web
        version.

        If web is not requested we will try to fake out Duo to move ahead with
        MFA without needing to use their iframe format.

        In either case we then immediately go into a wait loop. Each time we
        loop around, we pull the latest status for that push event. If it's
        declined we will throw an error. If its accepted, we write out our
        SessionToken.

        Args:
            fid: Okta Factor ID used to trigger the push
            state_token: State Token allowing us to trigger the push
            passcode: OTP passcode string

        Returns:
            Dict (JSON) of API response for the MFA status if successful
            otherwise None
        """
        if self.duo_factor is None:
            # Prompt user for which Duo factor to use
            raise duo.FactorRequired(id, state_token)

        if self.duo_factor == "passcode" and not passcode:
            raise duo.PasscodeRequired(fid, state_token)

        path = '/authn/factors/{fid}/verify'.format(fid=fid)
        data = {'fid': fid,
                'stateToken': state_token}
        ret = self._request(path, data)
        verification = ret['_embedded']['factor']['_embedded']['verification']

        auth = None
        duo_client = duo.Duo(verification, state_token, self.duo_factor)
        if self.duo_factor == "web":
            # Duo Web via local browser
            LOG.warning('Duo required; opening browser...')
            proc = Process(target=duo_client.trigger_web_duo)
            proc.start()
            time.sleep(2)
            webbrowser.open_new('http://127.0.0.1:65432/duo.html')
        elif self.duo_factor == "passcode":
            # Duo auth with OTP code without a browser
            LOG.warning('Duo required; using OTP...')
            auth = duo_client.trigger_duo(passcode=passcode)
        else:
            # Duo Auth without the browser
            LOG.warning('Duo required; check your phone... 📱')
            auth = duo_client.trigger_duo()

        if auth is not None:
            self.mfa_callback(auth, verification, state_token)
            ret = self.mfa_wait_loop(ret, data)
            if ret:
                self.set_token(ret)
                return True
        return None