Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
189 changes: 93 additions & 96 deletions gcloud/storage/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ def revoke_owner(self):
class ACL(object):
"""Container class representing a list of access controls."""

_URL_PATH_ELEM = 'acl'
loaded = False

def __init__(self):
Expand Down Expand Up @@ -345,117 +346,140 @@ def get_entities(self):
self._ensure_loaded()
return list(self.entities.values())

def reload(self):
"""Reload the ACL data from Cloud Storage.
@property
def connection(self):
"""Compute the connection for API requests for this ACL.

This comment was marked as spam.


This is a virtual method, expected to be implemented by subclasses.

:raises: :class:`NotImplementedError`
:raises: :class:`NotImplementedError` if ``_connection`` attribute
is not set on the instance.

"""
# Allow override for testing
connection = getattr(self, '_connection', None)

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

if connection is not None:
return connection
raise NotImplementedError

def save(self, acl=None):
"""A method to be overridden by subclasses.
@property
def reload_path(self):
"""Compute the path for GET API requests for this ACL.

: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.
This is a virtual method, expected to be implemented by subclasses.

:raises: NotImplementedError
:raises: :class:`NotImplementedError` if ``_reload_path`` attribute
is not set on the instance.
"""
# Allow override for testing
path = getattr(self, '_reload_path', None)

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

if path is not None:
return path
raise NotImplementedError

def clear(self):
"""Remove all entities from the ACL."""
raise NotImplementedError

@property
def save_path(self):
"""Compute the path for PATCH API requests for this ACL.

class BucketACL(ACL):
"""An ACL specifically for a bucket."""
This is a virtual method, expected to be implemented by subclasses.

_URL_PATH_ELEM = 'acl'
:raises: :class:`NotImplementedError` if ``_save_path`` attribute
is not set on the instance.

def __init__(self, bucket):
"""
:type bucket: :class:`gcloud.storage.bucket.Bucket`
:param bucket: The bucket to which this ACL relates.
# Allow override for testing
path = getattr(self, '_save_path', None)
if path is not None:
return path
raise NotImplementedError

def reload(self, connection=None):
"""Reload the ACL data from Cloud Storage.

:type connection: :class:`gcloud.storage.connection.Connection` or None
:param connection: explicit connection to use for API request;
defaults to instance property.
"""
super(BucketACL, self).__init__()
self.bucket = bucket
if connection is None:
connection = self.connection

This comment was marked as spam.


def reload(self):
"""Reload the ACL data from Cloud Storage."""
self.entities.clear()

url_path = '%s/%s' % (self.bucket.path, self._URL_PATH_ELEM)
found = self.bucket.connection.api_request(method='GET', path=url_path)
found = connection.api_request(method='GET', path=self.reload_path)
self.loaded = True
for entry in found.get('items', ()):
self.add_entity(self.entity_from_dict(entry))

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

If called without arguments, this will save the entries
currently stored on this ACL::

>>> acl.save()

You can also provide a specific ACL to save instead of the one
currently set on the Bucket object::

>>> acl.save(acl=my_other_acl)

You can use this to set access controls to be consistent from
one bucket to another::

>>> bucket1 = storage.get_bucket(bucket1_name, connection=connection)
>>> bucket2 = storage.get_bucket(bucket2_name, connection=connection)
>>> bucket2.acl.save(bucket1.acl)

: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 connection: :class:`gcloud.storage.connection.Connection` or None
:param connection: explicit connection to use for API request;
defaults to instance property.
"""
if connection is None:
connection = self.connection

if acl is None:
acl = self
save_to_backend = acl.loaded
else:
save_to_backend = True

if save_to_backend:
result = self.bucket.connection.api_request(
method='PATCH', path=self.bucket.path,
result = connection.api_request(
method='PATCH', path=self.save_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

def clear(self):
def clear(self, connection=None):
"""Remove all ACL entries.

Note that this won't actually remove *ALL* the rules, but it
will remove all the non-default rules. In short, you'll still
have access to a bucket that you created even after you clear
ACL rules with this method.

For example, imagine that you granted access to this bucket to a
bunch of coworkers::

>>> acl.user('coworker1@example.org').grant_read()
>>> acl.user('coworker2@example.org').grant_read()
>>> acl.save()
:type connection: :class:`gcloud.storage.connection.Connection` or None
:param connection: explicit connection to use for API request;
defaults to instance property.
"""
self.save([], connection)

Now they work in another part of the company and you want to
'start fresh' on who has access::

>>> acl.clear()
class BucketACL(ACL):
"""An ACL specifically for a bucket."""

At this point all the custom rules you created have been removed.
def __init__(self, bucket):
"""
:type bucket: :class:`gcloud.storage.bucket.Bucket`
:param bucket: The bucket to which this ACL relates.
"""
self.save([])
super(BucketACL, self).__init__()
self.bucket = bucket

@property
def connection(self):

This comment was marked as spam.

"""Compute the connection for API requests for this ACL."""
return self.bucket.connection

@property
def reload_path(self):
"""Compute the path for GET API requests for this ACL."""
return '%s/%s' % (self.bucket.path, self._URL_PATH_ELEM)

@property
def save_path(self):
"""Compute the path for PATCH API requests for this ACL."""
return self.bucket.path


class DefaultObjectACL(BucketACL):
Expand All @@ -475,44 +499,17 @@ def __init__(self, blob):
super(ObjectACL, self).__init__()
self.blob = blob

def reload(self):
"""Reload the ACL data from Cloud Storage."""
self.entities.clear()

url_path = '%s/acl' % self.blob.path
found = self.blob.connection.api_request(method='GET', path=url_path)
self.loaded = True
for entry in found.get('items', ()):
self.add_entity(self.entity_from_dict(entry))

def save(self, acl=None):
"""Save the ACL data for this blob.
@property
def connection(self):
"""Compute the connection for API requests for this ACL."""
return self.blob.connection

:type acl: :class:`gcloud.storage.acl.ACL`
:param acl: The ACL object to save. If left blank, this will
save the entries set locally on the ACL.
"""
if acl is None:
acl = self
save_to_backend = acl.loaded
else:
save_to_backend = True

if save_to_backend:
result = self.blob.connection.api_request(
method='PATCH', path=self.blob.path, data={'acl': list(acl)},
query_params={'projection': 'full'})
self.entities.clear()
for entry in result.get('acl', ()):
self.add_entity(self.entity_from_dict(entry))
self.loaded = True
@property
def reload_path(self):
"""Compute the path for GET API requests for this ACL."""
return '%s/acl' % self.blob.path

def clear(self):
"""Remove all ACL rules from the blob.

Note that this won't actually remove *ALL* the rules, but it
will remove all the non-default rules. In short, you'll still
have access to a blob that you created even after you clear ACL
rules with this method.
"""
self.save([])
@property
def save_path(self):
"""Compute the path for PATCH API requests for this ACL."""
return self.blob.path
Loading