Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'ACL.save_predefined' method. #1217

Merged
merged 2 commits into from
Nov 17, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 67 additions & 11 deletions gcloud/storage/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@ class ACL(object):
"""Container class representing a list of access controls."""

_URL_PATH_ELEM = 'acl'
_PREDEFINED_QUERY_PARAM = 'predefinedAcl'

_PREDEFINED_ACLS = frozenset([
"private",

This comment was marked as spam.

This comment was marked as spam.

"project-private",
"public-read",
"public-read-write",
"authenticated-read",
"bucket-owner-read",
"bucket-owner-full-control",
])
"""See:
https://cloud.google.com/storage/docs/access-control#predefined-acl
"""

loaded = False

# Subclasses must override to provide these attributes (typically,
Expand Down Expand Up @@ -385,6 +400,39 @@ def reload(self, client=None):
for entry in found.get('items', ()):
self.add_entity(self.entity_from_dict(entry))

def _save(self, acl, predefined, client):

This comment was marked as spam.

"""Helper for :meth:`save` and :meth:`save_predefined`.

:type acl: :class:`gcloud.storage.acl.ACL`, or a compatible list.
:param acl: The ACL object to save. If left blank, this will save
current entries.

:type predefined: string or None
:param predefined: An identifier for a predefined ACL. Must be one
of the keys in :attr:`_PREDEFINED_ACLS`
If passed, `acl` must be None.

:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to the ``client`` stored on the ACL's parent.
"""
query_params = {'projection': 'full'}
if predefined is not None:
acl = []
query_params[self._PREDEFINED_QUERY_PARAM] = predefined

path = self.save_path
client = self._require_client(client)
result = client.connection.api_request(
method='PATCH',
path=path,
data={self._URL_PATH_ELEM: list(acl)},
query_params=query_params)
self.entities.clear()
for entry in result.get(self._URL_PATH_ELEM, ()):
self.add_entity(self.entity_from_dict(entry))
self.loaded = True

def save(self, acl=None, client=None):
"""Save this ACL for the current bucket.

Expand All @@ -403,17 +451,24 @@ def save(self, acl=None, client=None):
save_to_backend = True

if save_to_backend:
path = self.save_path
client = self._require_client(client)
result = client.connection.api_request(
method='PATCH',
path=path,
data={self._URL_PATH_ELEM: list(acl)},
query_params={'projection': 'full'})
self.entities.clear()
for entry in result.get(self._URL_PATH_ELEM, ()):
self.add_entity(self.entity_from_dict(entry))
self.loaded = True
self._save(acl, None, client)

def save_predefined(self, predefined, client=None):
"""Save this ACL for the current bucket using a predefined ACL.

:type predefined: string
:param predefined: An identifier for a predefined ACL. Must be one
of the keys in :attr:`_PREDEFINED_ACLS`
If passed, `acl` must be None.

:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to the ``client`` stored on the ACL's parent.
"""
if predefined not in self._PREDEFINED_ACLS:
raise ValueError("Invalid predefined ACL: %s" % (predefined,))

self._save(None, predefined, client)

def clear(self, client=None):
"""Remove all ACL entries.
Expand Down Expand Up @@ -461,6 +516,7 @@ class DefaultObjectACL(BucketACL):
"""A class representing the default object ACL for a bucket."""

_URL_PATH_ELEM = 'defaultObjectAcl'
_PREDEFINED_QUERY_PARAM = 'predefinedDefaultObjectAcl'


class ObjectACL(ACL):
Expand Down
51 changes: 49 additions & 2 deletions gcloud/storage/test_acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ def test_save_existing_missing_none_passed(self):
self.assertEqual(kw[0]['data'], {'acl': []})
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})

def test_save_no_arg(self):
def test_save_no_acl(self):
ROLE = 'role'
AFTER = [{'entity': 'allUsers', 'role': ROLE}]
connection = _Connection({'acl': AFTER})
Expand All @@ -599,7 +599,7 @@ def test_save_no_arg(self):
self.assertEqual(kw[0]['data'], {'acl': AFTER})
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})

def test_save_w_arg(self):
def test_save_w_acl(self):
ROLE1 = 'role1'
ROLE2 = 'role2'
STICKY = {'entity': 'allUsers', 'role': ROLE2}
Expand All @@ -621,6 +621,53 @@ def test_save_w_arg(self):
self.assertEqual(kw[0]['data'], {'acl': new_acl})
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})

def test_save_prefefined_invalid(self):
connection = _Connection()
client = _Client(connection)
acl = self._makeOne()
acl.save_path = '/testing'
acl.loaded = True
with self.assertRaises(ValueError):
acl.save_predefined('bogus', client=client)

def test_save_predefined_valid(self):
PREDEFINED = 'private'
connection = _Connection({'acl': []})
client = _Client(connection)
acl = self._makeOne()
acl.save_path = '/testing'
acl.loaded = True
acl.save_predefined(PREDEFINED, client=client)
entries = list(acl)
self.assertEqual(len(entries), 0)
kw = connection._requested
self.assertEqual(len(kw), 1)
self.assertEqual(kw[0]['method'], 'PATCH')
self.assertEqual(kw[0]['path'], '/testing')
self.assertEqual(kw[0]['data'], {'acl': []})
self.assertEqual(kw[0]['query_params'],
{'projection': 'full', 'predefinedAcl': PREDEFINED})

def test_save_predefined_valid_w_alternate_query_param(self):
# Cover case where subclass overrides _PREDEFINED_QUERY_PARAM
PREDEFINED = 'private'
connection = _Connection({'acl': []})
client = _Client(connection)
acl = self._makeOne()
acl.save_path = '/testing'
acl.loaded = True
acl._PREDEFINED_QUERY_PARAM = 'alternate'
acl.save_predefined(PREDEFINED, client=client)
entries = list(acl)
self.assertEqual(len(entries), 0)
kw = connection._requested
self.assertEqual(len(kw), 1)
self.assertEqual(kw[0]['method'], 'PATCH')
self.assertEqual(kw[0]['path'], '/testing')
self.assertEqual(kw[0]['data'], {'acl': []})
self.assertEqual(kw[0]['query_params'],
{'projection': 'full', 'alternate': PREDEFINED})

def test_clear(self):
ROLE1 = 'role1'
ROLE2 = 'role2'
Expand Down