Stop sending scopes in token requests on GCE
Also check to warn a user when a scope is passed (since scopes have no effect for GCE service accounts).
This commit is contained in:
@@ -1551,7 +1551,7 @@ def _get_application_default_credential_GAE():
|
||||
def _get_application_default_credential_GCE():
|
||||
from oauth2client.contrib.gce import AppAssertionCredentials
|
||||
|
||||
return AppAssertionCredentials([])
|
||||
return AppAssertionCredentials()
|
||||
|
||||
|
||||
class AssertionCredentials(GoogleCredentials):
|
||||
|
||||
@@ -19,6 +19,8 @@ Utilities for making it easier to use OAuth 2.0 on Google Compute Engine.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from six.moves import http_client
|
||||
from six.moves import urllib
|
||||
|
||||
@@ -34,7 +36,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# URI Template for the endpoint that returns access_tokens.
|
||||
META = ('http://metadata.google.internal/computeMetadata/v1/instance/'
|
||||
'service-accounts/default/token{?scope}')
|
||||
'service-accounts/default/token')
|
||||
_SCOPES_WARNING = """\
|
||||
You have requested explicit scopes to be used with a GCE service account.
|
||||
Using this argument will have no effect on the actual scopes for tokens
|
||||
requested. These scopes are set at VM instance creation time and
|
||||
can't be overridden in the request.
|
||||
"""
|
||||
|
||||
|
||||
class AppAssertionCredentials(AssertionCredentials):
|
||||
@@ -51,13 +59,19 @@ class AppAssertionCredentials(AssertionCredentials):
|
||||
"""
|
||||
|
||||
@util.positional(2)
|
||||
def __init__(self, scope, **kwargs):
|
||||
def __init__(self, scope='', **kwargs):
|
||||
"""Constructor for AppAssertionCredentials
|
||||
|
||||
Args:
|
||||
scope: string or iterable of strings, scope(s) of the credentials
|
||||
being requested.
|
||||
being requested. Using this argument will have no effect on
|
||||
the actual scopes for tokens requested. These scopes are
|
||||
set at VM instance creation time and won't change.
|
||||
"""
|
||||
if scope:
|
||||
warnings.warn(_SCOPES_WARNING)
|
||||
# This is just provided for backwards compatibility, but is not
|
||||
# used by this class.
|
||||
self.scope = util.scopes_to_string(scope)
|
||||
self.kwargs = kwargs
|
||||
|
||||
@@ -83,10 +97,8 @@ class AppAssertionCredentials(AssertionCredentials):
|
||||
Raises:
|
||||
HttpAccessTokenRefreshError: When the refresh fails.
|
||||
"""
|
||||
query = '?scope=%s' % urllib.parse.quote(self.scope, '')
|
||||
uri = META.replace('{?scope}', query)
|
||||
response, content = http_request(
|
||||
uri, headers={'Metadata-Flavor': 'Google'})
|
||||
META, headers={'Metadata-Flavor': 'Google'})
|
||||
content = _from_bytes(content)
|
||||
if response.status == http_client.OK:
|
||||
try:
|
||||
@@ -107,7 +119,7 @@ class AppAssertionCredentials(AssertionCredentials):
|
||||
'Cannot serialize credentials for GCE service accounts.')
|
||||
|
||||
def create_scoped_required(self):
|
||||
return not self.scope
|
||||
return False
|
||||
|
||||
def create_scoped(self, scopes):
|
||||
return AppAssertionCredentials(scopes, **self.kwargs)
|
||||
|
||||
@@ -25,6 +25,7 @@ from oauth2client._helpers import _to_bytes
|
||||
from oauth2client.client import AccessTokenRefreshError
|
||||
from oauth2client.client import Credentials
|
||||
from oauth2client.client import save_to_well_known_file
|
||||
from oauth2client.contrib.gce import _SCOPES_WARNING
|
||||
from oauth2client.contrib.gce import AppAssertionCredentials
|
||||
|
||||
|
||||
@@ -34,16 +35,23 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
||||
class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
|
||||
def test_constructor(self):
|
||||
credentials = AppAssertionCredentials(foo='bar')
|
||||
self.assertEqual(credentials.scope, '')
|
||||
self.assertEqual(credentials.kwargs, {'foo': 'bar'})
|
||||
self.assertEqual(credentials.assertion_type, None)
|
||||
|
||||
@mock.patch('warnings.warn')
|
||||
def test_constructor_with_scopes(self, warn_mock):
|
||||
scope = 'http://example.com/a http://example.com/b'
|
||||
scopes = scope.split()
|
||||
credentials = AppAssertionCredentials(scope=scopes, foo='bar')
|
||||
self.assertEqual(credentials.scope, scope)
|
||||
self.assertEqual(credentials.kwargs, {'foo': 'bar'})
|
||||
self.assertEqual(credentials.assertion_type, None)
|
||||
warn_mock.assert_called_once_with(_SCOPES_WARNING)
|
||||
|
||||
def test_to_json_and_from_json(self):
|
||||
credentials = AppAssertionCredentials(
|
||||
scope=['http://example.com/a', 'http://example.com/b'])
|
||||
credentials = AppAssertionCredentials()
|
||||
json = credentials.to_json()
|
||||
credentials_from_json = Credentials.new_from_json(json)
|
||||
self.assertEqual(credentials.access_token,
|
||||
@@ -58,8 +66,7 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
http.request = mock.MagicMock(
|
||||
return_value=(mock.Mock(status=http_client.OK), return_val))
|
||||
|
||||
scopes = ['http://example.com/a', 'http://example.com/b']
|
||||
credentials = AppAssertionCredentials(scope=scopes)
|
||||
credentials = AppAssertionCredentials()
|
||||
self.assertEquals(None, credentials.access_token)
|
||||
credentials.refresh(http)
|
||||
self.assertEquals(access_token, credentials.access_token)
|
||||
@@ -67,10 +74,8 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
base_metadata_uri = (
|
||||
'http://metadata.google.internal/computeMetadata/v1/instance/'
|
||||
'service-accounts/default/token')
|
||||
escaped_scopes = urllib.parse.quote(' '.join(scopes), safe='')
|
||||
request_uri = base_metadata_uri + '?scope=' + escaped_scopes
|
||||
http.request.assert_called_once_with(
|
||||
request_uri, headers={'Metadata-Flavor': 'Google'})
|
||||
base_metadata_uri, headers={'Metadata-Flavor': 'Google'})
|
||||
|
||||
def test_refresh_success(self):
|
||||
self._refresh_success_helper(bytes_response=False)
|
||||
@@ -84,8 +89,7 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
http.request = mock.MagicMock(
|
||||
return_value=(mock.Mock(status=http_client.OK), content))
|
||||
|
||||
credentials = AppAssertionCredentials(
|
||||
scope=['http://example.com/a', 'http://example.com/b'])
|
||||
credentials = AppAssertionCredentials()
|
||||
self.assertRaises(AccessTokenRefreshError, credentials.refresh, http)
|
||||
|
||||
def test_refresh_failure_400(self):
|
||||
@@ -94,9 +98,7 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
http.request = mock.MagicMock(
|
||||
return_value=(mock.Mock(status=http_client.BAD_REQUEST), content))
|
||||
|
||||
credentials = AppAssertionCredentials(
|
||||
scope=['http://example.com/a', 'http://example.com/b'])
|
||||
|
||||
credentials = AppAssertionCredentials()
|
||||
exception_caught = None
|
||||
try:
|
||||
credentials.refresh(http)
|
||||
@@ -112,9 +114,7 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
http.request = mock.MagicMock(
|
||||
return_value=(mock.Mock(status=http_client.NOT_FOUND), content))
|
||||
|
||||
credentials = AppAssertionCredentials(
|
||||
scope=['http://example.com/a', 'http://example.com/b'])
|
||||
|
||||
credentials = AppAssertionCredentials()
|
||||
exception_caught = None
|
||||
try:
|
||||
credentials.refresh(http)
|
||||
@@ -127,24 +127,28 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
self.assertEqual(str(exception_caught), expanded_content)
|
||||
|
||||
def test_serialization_data(self):
|
||||
credentials = AppAssertionCredentials(scope=[])
|
||||
credentials = AppAssertionCredentials()
|
||||
self.assertRaises(NotImplementedError, getattr,
|
||||
credentials, 'serialization_data')
|
||||
|
||||
def test_create_scoped_required_without_scopes(self):
|
||||
credentials = AppAssertionCredentials([])
|
||||
self.assertTrue(credentials.create_scoped_required())
|
||||
|
||||
def test_create_scoped_required_with_scopes(self):
|
||||
credentials = AppAssertionCredentials(['dummy_scope'])
|
||||
credentials = AppAssertionCredentials()
|
||||
self.assertFalse(credentials.create_scoped_required())
|
||||
|
||||
def test_create_scoped(self):
|
||||
credentials = AppAssertionCredentials([])
|
||||
@mock.patch('warnings.warn')
|
||||
def test_create_scoped_required_with_scopes(self, warn_mock):
|
||||
credentials = AppAssertionCredentials(['dummy_scope'])
|
||||
self.assertFalse(credentials.create_scoped_required())
|
||||
warn_mock.assert_called_once_with(_SCOPES_WARNING)
|
||||
|
||||
@mock.patch('warnings.warn')
|
||||
def test_create_scoped(self, warn_mock):
|
||||
credentials = AppAssertionCredentials()
|
||||
new_credentials = credentials.create_scoped(['dummy_scope'])
|
||||
self.assertNotEqual(credentials, new_credentials)
|
||||
self.assertTrue(isinstance(new_credentials, AppAssertionCredentials))
|
||||
self.assertEqual('dummy_scope', new_credentials.scope)
|
||||
warn_mock.assert_called_once_with(_SCOPES_WARNING)
|
||||
|
||||
def test_get_access_token(self):
|
||||
http = mock.MagicMock()
|
||||
@@ -152,14 +156,14 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
return_value=(mock.Mock(status=http_client.OK),
|
||||
'{"access_token": "this-is-a-token"}'))
|
||||
|
||||
credentials = AppAssertionCredentials(['dummy_scope'])
|
||||
credentials = AppAssertionCredentials()
|
||||
token = credentials.get_access_token(http=http)
|
||||
self.assertEqual('this-is-a-token', token.access_token)
|
||||
self.assertEqual(None, token.expires_in)
|
||||
|
||||
http.request.assert_called_once_with(
|
||||
'http://metadata.google.internal/computeMetadata/v1/instance/'
|
||||
'service-accounts/default/token?scope=dummy_scope',
|
||||
'service-accounts/default/token',
|
||||
headers={'Metadata-Flavor': 'Google'})
|
||||
|
||||
def test_save_to_well_known_file(self):
|
||||
@@ -167,7 +171,7 @@ class AppAssertionCredentialsTests(unittest.TestCase):
|
||||
ORIGINAL_ISDIR = os.path.isdir
|
||||
try:
|
||||
os.path.isdir = lambda path: True
|
||||
credentials = AppAssertionCredentials([])
|
||||
credentials = AppAssertionCredentials()
|
||||
self.assertRaises(NotImplementedError, save_to_well_known_file,
|
||||
credentials)
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user