diff --git a/gcloud/connection.py b/gcloud/connection.py index d411b04e4b82..c74001491082 100644 --- a/gcloud/connection.py +++ b/gcloud/connection.py @@ -21,7 +21,6 @@ import httplib2 -from gcloud.credentials import get_credentials from gcloud.exceptions import make_exception @@ -94,6 +93,25 @@ def http(self): self._http = self._credentials.authorize(self._http) return self._http + @staticmethod + def _create_scoped_credentials(credentials, scope): + """Create a scoped set of credentials if it is required. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :param credentials: The OAuth2 Credentials to add a scope to. + + :type scope: list of URLs + :param scope: the effective service auth scopes for the connection. + + :rtype: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :returns: A new credentials object that has a scope added (if needed). + """ + if credentials and credentials.create_scoped_required(): + credentials = credentials.create_scoped(scope) + return credentials + class JSONConnection(Connection): """A connection to a Google JSON-based API. @@ -320,20 +338,3 @@ def api_request(self, method, path, query_params=None, return json.loads(content) return content - - -def get_scoped_connection(klass, scopes): - """Create a scoped connection to GCloud. - - :type klass: subclass of :class:`gcloud.connection.Connection` - :param klass: the specific ``Connection`` class to instantiate. - - :type scopes: list of URLs - :param scopes: the effective service auth scopes for the connection. - - :rtype: instance of ``klass`` - :returns: A connection defined with the proper credentials. - """ - implicit_credentials = get_credentials() - scoped_credentials = implicit_credentials.create_scoped(scopes) - return klass(credentials=scoped_credentials) diff --git a/gcloud/datastore/__init__.py b/gcloud/datastore/__init__.py index f4f5319e7319..f5bfa7de4cc4 100644 --- a/gcloud/datastore/__init__.py +++ b/gcloud/datastore/__init__.py @@ -48,7 +48,6 @@ when race conditions may occur. """ -from gcloud.datastore._implicit_environ import SCOPE from gcloud.datastore._implicit_environ import get_connection from gcloud.datastore._implicit_environ import get_default_connection from gcloud.datastore._implicit_environ import get_default_dataset_id @@ -59,6 +58,7 @@ from gcloud.datastore.api import get from gcloud.datastore.api import put from gcloud.datastore.batch import Batch +from gcloud.datastore.connection import SCOPE from gcloud.datastore.connection import Connection from gcloud.datastore.dataset import Dataset from gcloud.datastore.entity import Entity diff --git a/gcloud/datastore/_implicit_environ.py b/gcloud/datastore/_implicit_environ.py index 0d18b7540bf0..d664d345161e 100644 --- a/gcloud/datastore/_implicit_environ.py +++ b/gcloud/datastore/_implicit_environ.py @@ -23,14 +23,10 @@ from gcloud._helpers import _app_engine_id from gcloud._helpers import _compute_engine_id from gcloud._helpers import _lazy_property_deco -from gcloud.connection import get_scoped_connection +from gcloud.credentials import get_credentials from gcloud.datastore.connection import Connection -SCOPE = ('https://www.googleapis.com/auth/datastore', - 'https://www.googleapis.com/auth/userinfo.email') -"""The scopes required for authenticating as a Cloud Datastore consumer.""" - _DATASET_ENV_VAR_NAME = 'GCLOUD_DATASET_ID' _GCD_DATASET_ENV_VAR_NAME = 'DATASTORE_DATASET' @@ -126,7 +122,8 @@ def get_connection(): :rtype: :class:`gcloud.datastore.connection.Connection` :returns: A connection defined with the proper credentials. """ - return get_scoped_connection(Connection, SCOPE) + credentials = get_credentials() + return Connection(credentials=credentials) def set_default_connection(connection=None): diff --git a/gcloud/datastore/connection.py b/gcloud/datastore/connection.py index 1fa4e42fcdac..9a9f9ccc583f 100644 --- a/gcloud/datastore/connection.py +++ b/gcloud/datastore/connection.py @@ -21,6 +21,10 @@ from gcloud.datastore import _datastore_v1_pb2 as datastore_pb +SCOPE = ('https://www.googleapis.com/auth/datastore', + 'https://www.googleapis.com/auth/userinfo.email') +"""The scopes required for authenticating as a Cloud Datastore consumer.""" + _GCD_HOST_ENV_VAR_NAME = 'DATASTORE_HOST' @@ -46,6 +50,7 @@ class Connection(connection.Connection): """A template for the URL of a particular API call.""" def __init__(self, credentials=None, http=None, api_base_url=None): + credentials = self._create_scoped_credentials(credentials, SCOPE) super(Connection, self).__init__(credentials=credentials, http=http) if api_base_url is None: api_base_url = os.getenv(_GCD_HOST_ENV_VAR_NAME, diff --git a/gcloud/datastore/test__implicit_environ.py b/gcloud/datastore/test__implicit_environ.py index 9f2d7018041e..63f2c8c270f7 100644 --- a/gcloud/datastore/test__implicit_environ.py +++ b/gcloud/datastore/test__implicit_environ.py @@ -305,7 +305,7 @@ def _callFUT(self): def test_it(self): from gcloud import credentials - from gcloud.datastore._implicit_environ import SCOPE + from gcloud.datastore.connection import SCOPE from gcloud.datastore.connection import Connection from gcloud.test_credentials import _Client from gcloud._testing import _Monkey diff --git a/gcloud/datastore/test_connection.py b/gcloud/datastore/test_connection.py index f91ae06b7482..6796a493e360 100644 --- a/gcloud/datastore/test_connection.py +++ b/gcloud/datastore/test_connection.py @@ -97,7 +97,12 @@ def test_ctor_defaults(self): self.assertEqual(conn.credentials, None) def test_ctor_explicit(self): - creds = object() + class Creds(object): + + def create_scoped_required(self): + return False + + creds = Creds() conn = self._makeOne(creds) self.assertTrue(conn.credentials is creds) @@ -122,6 +127,10 @@ class Creds(object): def authorize(self, http): self._called_with = http return authorized + + def create_scoped_required(self): + return False + creds = Creds() conn = self._makeOne(creds) self.assertTrue(conn.http is authorized) diff --git a/gcloud/pubsub/__init__.py b/gcloud/pubsub/__init__.py index 9f806203f1e1..bef2522e7228 100644 --- a/gcloud/pubsub/__init__.py +++ b/gcloud/pubsub/__init__.py @@ -26,20 +26,17 @@ from gcloud._helpers import get_default_project from gcloud._helpers import set_default_project -from gcloud.connection import get_scoped_connection +from gcloud.credentials import get_credentials from gcloud.pubsub import _implicit_environ from gcloud.pubsub._implicit_environ import get_default_connection from gcloud.pubsub.api import list_subscriptions from gcloud.pubsub.api import list_topics +from gcloud.pubsub.connection import SCOPE from gcloud.pubsub.connection import Connection from gcloud.pubsub.subscription import Subscription from gcloud.pubsub.topic import Topic -SCOPE = ('https://www.googleapis.com/auth/pubsub', - 'https://www.googleapis.com/auth/cloud-platform') - - def set_default_connection(connection=None): """Set default connection either explicitly or implicitly as fall-back. @@ -78,4 +75,5 @@ def get_connection(): :rtype: :class:`gcloud.pubsub.connection.Connection` :returns: A connection defined with the proper credentials. """ - return get_scoped_connection(Connection, SCOPE) + credentials = get_credentials() + return Connection(credentials=credentials) diff --git a/gcloud/pubsub/connection.py b/gcloud/pubsub/connection.py index a804c76024ee..d73ed9cfeebd 100644 --- a/gcloud/pubsub/connection.py +++ b/gcloud/pubsub/connection.py @@ -17,6 +17,11 @@ from gcloud import connection as base_connection +SCOPE = ('https://www.googleapis.com/auth/pubsub', + 'https://www.googleapis.com/auth/cloud-platform') +"""The scopes required for authenticating as a Cloud Pub/Sub consumer.""" + + class Connection(base_connection.JSONConnection): """A connection to Google Cloud Pubsub via the JSON REST API.""" @@ -28,3 +33,7 @@ class Connection(base_connection.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" + + def __init__(self, credentials=None, http=None): + credentials = self._create_scoped_credentials(credentials, SCOPE) + super(Connection, self).__init__(credentials=credentials, http=http) diff --git a/gcloud/storage/__init__.py b/gcloud/storage/__init__.py index 214571d0008e..8097e51c9b6c 100644 --- a/gcloud/storage/__init__.py +++ b/gcloud/storage/__init__.py @@ -43,7 +43,6 @@ from gcloud._helpers import get_default_project from gcloud._helpers import set_default_project from gcloud.storage import _implicit_environ -from gcloud.storage._implicit_environ import SCOPE from gcloud.storage._implicit_environ import get_connection from gcloud.storage._implicit_environ import get_default_bucket from gcloud.storage._implicit_environ import get_default_connection @@ -55,6 +54,7 @@ from gcloud.storage.batch import Batch from gcloud.storage.blob import Blob from gcloud.storage.bucket import Bucket +from gcloud.storage.connection import SCOPE from gcloud.storage.connection import Connection diff --git a/gcloud/storage/_implicit_environ.py b/gcloud/storage/_implicit_environ.py index 74899ff9fb9a..7c630df6a3e2 100644 --- a/gcloud/storage/_implicit_environ.py +++ b/gcloud/storage/_implicit_environ.py @@ -20,15 +20,10 @@ from gcloud._helpers import _lazy_property_deco -from gcloud.connection import get_scoped_connection +from gcloud.credentials import get_credentials from gcloud.storage.connection import Connection -SCOPE = ('https://www.googleapis.com/auth/devstorage.full_control', - 'https://www.googleapis.com/auth/devstorage.read_only', - 'https://www.googleapis.com/auth/devstorage.read_write') - - class _DefaultsContainer(object): """Container for defaults. @@ -83,7 +78,8 @@ def get_connection(): :rtype: :class:`gcloud.storage.connection.Connection` :returns: A connection defined with the proper credentials. """ - return get_scoped_connection(Connection, SCOPE) + credentials = get_credentials() + return Connection(credentials=credentials) def set_default_connection(connection=None): diff --git a/gcloud/storage/connection.py b/gcloud/storage/connection.py index 4a0eb98c3b04..2740a47063db 100644 --- a/gcloud/storage/connection.py +++ b/gcloud/storage/connection.py @@ -17,6 +17,12 @@ from gcloud import connection as base_connection +SCOPE = ('https://www.googleapis.com/auth/devstorage.full_control', + 'https://www.googleapis.com/auth/devstorage.read_only', + 'https://www.googleapis.com/auth/devstorage.read_write') +"""The scopes required for authenticating as a Cloud Storage consumer.""" + + class Connection(base_connection.JSONConnection): """A connection to Google Cloud Storage via the JSON REST API.""" @@ -28,3 +34,7 @@ class Connection(base_connection.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/storage/{api_version}{path}' """A template for the URL of a particular API call.""" + + def __init__(self, credentials=None, http=None): + credentials = self._create_scoped_credentials(credentials, SCOPE) + super(Connection, self).__init__(credentials=credentials, http=http) diff --git a/gcloud/test_connection.py b/gcloud/test_connection.py index 06f70f3e8890..706e939d1cac 100644 --- a/gcloud/test_connection.py +++ b/gcloud/test_connection.py @@ -372,31 +372,3 @@ def __init__(self, headers, content): def request(self, **kw): self._called_with = kw return self._response, self._content - - -class Test_get_scoped_connection(unittest2.TestCase): - - def _callFUT(self, klass, scopes): - from gcloud.connection import get_scoped_connection - return get_scoped_connection(klass, scopes) - - def test_it(self): - from gcloud import credentials - from gcloud.test_credentials import _Client - from gcloud._testing import _Monkey - - class _Connection(object): - def __init__(self, credentials): - self._credentials = credentials - - SCOPES = ('https://www.googleapis.com/auth/example', - 'https://www.googleapis.com/auth/userinfo.email') - - client = _Client() - with _Monkey(credentials, client=client): - found = self._callFUT(_Connection, SCOPES) - - self.assertTrue(isinstance(found, _Connection)) - self.assertTrue(found._credentials is client._signed) - self.assertEqual(found._credentials._scopes, SCOPES) - self.assertTrue(client._get_app_default_called) diff --git a/gcloud/test_credentials.py b/gcloud/test_credentials.py index 6e743cd83269..732861adbf1a 100644 --- a/gcloud/test_credentials.py +++ b/gcloud/test_credentials.py @@ -396,6 +396,9 @@ def create_scoped(self, scopes): self._scopes = scopes return self + def create_scoped_required(self): + return True + class _Client(object): def __init__(self):