Skip to content

Commit 90b4f72

Browse files
committed
Merge pull request #853 from tseaver/825-storage_acl-explicit_connection
#825: Allow passing explicit connection to `ACL.{reload,save}`
2 parents 73a341c + d4bc450 commit 90b4f72

File tree

4 files changed

+278
-338
lines changed

4 files changed

+278
-338
lines changed

gcloud/storage/acl.py

Lines changed: 57 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@
7878
when sending metadata for ACLs to the API.
7979
"""
8080

81+
from gcloud.storage._helpers import _require_connection
82+
8183

8284
class _ACLEntity(object):
8385
"""Class representing a set of roles for an entity.
@@ -167,8 +169,14 @@ def revoke_owner(self):
167169
class ACL(object):
168170
"""Container class representing a list of access controls."""
169171

172+
_URL_PATH_ELEM = 'acl'
170173
loaded = False
171174

175+
# Subclasses must override to provide these attributes (typically,
176+
# as properties).
177+
reload_path = None
178+
save_path = None
179+
172180
def __init__(self):
173181
self.entities = {}
174182

@@ -345,77 +353,33 @@ def get_entities(self):
345353
self._ensure_loaded()
346354
return list(self.entities.values())
347355

348-
def reload(self):
356+
def reload(self, connection=None):
349357
"""Reload the ACL data from Cloud Storage.
350358
351-
This is a virtual method, expected to be implemented by subclasses.
352-
353-
:raises: :class:`NotImplementedError`
354-
"""
355-
raise NotImplementedError
356-
357-
def save(self, acl=None):
358-
"""A method to be overridden by subclasses.
359-
360-
:type acl: :class:`gcloud.storage.acl.ACL`, or a compatible list.
361-
:param acl: The ACL object to save. If left blank, this will save
362-
current entries.
363-
364-
:raises: NotImplementedError
365-
"""
366-
raise NotImplementedError
367-
368-
def clear(self):
369-
"""Remove all entities from the ACL."""
370-
raise NotImplementedError
371-
372-
373-
class BucketACL(ACL):
374-
"""An ACL specifically for a bucket."""
375-
376-
_URL_PATH_ELEM = 'acl'
377-
378-
def __init__(self, bucket):
359+
:type connection: :class:`gcloud.storage.connection.Connection` or None
360+
:param connection: explicit connection to use for API request;
361+
defaults to instance property.
379362
"""
380-
:type bucket: :class:`gcloud.storage.bucket.Bucket`
381-
:param bucket: The bucket to which this ACL relates.
382-
"""
383-
super(BucketACL, self).__init__()
384-
self.bucket = bucket
363+
path = self.reload_path
364+
connection = _require_connection(connection)
385365

386-
def reload(self):
387-
"""Reload the ACL data from Cloud Storage."""
388366
self.entities.clear()
389367

390-
url_path = '%s/%s' % (self.bucket.path, self._URL_PATH_ELEM)
391-
found = self.bucket.connection.api_request(method='GET', path=url_path)
368+
found = connection.api_request(method='GET', path=path)
392369
self.loaded = True
393370
for entry in found.get('items', ()):
394371
self.add_entity(self.entity_from_dict(entry))
395372

396-
def save(self, acl=None):
373+
def save(self, acl=None, connection=None):
397374
"""Save this ACL for the current bucket.
398375
399-
If called without arguments, this will save the entries
400-
currently stored on this ACL::
401-
402-
>>> acl.save()
403-
404-
You can also provide a specific ACL to save instead of the one
405-
currently set on the Bucket object::
406-
407-
>>> acl.save(acl=my_other_acl)
408-
409-
You can use this to set access controls to be consistent from
410-
one bucket to another::
411-
412-
>>> bucket1 = storage.get_bucket(bucket1_name, connection=connection)
413-
>>> bucket2 = storage.get_bucket(bucket2_name, connection=connection)
414-
>>> bucket2.acl.save(bucket1.acl)
415-
416376
:type acl: :class:`gcloud.storage.acl.ACL`, or a compatible list.
417377
:param acl: The ACL object to save. If left blank, this will save
418378
current entries.
379+
380+
:type connection: :class:`gcloud.storage.connection.Connection` or None
381+
:param connection: explicit connection to use for API request;
382+
defaults to instance property.
419383
"""
420384
if acl is None:
421385
acl = self
@@ -424,38 +388,53 @@ def save(self, acl=None):
424388
save_to_backend = True
425389

426390
if save_to_backend:
427-
result = self.bucket.connection.api_request(
428-
method='PATCH', path=self.bucket.path,
391+
path = self.save_path
392+
connection = _require_connection(connection)
393+
result = connection.api_request(
394+
method='PATCH',
395+
path=path,
429396
data={self._URL_PATH_ELEM: list(acl)},
430397
query_params={'projection': 'full'})
431398
self.entities.clear()
432399
for entry in result.get(self._URL_PATH_ELEM, ()):
433400
self.add_entity(self.entity_from_dict(entry))
434401
self.loaded = True
435402

436-
def clear(self):
403+
def clear(self, connection=None):
437404
"""Remove all ACL entries.
438405
439406
Note that this won't actually remove *ALL* the rules, but it
440407
will remove all the non-default rules. In short, you'll still
441408
have access to a bucket that you created even after you clear
442409
ACL rules with this method.
443410
444-
For example, imagine that you granted access to this bucket to a
445-
bunch of coworkers::
446-
447-
>>> acl.user('[email protected]').grant_read()
448-
>>> acl.user('[email protected]').grant_read()
449-
>>> acl.save()
411+
:type connection: :class:`gcloud.storage.connection.Connection` or None
412+
:param connection: explicit connection to use for API request;
413+
defaults to instance property.
414+
"""
415+
self.save([], connection)
450416

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

454-
>>> acl.clear()
418+
class BucketACL(ACL):
419+
"""An ACL specifically for a bucket."""
455420

456-
At this point all the custom rules you created have been removed.
421+
def __init__(self, bucket):
457422
"""
458-
self.save([])
423+
:type bucket: :class:`gcloud.storage.bucket.Bucket`
424+
:param bucket: The bucket to which this ACL relates.
425+
"""
426+
super(BucketACL, self).__init__()
427+
self.bucket = bucket
428+
429+
@property
430+
def reload_path(self):
431+
"""Compute the path for GET API requests for this ACL."""
432+
return '%s/%s' % (self.bucket.path, self._URL_PATH_ELEM)
433+
434+
@property
435+
def save_path(self):
436+
"""Compute the path for PATCH API requests for this ACL."""
437+
return self.bucket.path
459438

460439

461440
class DefaultObjectACL(BucketACL):
@@ -475,44 +454,12 @@ def __init__(self, blob):
475454
super(ObjectACL, self).__init__()
476455
self.blob = blob
477456

478-
def reload(self):
479-
"""Reload the ACL data from Cloud Storage."""
480-
self.entities.clear()
481-
482-
url_path = '%s/acl' % self.blob.path
483-
found = self.blob.connection.api_request(method='GET', path=url_path)
484-
self.loaded = True
485-
for entry in found.get('items', ()):
486-
self.add_entity(self.entity_from_dict(entry))
487-
488-
def save(self, acl=None):
489-
"""Save the ACL data for this blob.
457+
@property
458+
def reload_path(self):
459+
"""Compute the path for GET API requests for this ACL."""
460+
return '%s/acl' % self.blob.path
490461

491-
:type acl: :class:`gcloud.storage.acl.ACL`
492-
:param acl: The ACL object to save. If left blank, this will
493-
save the entries set locally on the ACL.
494-
"""
495-
if acl is None:
496-
acl = self
497-
save_to_backend = acl.loaded
498-
else:
499-
save_to_backend = True
500-
501-
if save_to_backend:
502-
result = self.blob.connection.api_request(
503-
method='PATCH', path=self.blob.path, data={'acl': list(acl)},
504-
query_params={'projection': 'full'})
505-
self.entities.clear()
506-
for entry in result.get('acl', ()):
507-
self.add_entity(self.entity_from_dict(entry))
508-
self.loaded = True
509-
510-
def clear(self):
511-
"""Remove all ACL rules from the blob.
512-
513-
Note that this won't actually remove *ALL* the rules, but it
514-
will remove all the non-default rules. In short, you'll still
515-
have access to a blob that you created even after you clear ACL
516-
rules with this method.
517-
"""
518-
self.save([])
462+
@property
463+
def save_path(self):
464+
"""Compute the path for PATCH API requests for this ACL."""
465+
return self.blob.path

0 commit comments

Comments
 (0)