Skip to content

Commit 3377419

Browse files
tseaverchemelnucfin
authored andcommitted
Enable anonymous access to blobs in public buckets (#4315)
1 parent af35095 commit 3377419

File tree

4 files changed

+44
-1
lines changed

4 files changed

+44
-1
lines changed

storage/google/cloud/storage/client.py

+18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"""Client for interacting with the Google Cloud Storage API."""
1616

1717

18+
from google.auth.credentials import AnonymousCredentials
19+
1820
from google.api_core import page_iterator
1921
from google.cloud._helpers import _LocalStack
2022
from google.cloud.client import ClientWithProject
@@ -60,6 +62,22 @@ def __init__(self, project=None, credentials=None, _http=None):
6062
self._connection = Connection(self)
6163
self._batch_stack = _LocalStack()
6264

65+
@classmethod
66+
def create_anonymous_client(cls):
67+
"""Factory: return client with anonymous credentials.
68+
69+
.. note::
70+
71+
Such a client has only limited access to "public" buckets:
72+
listing their contents and downloading their blobs.
73+
74+
:rtype: :class:`google.cloud.storage.client.Client`
75+
:returns: Instance w/ anonymous credentials and no project.
76+
"""
77+
client = cls(project='<none>', credentials=AnonymousCredentials())
78+
client.project = None
79+
return client
80+
6381
@property
6482
def _connection(self):
6583
"""Get connection or batch on the client.

storage/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
REQUIREMENTS = [
5454
'google-cloud-core >= 0.28.0, < 0.29dev',
5555
'google-api-core >= 0.1.1, < 0.2.0dev',
56-
'google-auth >= 1.0.0',
56+
'google-auth >= 1.2.0',
5757
'google-resumable-media >= 0.3.1',
5858
'requests >= 2.18.0',
5959
]

storage/tests/system.py

+12
Original file line numberDiff line numberDiff line change
@@ -952,3 +952,15 @@ def test_notification_w_user_project(self):
952952
self.assertEqual(notifications[0].topic_name, self.TOPIC_NAME)
953953
finally:
954954
notification.delete()
955+
956+
957+
class TestAnonymousClient(unittest.TestCase):
958+
959+
PUBLIC_BUCKET = 'gcp-public-data-landsat'
960+
961+
def test_access_to_public_bucket(self):
962+
anonymous = storage.Client.create_anonymous_client()
963+
bucket = anonymous.bucket(self.PUBLIC_BUCKET)
964+
blob, = bucket.list_blobs(max_results=1)
965+
with tempfile.TemporaryFile() as stream:
966+
blob.download_to_file(stream)

storage/tests/unit/test_client.py

+13
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,25 @@ def test_ctor_connection_type(self):
6868
CREDENTIALS = _make_credentials()
6969

7070
client = self._make_one(project=PROJECT, credentials=CREDENTIALS)
71+
7172
self.assertEqual(client.project, PROJECT)
7273
self.assertIsInstance(client._connection, Connection)
7374
self.assertIs(client._connection.credentials, CREDENTIALS)
7475
self.assertIsNone(client.current_batch)
7576
self.assertEqual(list(client._batch_stack), [])
7677

78+
def test_create_anonymous_client(self):
79+
from google.auth.credentials import AnonymousCredentials
80+
from google.cloud.storage._http import Connection
81+
82+
klass = self._get_target_class()
83+
client = klass.create_anonymous_client()
84+
85+
self.assertIsNone(client.project)
86+
self.assertIsInstance(client._connection, Connection)
87+
self.assertIsInstance(
88+
client._connection.credentials, AnonymousCredentials)
89+
7790
def test__push_batch_and__pop_batch(self):
7891
from google.cloud.storage.batch import Batch
7992

0 commit comments

Comments
 (0)