Exemple #1
0
    def test_handler(self, qh_mock):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.handler_with_html('foo', 'bar', 'baz')

        assert qh_mock.called

        qh_mock.assert_has_calls([mock.call(None, 'foo', 'bar', 'baz')])
Exemple #2
0
    def test_do_auth_302_success(self):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        duo_test.session.params = mock.MagicMock()
        duo_test.session.post.return_value = mock.MagicMock()
        duo_test.session.post.return_value.status_code = 302
        duo_test.session.post.return_value.headers = {
            "Location": "https://someurl/foo?sid=somesid",
        }
        ret = duo_test.do_auth("sid", "certs_url")

        self.assertEqual(
            duo_test.session.params,
            {
                "certs_url": "certs_url",
                "sid": "sid"
            },
        )
        self.assertEqual(
            duo_test.session.headers,
            {
                "Origin": "https://somehost",
                "Content-Type": "application/x-www-form-urlencoded",
            },
        )
        duo_test.session.assert_has_calls([
            mock.call.post(
                ("https://somehost/frame/web/v1/auth?tx=somesig&parent="
                 "http://0.0.0.0:3000/duo&v=2.1"),
                allow_redirects=False,
            ),
        ], )
        self.assertEqual(ret, "somesid")
Exemple #3
0
    def test_do_auth_302_success(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        duo_test.session.params = mock.MagicMock()
        duo_test.session.post.return_value = mock.MagicMock()
        duo_test.session.post.return_value.status_code = 302
        duo_test.session.post.return_value.headers = {
            'Location': 'https://someurl/foo?sid=somesid'
        }
        ret = duo_test.do_auth('sid', 'certs_url')

        self.assertEqual(duo_test.session.params, {
            'certs_url': 'certs_url',
            'sid': 'sid'
        })
        self.assertEqual(
            duo_test.session.headers, {
                'Origin': 'https://somehost',
                'Content-Type': 'application/x-www-form-urlencoded'
            })
        duo_test.session.assert_has_calls([
            mock.call.post(
                ('https://somehost/frame/web/v1/auth?tx=somesig&parent='
                 'http://0.0.0.0:3000/duo&v=2.1'),
                allow_redirects=False)
        ])
        self.assertEqual(ret, 'somesid')
Exemple #4
0
 def setup_for_trigger_duo(self, factor):
     self.duo_test = duo.Duo(DETAILS, 'token', factor)
     self.duo_test.do_auth = mock.MagicMock()
     self.duo_test.do_auth.return_value = "sid"
     self.duo_test.get_txid = mock.MagicMock()
     self.duo_test.get_txid.return_value = 'txid'
     self.duo_test.get_status = mock.MagicMock()
     self.duo_test.get_status.return_value = 'auth'
Exemple #5
0
    def test_do_auth_500(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        duo_test.session.params = mock.MagicMock()
        duo_test.session.post.return_value = mock.MagicMock()
        duo_test.session.post.return_value.status_code = 500

        with self.assertRaises(Exception):
            duo_test.do_auth('sid', 'certs_url')
Exemple #6
0
    def test_handler(self, qh_mock):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.handler_with_html("foo", "bar", "baz")

        assert qh_mock.called

        qh_mock.assert_has_calls([
            mock.call(None, "foo", "bar", "baz"),
        ], )
Exemple #7
0
    def test_duo_webserver(self, server_mock):
        server_mock.return_value = mock.MagicMock()

        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.duo_webserver()

        server_mock.assert_has_calls([
            mock.call(('127.0.0.1', 65432), duo_test.handler_with_html),
            mock.call().serve_forever()
        ])
Exemple #8
0
    def test_do_redirect_missing_cookie(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        json_ok = {'response': {'crumbs': 'yum'}, 'stat': 'OK'}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.return_value = MockResponse(
            headers, 200, json_ok)
        ret = duo_test.do_redirect('url', 'sid')

        self.assertEqual(ret, None)
Exemple #9
0
    def test_do_auth_302_location_missing(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        duo_test.session.params = mock.MagicMock()
        duo_test.session.post.return_value = mock.MagicMock()
        duo_test.session.post.return_value.status_code = 302
        duo_test.session.post.return_value.headers = {}

        with self.assertRaises(Exception):
            duo_test.do_auth('sid', 'certs_url')
Exemple #10
0
    def test_trigger_duo(self, process_mock, _sleep_mock):
        process_mock.start.return_value = None

        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.trigger_duo()

        process_mock.assert_has_calls([
            mock.call().start(),
            mock.call().terminate(),
        ])
Exemple #11
0
    def test_do_redirect_failure(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        json_ok = {'response': {'cookie': 'yum'}, 'stat': 'OK'}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.return_value = MockResponse(
            headers, 500, json_ok)

        with self.assertRaises(Exception):
            duo_test.do_redirect('url', 'sid')
Exemple #12
0
    def test_do_auth_200(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        duo_test.session.params = mock.MagicMock()
        json = {'response': {'sid': 'sid', 'certs_url': 'certs_url'}}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.side_effect = [
            MockResponse(headers, 200, json), MockResponse(headers, 302, json)]
        ret = duo_test.do_auth(None, 'certs_url')

        self.assertEqual(ret, 'somesid')
Exemple #13
0
    def test_get_status_timeout(self, _sleep_mock):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        duo_test.do_redirect = mock.MagicMock()
        json_wait = {'stat': 'WAIT'}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.return_value = MockResponse(
            headers, 200, json_wait)

        with self.assertRaises(Exception):
            duo_test.get_status('txid', 'sid')
Exemple #14
0
    def test_do_redirect_success(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        json_ok = {'response': {'cookie': 'yum'}, 'stat': 'OK'}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.return_value = MockResponse(
            headers, 200, json_ok)
        ret = duo_test.do_redirect('url', 'sid')

        duo_test.session.assert_has_calls([
            mock.call.post('https://somehosturl?sid=sid')])
        self.assertEqual(ret, 'yum')
Exemple #15
0
    def test_get_txid_without_passcode(self):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        json = {'response': {'txid': 'txid'}}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.return_value = MockResponse(headers, 200, json)
        ret = duo_test.get_txid('sid', 'factor')

        duo_test.session.assert_has_calls([
            mock.call.post(('https://somehost/frame/prompt?sid=sid&device='
                            'phone1&factor=factor&out_of_date=False'))])
        self.assertEqual(ret, 'txid')
Exemple #16
0
    def test_get_status_redirect(self, _sleep_mock):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        duo_test.do_redirect = mock.MagicMock()
        json_wait = {'stat': 'WAIT'}
        json_ok = {'response': {'result_url': 'url'}, 'stat': 'OK'}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.side_effect = [
            MockResponse(headers, 200, json_wait),
            MockResponse(headers, 200, json_ok)]
        duo_test.get_status('txid', 'sid')

        duo_test.do_redirect.assert_has_calls([mock.call('url', 'sid')])
Exemple #17
0
    def test_do_auth_200(self):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        duo_test.session.params = mock.MagicMock()
        json = {"response": {"sid": "sid", "certs_url": "certs_url"}}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.side_effect = [
            MockResponse(headers, 200, json),
            MockResponse(headers, 302, json),
        ]
        ret = duo_test.do_auth(None, "certs_url")

        self.assertEqual(ret, "somesid")
Exemple #18
0
    def test_do_redirect_failure(self):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        json_ok = {"response": {"cookie": "yum"}, "stat": "OK"}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.return_value = MockResponse(
            headers,
            500,
            json_ok,
        )

        with self.assertRaises(Exception):
            duo_test.do_redirect("url", "sid")
Exemple #19
0
    def test_do_redirect_missing_cookie(self):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        json_ok = {"response": {"crumbs": "yum"}, "stat": "OK"}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.return_value = MockResponse(
            headers,
            200,
            json_ok,
        )
        ret = duo_test.do_redirect("url", "sid")

        self.assertEqual(ret, None)
Exemple #20
0
    def test_get_status_redirect(self, _sleep_mock):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        duo_test.do_redirect = mock.MagicMock()
        json_wait = {"stat": "WAIT"}
        json_ok = {"response": {"result_url": "url"}, "stat": "OK"}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.side_effect = [
            MockResponse(headers, 200, json_wait),
            MockResponse(headers, 200, json_ok),
        ]
        duo_test.get_status("txid", "sid")

        duo_test.do_redirect.assert_has_calls([mock.call("url", "sid")])
Exemple #21
0
    def test_get_status_timeout(self, _sleep_mock):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        duo_test.do_redirect = mock.MagicMock()
        json_wait = {"stat": "WAIT"}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.return_value = MockResponse(
            headers,
            200,
            json_wait,
        )

        with self.assertRaises(Exception):
            duo_test.get_status("txid", "sid")
Exemple #22
0
    def test_get_txid_without_passcode(self):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        json = {"response": {"txid": "txid"}}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.return_value = MockResponse(headers, 200, json)
        ret = duo_test.get_txid("sid", "factor")

        duo_test.session.assert_has_calls([
            mock.call.post(
                "https://somehost/frame/prompt?sid=sid&device="
                "phone1&factor=factor&out_of_date=False", ),
        ], )
        self.assertEqual(ret, "txid")
Exemple #23
0
    def test_get_status_success(self, _sleep_mock):
        duo_test = duo.Duo(DETAILS, 'token')
        duo_test.session = mock.MagicMock()
        json_wait = {'stat': 'WAIT'}
        json_ok = {'response': {'cookie': 'yum'}, 'stat': 'OK'}
        headers = {'Location': 'https://someurl/foo?sid=somesid'}
        duo_test.session.post.side_effect = [
            MockResponse(headers, 200, json_wait),
            MockResponse(headers, 200, json_ok)]
        ret = duo_test.get_status('txid', 'sid')

        duo_test.session.assert_has_calls([
            mock.call.post('https://somehost/frame/status?sid=sid&txid=txid'),
            mock.call.post('https://somehost/frame/status?sid=sid&txid=txid')])
        self.assertEqual(ret, 'yum')
Exemple #24
0
    def test_do_redirect_success(self):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        json_ok = {"response": {"cookie": "yum"}, "stat": "OK"}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.return_value = MockResponse(
            headers,
            200,
            json_ok,
        )
        ret = duo_test.do_redirect("url", "sid")

        duo_test.session.assert_has_calls([
            mock.call.post("https://somehosturl?sid=sid"),
        ], )
        self.assertEqual(ret, "yum")
Exemple #25
0
    def test_get_status_success(self, _sleep_mock):
        duo_test = duo.Duo(DETAILS, "token")
        duo_test.session = mock.MagicMock()
        json_wait = {"stat": "WAIT"}
        json_ok = {"response": {"cookie": "yum"}, "stat": "OK"}
        headers = {"Location": "https://someurl/foo?sid=somesid"}
        duo_test.session.post.side_effect = [
            MockResponse(headers, 200, json_wait),
            MockResponse(headers, 200, json_ok),
        ]
        ret = duo_test.get_status("txid", "sid")

        duo_test.session.assert_has_calls([
            mock.call.post(
                "https://somehost/frame/status?sid=sid&txid=txid", ),
            mock.call.post(
                "https://somehost/frame/status?sid=sid&txid=txid", ),
        ], )
        self.assertEqual(ret, "yum")
Exemple #26
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
Exemple #27
0
 def test_init_with_args(self):
     duo_test = duo.Duo(DETAILS, 'token')
     self.assertEqual(duo_test.details, DETAILS)
     self.assertEqual(duo_test.token, 'token')
Exemple #28
0
 def test_init_missing_args(self):
     with self.assertRaises(TypeError):
         # noinspection PyArgumentList
         duo.Duo()
Exemple #29
0
 def test_init_missing_args(self):
     with self.assertRaises(TypeError):
         duo.Duo()