def test_get_access_token_on_refresh(self): app_identity_stub = self.AppIdentityStubImpl() apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() apiproxy_stub_map.apiproxy.RegisterStub("app_identity_service", app_identity_stub) apiproxy_stub_map.apiproxy.RegisterStub( 'memcache', memcache_stub.MemcacheServiceStub()) scope = [ "http://www.googleapis.com/scope", "http://www.googleapis.com/scope2" ] credentials = appengine.AppAssertionCredentials(scope) http = http_mock.HttpMock(data=DEFAULT_RESP) credentials.refresh(http) self.assertEqual('a_token_123', credentials.access_token) json = credentials.to_json() credentials = client.Credentials.new_from_json(json) self.assertEqual( 'http://www.googleapis.com/scope http://www.googleapis.com/scope2', credentials.scope) scope = ('http://www.googleapis.com/scope ' 'http://www.googleapis.com/scope2') credentials = appengine.AppAssertionCredentials(scope) http = http_mock.HttpMock(data=DEFAULT_RESP) credentials.refresh(http) self.assertEqual('a_token_123', credentials.access_token) self.assertEqual( 'http://www.googleapis.com/scope http://www.googleapis.com/scope2', credentials.scope)
def test_get_and_put_cached(self): storage = appengine.StorageByKeyName(appengine.CredentialsModel, 'foo', 'credentials', cache=memcache) self.assertEqual(None, storage.get()) self.credentials.set_store(storage) http = http_mock.HttpMock(data=BASIC_RESP) self.credentials._refresh(http) credmodel = appengine.CredentialsModel.get_by_key_name('foo') self.assertEqual(BASIC_TOKEN, credmodel.credentials.access_token) # Now remove the item from the cache. memcache.delete('foo') # Check that getting refreshes the cache. credentials = storage.get() self.assertEqual(BASIC_TOKEN, credentials.access_token) self.assertNotEqual(None, memcache.get('foo')) # Deleting should clear the cache. storage.delete() credentials = storage.get() self.assertEqual(None, credentials) self.assertEqual(None, memcache.get('foo')) # Verify mock. self._verify_basic_refresh(http)
def test_aware(self, new_http): new_http.return_value = http_mock.HttpMock(data=DEFAULT_RESP) # An initial request to an oauth_aware decorated path should # not redirect. response = self.app.get('http://localhost/bar_path/2012/01') self.assertEqual('Hello World!', response.body) self.assertEqual('200 OK', response.status) self.assertEqual(False, self.decorator.has_credentials()) url = self.decorator.authorize_url() q = urllib.parse.parse_qs(url.split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0]) self.assertEqual('foo_client_id', q['client_id'][0]) self.assertEqual('foo_scope bar_scope', q['scope'][0]) self.assertEqual('http://localhost/bar_path/2012/01', q['state'][0].rsplit(':', 1)[0]) self.assertEqual('code', q['response_type'][0]) with mock.patch.object(appengine, '_parse_state_value', return_value='bar_path', autospec=True) as parse_state_value: # Now simulate the callback to /oauth2callback. url = self.decorator.authorize_url() response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'bar_path:xsrfkey456', }) self.assertEqual('http://localhost/bar_path', response.headers['Location']) self.assertEqual(False, self.decorator.has_credentials()) parse_state_value.assert_called_once_with('bar_path:xsrfkey456', self.current_user) # Now requesting the decorated path will have credentials. response = self.app.get('/bar_path/2012/01') self.assertEqual('200 OK', response.status) self.assertEqual('Hello World!', response.body) self.assertEqual(True, self.had_credentials) self.assertEqual('foo_refresh_token', self.found_credentials.refresh_token) self.assertEqual('foo_access_token', self.found_credentials.access_token) # Credentials should be cleared after each call. self.assertEqual(None, self.decorator.credentials) # Raising an exception still clears the Credentials. self.should_raise = Exception('') with self.assertRaises(Exception): self.app.get('/bar_path/2012/01') self.should_raise = False self.assertEqual(None, self.decorator.credentials) # Check the mocks were called. new_http.assert_called_once_with()
def test_verify_id_token_with_certs_uri_fails(self): jwt = self._create_signed_jwt() test_email = '*****@*****.**' http = http_mock.HttpMock(headers={'status': http_client.NOT_FOUND}, data=datafile('certs.json')) with self.assertRaises(client.VerifyJwtTokenError): client.verify_id_token(jwt, test_email, http=http) # Verify mocks. self._verify_http_mock(http)
def test_refresh(self): token_val = 'new_token' json_resp = '{"access_token": "%s"}' % (token_val, ) http = http_mock.HttpMock(data=json_resp) with self.app.test_request_context(): with mock.patch('flask.session'): self.oauth2.storage.put(self._generate_credentials()) self.oauth2.credentials.refresh(http) self.assertEqual(self.oauth2.storage.get().access_token, token_val)
def test_verify_id_token_with_certs_uri(self): jwt = self._create_signed_jwt() http = http_mock.HttpMock(data=datafile('certs.json')) contents = client.verify_id_token( jwt, '*****@*****.**', http=http) self.assertEqual('billy bob', contents['user']) self.assertEqual('data', contents['metadata']['meta']) # Verify mocks. self._verify_http_mock(http)
def test_get_and_put_simple(self): storage = appengine.StorageByKeyName(appengine.CredentialsModel, 'foo', 'credentials') self.assertEqual(None, storage.get()) self.credentials.set_store(storage) http = http_mock.HttpMock(data=BASIC_RESP) self.credentials._refresh(http) credmodel = appengine.CredentialsModel.get_by_key_name('foo') self.assertEqual(BASIC_TOKEN, credmodel.credentials.access_token) # Verify mock. self._verify_basic_refresh(http)
def test_raise_correct_type_of_exception(self): app_identity_stub = self.ErroringAppIdentityStubImpl() apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() apiproxy_stub_map.apiproxy.RegisterStub('app_identity_service', app_identity_stub) apiproxy_stub_map.apiproxy.RegisterStub( 'memcache', memcache_stub.MemcacheServiceStub()) scope = 'http://www.googleapis.com/scope' credentials = appengine.AppAssertionCredentials(scope) http = http_mock.HttpMock(data=DEFAULT_RESP) with self.assertRaises(client.AccessTokenRefreshError): credentials.refresh(http)
def test_verify_id_token_with_certs_uri_default_http(self): jwt = self._create_signed_jwt() http = http_mock.HttpMock(data=datafile('certs.json')) with mock.patch('oauth2client.transport._CACHED_HTTP', new=http): contents = client.verify_id_token( jwt, '*****@*****.**') self.assertEqual('billy bob', contents['user']) self.assertEqual('data', contents['metadata']['meta']) # Verify mocks. self._verify_http_mock(http)
def test_custom_metadata_root_from_env(self): headers = {'content-type': 'application/json'} http = http_mock.HttpMock(headers=headers, data='{}') fake_metadata_root = 'another.metadata.service' os.environ['GCE_METADATA_ROOT'] = fake_metadata_root reload_module(_metadata) try: _metadata.get(http, '') finally: del os.environ['GCE_METADATA_ROOT'] reload_module(_metadata) # Verify mock. self.assertEqual(http.requests, 1) expected_uri = 'http://{}/computeMetadata/v1/'.format(fake_metadata_root) self.assertEqual(http.uri, expected_uri)
def test_custom_service_account(self): scope = "http://www.googleapis.com/scope" account_id = "*****@*****.**" with mock.patch.object(app_identity, 'get_access_token', return_value=('a_token_456', None), autospec=True) as get_access_token: credentials = appengine.AppAssertionCredentials( scope, service_account_id=account_id) http = http_mock.HttpMock(data=DEFAULT_RESP) credentials.refresh(http) self.assertEqual('a_token_456', credentials.access_token) self.assertEqual(scope, credentials.scope) get_access_token.assert_called_once_with( [scope], service_account_id=account_id)
def test_get_and_put_ndb(self): # Start empty storage = appengine.StorageByKeyName(appengine.CredentialsNDBModel, 'foo', 'credentials') self.assertEqual(None, storage.get()) # Refresh storage and retrieve without using storage self.credentials.set_store(storage) http = http_mock.HttpMock(data=BASIC_RESP) self.credentials._refresh(http) credmodel = appengine.CredentialsNDBModel.get_by_id('foo') self.assertEqual(BASIC_TOKEN, credmodel.credentials.access_token) self.assertEqual(credmodel.credentials.to_json(), self.credentials.to_json()) # Verify mock. self._verify_basic_refresh(http)
def test_invalid_state(self, new_http): new_http.return_value = http_mock.HttpMock(data=DEFAULT_RESP) # Execute test after setting up mock. with mock.patch.object(appengine, '_parse_state_value', return_value=None, autospec=True): # Now simulate the callback to /oauth2callback. response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'foo_path:xsrfkey123', }) self.assertEqual('200 OK', response.status) self.assertEqual('The authorization request failed', response.body) # Check the mocks were called. new_http.assert_called_once_with()
def test_with_callable_http(self): headers = {} mock_result = object() http = http_mock.HttpMock(headers=headers, data=mock_result) result = transport.request(http, self.uri, method=self.method, body=self.body, redirections=self.redirections) self.assertEqual(result, (headers, mock_result)) # Verify mock. self.assertEqual(http.requests, 1) self.assertEqual(http.uri, self.uri) self.assertEqual(http.method, self.method) self.assertEqual(http.body, self.body) self.assertIsNone(http.headers) self.assertEqual(http.redirections, self.redirections)
def test_with_request_attr(self): mock_result = object() headers = {'foo': 'bar'} http = http_mock.HttpMock(headers=headers, data=mock_result) response, content = transport.request(http, self.uri, method=self.method, body=self.body, redirections=self.redirections) self.assertEqual(response, headers) self.assertIs(content, mock_result) # Verify mocks. self.assertEqual(http.requests, 1) self.assertEqual(http.uri, self.uri) self.assertEqual(http.method, self.method) self.assertEqual(http.body, self.body) self.assertIsNone(http.headers)
def test_refresh_token_failed_fetch(self): headers = { 'status': http_client.NOT_FOUND, 'content-type': 'application/json', } response = json.dumps({'access_token': 'a', 'expires_in': 100}) http = http_mock.HttpMock(headers=headers, data=response) credentials = gce.AppAssertionCredentials() credentials.invalid = False credentials.service_account_email = '*****@*****.**' with self.assertRaises(client.HttpAccessTokenRefreshError): credentials._refresh(http) # Verify mock. self.assertEqual(http.requests, 1) expected_uri = _metadata.METADATA_ROOT + METADATA_PATH self.assertEqual(http.uri, expected_uri) self.assertEqual(http.method, 'GET') self.assertIsNone(http.body) self.assertEqual(http.headers, _metadata.METADATA_HEADERS)
def test_get_and_put_mixed_db_storage_ndb_get(self): # Start empty storage = appengine.StorageByKeyName(appengine.CredentialsModel, 'foo', 'credentials') self.assertEqual(None, storage.get()) # Set DB store and refresh to add to storage self.credentials.set_store(storage) http = http_mock.HttpMock(data=BASIC_RESP) self.credentials._refresh(http) # Retrieve same key from NDB model to confirm mixing works credmodel = appengine.CredentialsNDBModel.get_by_id('foo') self.assertEqual(BASIC_TOKEN, credmodel.credentials.access_token) self.assertEqual(self.credentials.to_json(), credmodel.credentials.to_json()) # Verify mock. self._verify_basic_refresh(http)
def test_get_and_put_set_store_on_cache_retrieval(self): storage = appengine.StorageByKeyName(appengine.CredentialsModel, 'foo', 'credentials', cache=memcache) self.assertEqual(None, storage.get()) self.credentials.set_store(storage) storage.put(self.credentials) # Pre-bug 292 old_creds wouldn't have storage, and the _refresh # wouldn't be able to store the updated cred back into the storage. old_creds = storage.get() self.assertEqual(old_creds.access_token, 'foo') old_creds.invalid = True http = http_mock.HttpMock(data=BASIC_RESP) old_creds._refresh(http) new_creds = storage.get() self.assertEqual(new_creds.access_token, BASIC_TOKEN) # Verify mock. self._verify_basic_refresh(http)
def test_storage_delete(self, new_http): new_http.return_value = http_mock.HttpMock(data=DEFAULT_RESP) # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. response = self.app.get('/foo_path') self.assertTrue(response.status.startswith('302')) with mock.patch.object(appengine, '_parse_state_value', return_value='foo_path', autospec=True) as parse_state_value: # Now simulate the callback to /oauth2callback. response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'foo_path:xsrfkey123', }) self.assertEqual('http://localhost/foo_path', response.headers['Location']) self.assertEqual(None, self.decorator.credentials) # Now requesting the decorated path should work. response = self.app.get('/foo_path') self.assertTrue(self.had_credentials) # Credentials should be cleared after each call. self.assertEqual(None, self.decorator.credentials) # Invalidate the stored Credentials. self.found_credentials.store.delete() # Invalid Credentials should start the OAuth dance again. response = self.app.get('/foo_path') self.assertTrue(response.status.startswith('302')) parse_state_value.assert_called_once_with('foo_path:xsrfkey123', self.current_user) # Check the mocks were called. new_http.assert_called_once_with()
def test_decorator_from_client_secrets(self, new_http): new_http.return_value = http_mock.HttpMock(data=DEFAULT_RESP) # Execute test after setting up mock. decorator = appengine.OAuth2DecoratorFromClientSecrets( datafile('client_secrets.json'), scope=['foo_scope', 'bar_scope']) self._finish_setup(decorator, user_mock=UserMock) self.assertFalse(decorator._in_error) self.decorator = decorator self.test_required() http = self.decorator.http() self.assertEquals('foo_access_token', http.request.credentials.access_token) # revoke_uri is not required self.assertEqual(self.decorator._revoke_uri, 'https://oauth2.googleapis.com/revoke') self.assertEqual(self.decorator._revoke_uri, self.decorator.credentials.revoke_uri) # Check the mocks were called. new_http.assert_called_once_with()
def test_callback_view(self): self.oauth2.storage = mock.Mock() with self.app.test_client() as client: with mock.patch( 'oauth2client.transport.get_http_object') as new_http: # Set-up mock. http = http_mock.HttpMock(data=DEFAULT_RESP) new_http.return_value = http # Run tests. state = self._setup_callback_state(client) response = client.get( '/oauth2callback?state={0}&code=codez'.format(state)) self.assertEqual(response.status_code, httplib.FOUND) self.assertIn('/return_url', response.headers['Location']) self.assertIn(self.oauth2.client_secret, http.body) self.assertIn('codez', http.body) self.assertTrue(self.oauth2.storage.put.called) # Check the mocks were called. new_http.assert_called_once_with()
def test_incremental_auth_exchange(self): self._create_incremental_auth_app() with mock.patch('oauth2client.transport.get_http_object') as new_http: # Set-up mock. new_http.return_value = http_mock.HttpMock(data=DEFAULT_RESP) # Run tests. with self.app.test_client() as client: state = self._setup_callback_state( client, return_url='/return_url', # Incremental auth scopes. scopes=['one', 'two']) response = client.get( '/oauth2callback?state={0}&code=codez'.format(state)) self.assertEqual(response.status_code, httplib.FOUND) credentials = self.oauth2.credentials self.assertTrue(credentials.has_scopes(['email', 'one', 'two'])) # Check the mocks were called. new_http.assert_called_once_with()
def test_token_refresh_store_expired(self): expiration = (datetime.datetime.utcnow() - datetime.timedelta(minutes=15)) credentials = self._create_test_credentials(expiration=expiration) storage = file_module.Storage(FILENAME) storage.put(credentials) credentials = storage.get() new_cred = copy.copy(credentials) new_cred.access_token = 'bar' storage.put(new_cred) access_token = '1/3w' token_response = {'access_token': access_token, 'expires_in': 3600} response_content = json.dumps(token_response).encode('utf-8') http = http_mock.HttpMock(data=response_content) credentials._refresh(http) self.assertEquals(credentials.access_token, access_token) # Verify mocks. self.assertEqual(http.requests, 1) self.assertEqual(http.uri, credentials.token_uri) self.assertEqual(http.method, 'POST') expected_body = { 'grant_type': ['refresh_token'], 'client_id': [credentials.client_id], 'client_secret': [credentials.client_secret], 'refresh_token': [credentials.refresh_token], } self.assertEqual(urllib_parse.parse_qs(http.body), expected_body) expected_headers = { 'content-type': 'application/x-www-form-urlencoded', 'user-agent': credentials.user_agent, } self.assertEqual(http.headers, expected_headers)
def _generate_token_response_http(new_token='new_token'): token_response = json.dumps({ 'access_token': new_token, 'expires_in': '3600', }) return http_mock.HttpMock(data=token_response)
def test_required(self, new_http): new_http.return_value = http_mock.HttpMock(data=DEFAULT_RESP) # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. self.assertEqual(self.decorator.flow, None) self.assertEqual(self.decorator.credentials, None) response = self.app.get('http://localhost/foo_path') self.assertTrue(response.status.startswith('302')) q = urllib.parse.parse_qs(response.headers['Location'].split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0]) self.assertEqual('foo_client_id', q['client_id'][0]) self.assertEqual('foo_scope bar_scope', q['scope'][0]) self.assertEqual('http://localhost/foo_path', q['state'][0].rsplit(':', 1)[0]) self.assertEqual('code', q['response_type'][0]) self.assertEqual(False, self.decorator.has_credentials()) with mock.patch.object(appengine, '_parse_state_value', return_value='foo_path', autospec=True) as parse_state_value: # Now simulate the callback to /oauth2callback. response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'foo_path:xsrfkey123', }) parts = response.headers['Location'].split('?', 1) self.assertEqual('http://localhost/foo_path', parts[0]) self.assertEqual(None, self.decorator.credentials) if self.decorator._token_response_param: response_query = urllib.parse.parse_qs(parts[1]) response = response_query[ self.decorator._token_response_param][0] self.assertEqual(json.loads(DEFAULT_RESP), json.loads(urllib.parse.unquote(response))) self.assertEqual(self.decorator.flow, self.decorator._tls.flow) self.assertEqual(self.decorator.credentials, self.decorator._tls.credentials) parse_state_value.assert_called_once_with('foo_path:xsrfkey123', self.current_user) # Now requesting the decorated path should work. response = self.app.get('/foo_path') self.assertEqual('200 OK', response.status) self.assertEqual(True, self.had_credentials) self.assertEqual('foo_refresh_token', self.found_credentials.refresh_token) self.assertEqual('foo_access_token', self.found_credentials.access_token) self.assertEqual(None, self.decorator.credentials) # Raising an exception still clears the Credentials. self.should_raise = Exception('') with self.assertRaises(Exception): self.app.get('/foo_path') self.should_raise = False self.assertEqual(None, self.decorator.credentials) # Access token refresh error should start the dance again self.should_raise = client.AccessTokenRefreshError() response = self.app.get('/foo_path') self.should_raise = False self.assertTrue(response.status.startswith('302')) query_params = urllib.parse.parse_qs( response.headers['Location'].split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', query_params['redirect_uri'][0]) # Invalidate the stored Credentials. self.found_credentials.invalid = True self.found_credentials.store.put(self.found_credentials) # Invalid Credentials should start the OAuth dance again. response = self.app.get('/foo_path') self.assertTrue(response.status.startswith('302')) query_params = urllib.parse.parse_qs( response.headers['Location'].split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', query_params['redirect_uri'][0]) # Check the mocks were called. new_http.assert_called_once_with()
def test_callback_view_errors(self): # Error supplied to callback with self.app.test_client() as client: with client.session_transaction() as session: session['google_oauth2_csrf_token'] = 'tokenz' response = client.get('/oauth2callback?state={}&error=something') self.assertEqual(response.status_code, httplib.BAD_REQUEST) self.assertIn('something', response.data.decode('utf-8')) # CSRF mismatch with self.app.test_client() as client: with client.session_transaction() as session: session['google_oauth2_csrf_token'] = 'goodstate' state = json.dumps({ 'csrf_token': 'badstate', 'return_url': '/return_url' }) response = client.get( '/oauth2callback?state={0}&code=codez'.format(state)) self.assertEqual(response.status_code, httplib.BAD_REQUEST) # KeyError, no CSRF state. with self.app.test_client() as client: response = client.get('/oauth2callback?state={}&code=codez') self.assertEqual(response.status_code, httplib.BAD_REQUEST) # Code exchange error with self.app.test_client() as client: state = self._setup_callback_state(client) with mock.patch( 'oauth2client.transport.get_http_object') as new_http: # Set-up mock. new_http.return_value = http_mock.HttpMock( headers={'status': httplib.INTERNAL_SERVER_ERROR}, data=DEFAULT_RESP) # Run tests. response = client.get( '/oauth2callback?state={0}&code=codez'.format(state)) self.assertEqual(response.status_code, httplib.BAD_REQUEST) # Check the mocks were called. new_http.assert_called_once_with() # Invalid state json with self.app.test_client() as client: with client.session_transaction() as session: session['google_oauth2_csrf_token'] = 'tokenz' state = '[{' response = client.get( '/oauth2callback?state={0}&code=codez'.format(state)) self.assertEqual(response.status_code, httplib.BAD_REQUEST) # Missing flow. with self.app.test_client() as client: with client.session_transaction() as session: session['google_oauth2_csrf_token'] = 'tokenz' state = json.dumps({ 'csrf_token': 'tokenz', 'return_url': '/return_url' }) response = client.get( '/oauth2callback?state={0}&code=codez'.format(state)) self.assertEqual(response.status_code, httplib.BAD_REQUEST)
def request_mock(status, content_type, content): headers = {'status': status, 'content-type': content_type} http = http_mock.HttpMock(headers=headers, data=content.encode('utf-8')) return http