From ad1c2ab90cc8e6f4ead173ceb434f67571a2775c Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 1 Nov 2017 13:07:22 -0400 Subject: [PATCH 1/2] Add 'Client.create_anonymous_client' factory method. --- storage/google/cloud/storage/client.py | 18 ++++++++++++++++++ storage/setup.py | 2 +- storage/tests/unit/test_client.py | 13 +++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/storage/google/cloud/storage/client.py b/storage/google/cloud/storage/client.py index b11219385d70..13a2fe21e460 100644 --- a/storage/google/cloud/storage/client.py +++ b/storage/google/cloud/storage/client.py @@ -15,6 +15,8 @@ """Client for interacting with the Google Cloud Storage API.""" +from google.auth.credentials import AnonymousCredentials + from google.api_core import page_iterator from google.cloud._helpers import _LocalStack from google.cloud.client import ClientWithProject @@ -60,6 +62,22 @@ def __init__(self, project=None, credentials=None, _http=None): self._connection = Connection(self) self._batch_stack = _LocalStack() + @classmethod + def create_anonymous_client(cls): + """Factory: return client with anonymous credentials. + + .. note:: + + Such a client has only limited access to "public" buckets: + listing their contents and downloading their blobs. + + :rtype: :class:`google.cloud.storage.client.Client` + :returns: Instance w/ anonymous credentials and no project. + """ + client = cls(project='', credentials=AnonymousCredentials()) + client.project = None + return client + @property def _connection(self): """Get connection or batch on the client. diff --git a/storage/setup.py b/storage/setup.py index 03507af63c20..0edc3f6d99c1 100644 --- a/storage/setup.py +++ b/storage/setup.py @@ -53,7 +53,7 @@ REQUIREMENTS = [ 'google-cloud-core >= 0.28.0, < 0.29dev', 'google-api-core >= 0.1.1, < 0.2.0dev', - 'google-auth >= 1.0.0', + 'google-auth >= 1.2.0', 'google-resumable-media >= 0.3.1', 'requests >= 2.18.0', ] diff --git a/storage/tests/unit/test_client.py b/storage/tests/unit/test_client.py index b704fcf3f1e0..441e725101de 100644 --- a/storage/tests/unit/test_client.py +++ b/storage/tests/unit/test_client.py @@ -68,12 +68,25 @@ def test_ctor_connection_type(self): CREDENTIALS = _make_credentials() client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + self.assertEqual(client.project, PROJECT) self.assertIsInstance(client._connection, Connection) self.assertIs(client._connection.credentials, CREDENTIALS) self.assertIsNone(client.current_batch) self.assertEqual(list(client._batch_stack), []) + def test_create_anonymous_client(self): + from google.auth.credentials import AnonymousCredentials + from google.cloud.storage._http import Connection + + klass = self._get_target_class() + client = klass.create_anonymous_client() + + self.assertIsNone(client.project) + self.assertIsInstance(client._connection, Connection) + self.assertIsInstance( + client._connection.credentials, AnonymousCredentials) + def test__push_batch_and__pop_batch(self): from google.cloud.storage.batch import Batch From 9c685f1f81da819b1ae0e72ff6d67bc916365262 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 1 Nov 2017 14:55:52 -0400 Subject: [PATCH 2/2] Add system test for anonymous access to a public bucket's blobs. --- storage/tests/system.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/storage/tests/system.py b/storage/tests/system.py index dc606a2c1678..25736ed7188e 100644 --- a/storage/tests/system.py +++ b/storage/tests/system.py @@ -952,3 +952,15 @@ def test_notification_w_user_project(self): self.assertEqual(notifications[0].topic_name, self.TOPIC_NAME) finally: notification.delete() + + +class TestAnonymousClient(unittest.TestCase): + + PUBLIC_BUCKET = 'gcp-public-data-landsat' + + def test_access_to_public_bucket(self): + anonymous = storage.Client.create_anonymous_client() + bucket = anonymous.bucket(self.PUBLIC_BUCKET) + blob, = bucket.list_blobs(max_results=1) + with tempfile.TemporaryFile() as stream: + blob.download_to_file(stream)