• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 Google Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import socket
16import sys
17import threading
18
19import mock
20from six.moves.urllib import request
21import unittest2
22
23from oauth2client import client
24from oauth2client import tools
25
26try:
27    import argparse
28except ImportError:  # pragma: NO COVER
29    raise unittest2.SkipTest('argparase unavailable.')
30
31
32class TestClientRedirectServer(unittest2.TestCase):
33    """Test the ClientRedirectServer and ClientRedirectHandler classes."""
34
35    def test_ClientRedirectServer(self):
36        # create a ClientRedirectServer and run it in a thread to listen
37        # for a mock GET request with the access token
38        # the server should return a 200 message and store the token
39        httpd = tools.ClientRedirectServer(('localhost', 0),
40                                           tools.ClientRedirectHandler)
41        code = 'foo'
42        url = 'http://localhost:{0}?code={1}'.format(
43            httpd.server_address[1], code)
44        t = threading.Thread(target=httpd.handle_request)
45        t.setDaemon(True)
46        t.start()
47        f = request.urlopen(url)
48        self.assertTrue(f.read())
49        t.join()
50        httpd.server_close()
51        self.assertEqual(httpd.query_params.get('code'), code)
52
53
54class TestRunFlow(unittest2.TestCase):
55
56    def setUp(self):
57        self.server = mock.Mock()
58        self.flow = mock.Mock()
59        self.storage = mock.Mock()
60        self.credentials = mock.Mock()
61
62        self.flow.step1_get_authorize_url.return_value = (
63            'http://example.com/auth')
64        self.flow.step2_exchange.return_value = self.credentials
65
66        self.flags = argparse.Namespace(
67            noauth_local_webserver=True, logging_level='INFO')
68        self.server_flags = argparse.Namespace(
69            noauth_local_webserver=False,
70            logging_level='INFO',
71            auth_host_port=[8080, ],
72            auth_host_name='localhost')
73
74    @mock.patch.object(sys, 'argv', ['ignored', '--noauth_local_webserver'])
75    @mock.patch('oauth2client.tools.logging')
76    @mock.patch('oauth2client.tools.input')
77    def test_run_flow_no_webserver(self, input_mock, logging_mock):
78        input_mock.return_value = 'auth_code'
79
80        # Successful exchange.
81        returned_credentials = tools.run_flow(self.flow, self.storage)
82
83        self.assertEqual(self.credentials, returned_credentials)
84        self.assertEqual(self.flow.redirect_uri, client.OOB_CALLBACK_URN)
85        self.flow.step2_exchange.assert_called_once_with(
86            'auth_code', http=None)
87        self.storage.put.assert_called_once_with(self.credentials)
88        self.credentials.set_store.assert_called_once_with(self.storage)
89
90    @mock.patch('oauth2client.tools.logging')
91    @mock.patch('oauth2client.tools.input')
92    def test_run_flow_no_webserver_explicit_flags(
93            self, input_mock, logging_mock):
94        input_mock.return_value = 'auth_code'
95
96        # Successful exchange.
97        returned_credentials = tools.run_flow(
98            self.flow, self.storage, flags=self.flags)
99
100        self.assertEqual(self.credentials, returned_credentials)
101        self.assertEqual(self.flow.redirect_uri, client.OOB_CALLBACK_URN)
102        self.flow.step2_exchange.assert_called_once_with(
103            'auth_code', http=None)
104
105    @mock.patch('oauth2client.tools.logging')
106    @mock.patch('oauth2client.tools.input')
107    def test_run_flow_no_webserver_exchange_error(
108            self, input_mock, logging_mock):
109        input_mock.return_value = 'auth_code'
110        self.flow.step2_exchange.side_effect = client.FlowExchangeError()
111
112        # Error while exchanging.
113        with self.assertRaises(SystemExit):
114            tools.run_flow(self.flow, self.storage, flags=self.flags)
115
116        self.flow.step2_exchange.assert_called_once_with(
117            'auth_code', http=None)
118
119    @mock.patch('oauth2client.tools.logging')
120    @mock.patch('oauth2client.tools.ClientRedirectServer')
121    @mock.patch('webbrowser.open')
122    def test_run_flow_webserver(
123            self, webbrowser_open_mock, server_ctor_mock, logging_mock):
124        server_ctor_mock.return_value = self.server
125        self.server.query_params = {'code': 'auth_code'}
126
127        # Successful exchange.
128        returned_credentials = tools.run_flow(
129            self.flow, self.storage, flags=self.server_flags)
130
131        self.assertEqual(self.credentials, returned_credentials)
132        self.assertEqual(self.flow.redirect_uri, 'http://localhost:8080/')
133        self.flow.step2_exchange.assert_called_once_with(
134            'auth_code', http=None)
135        self.storage.put.assert_called_once_with(self.credentials)
136        self.credentials.set_store.assert_called_once_with(self.storage)
137        self.assertTrue(self.server.handle_request.called)
138        webbrowser_open_mock.assert_called_once_with(
139            'http://example.com/auth', autoraise=True, new=1)
140
141    @mock.patch('oauth2client.tools.logging')
142    @mock.patch('oauth2client.tools.ClientRedirectServer')
143    @mock.patch('webbrowser.open')
144    def test_run_flow_webserver_exchange_error(
145            self, webbrowser_open_mock, server_ctor_mock, logging_mock):
146        server_ctor_mock.return_value = self.server
147        self.server.query_params = {'error': 'any error'}
148
149        # Exchange returned an error code.
150        with self.assertRaises(SystemExit):
151            tools.run_flow(self.flow, self.storage, flags=self.server_flags)
152
153        self.assertTrue(self.server.handle_request.called)
154
155    @mock.patch('oauth2client.tools.logging')
156    @mock.patch('oauth2client.tools.ClientRedirectServer')
157    @mock.patch('webbrowser.open')
158    def test_run_flow_webserver_no_code(
159            self, webbrowser_open_mock, server_ctor_mock, logging_mock):
160        server_ctor_mock.return_value = self.server
161        self.server.query_params = {}
162
163        # No code found in response
164        with self.assertRaises(SystemExit):
165            tools.run_flow(self.flow, self.storage, flags=self.server_flags)
166
167        self.assertTrue(self.server.handle_request.called)
168
169    @mock.patch('oauth2client.tools.logging')
170    @mock.patch('oauth2client.tools.ClientRedirectServer')
171    @mock.patch('oauth2client.tools.input')
172    def test_run_flow_webserver_fallback(
173            self, input_mock, server_ctor_mock, logging_mock):
174        server_ctor_mock.side_effect = socket.error()
175        input_mock.return_value = 'auth_code'
176
177        # It should catch the socket error and proceed as if
178        # noauth_local_webserver was specified.
179        returned_credentials = tools.run_flow(
180            self.flow, self.storage, flags=self.server_flags)
181
182        self.assertEqual(self.credentials, returned_credentials)
183        self.assertEqual(self.flow.redirect_uri, client.OOB_CALLBACK_URN)
184        self.flow.step2_exchange.assert_called_once_with(
185            'auth_code', http=None)
186        self.assertTrue(server_ctor_mock.called)
187        self.assertFalse(self.server.handle_request.called)
188
189
190class TestMessageIfMissing(unittest2.TestCase):
191    def test_message_if_missing(self):
192        self.assertIn('somefile.txt', tools.message_if_missing('somefile.txt'))
193