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:
Danny Hermes
2016-02-18 20:20:39 -08:00
parent f9e16edc45
commit 6319e765db
3 changed files with 50 additions and 34 deletions

View File

@@ -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):

View File

@@ -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)

View File

@@ -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: