@@ -172,10 +172,6 @@ class ACL(object):
172172 def __init__ (self ):
173173 self .entities = {}
174174
175- def clear (self ):
176- """Remove all entities from the ACL."""
177- self .entities .clear ()
178-
179175 def reset (self ):
180176 """Remove all entities from the ACL, and clear the ``loaded`` flag."""
181177 self .entities .clear ()
@@ -338,17 +334,35 @@ def get_entities(self):
338334 """
339335 return self .entities .values ()
340336
341- def save (self ):
337+ def reload (self ):
338+ """Reload the ACL data from Cloud Storage.
339+
340+ :rtype: :class:`ACL`
341+ :returns: The current ACL.
342+ """
343+ raise NotImplementedError
344+
345+ def save (self , acl = None ):
342346 """A method to be overridden by subclasses.
343347
348+ :type acl: :class:`gcloud.storage.acl.ACL`, or a compatible list.
349+ :param acl: The ACL object to save. If left blank, this will save
350+ current entries.
351+
344352 :raises: NotImplementedError
345353 """
346354 raise NotImplementedError
347355
356+ def clear (self ):
357+ """Remove all entities from the ACL."""
358+ raise NotImplementedError
359+
348360
349361class BucketACL (ACL ):
350362 """An ACL specifically for a bucket."""
351363
364+ _SUBKEY = 'acl'
365+
352366 def __init__ (self , bucket ):
353367 """
354368 :type bucket: :class:`gcloud.storage.bucket.Bucket`
@@ -357,19 +371,103 @@ def __init__(self, bucket):
357371 super (BucketACL , self ).__init__ ()
358372 self .bucket = bucket
359373
360- def save (self ):
361- """Save this ACL for the current bucket."""
374+ def reload (self ):
375+ """Reload the ACL data from Cloud Storage.
376+
377+ :rtype: :class:`gcloud.storage.acl.BucketACL`
378+ :returns: The current ACL.
379+ """
380+ self .entities .clear ()
381+
382+ url_path = '%s/%s' % (self .bucket .path , self ._SUBKEY )
383+ found = self .bucket .connection .api_request (method = 'GET' , path = url_path )
384+ for entry in found ['items' ]:
385+ self .add_entity (self .entity_from_dict (entry ))
386+
387+ # Even if we fetch no entries, the ACL is still loaded.
388+ self .loaded = True
389+
390+ return self
391+
392+ def save (self , acl = None ):
393+ """Save this ACL for the current bucket.
394+
395+ If called without arguments, this will save the entries
396+ currently stored on this ACL::
362397
363- return self .bucket .save_acl (acl = self )
398+ >>> acl.save()
399+
400+ You can also provide a specific ACL to save instead of the one
401+ currently set on the Bucket object::
402+
403+ >>> acl.save(acl=my_other_acl)
404+
405+ You can use this to set access controls to be consistent from
406+ one bucket to another::
407+
408+ >>> bucket1 = connection.get_bucket(bucket1_name)
409+ >>> bucket2 = connection.get_bucket(bucket2_name)
410+ >>> bucket2.acl.save(bucket1.get_acl())
411+
412+ :type acl: :class:`gcloud.storage.acl.ACL`, or a compatible list.
413+ :param acl: The ACL object to save. If left blank, this will save
414+ current entries.
415+
416+ :rtype: :class:`gcloud.storage.acl.BucketACL`
417+ :returns: The current ACL.
418+ """
419+ # We do things in this weird way because [] and None
420+ # both evaluate to False, but mean very different things.
421+ if acl is None :
422+ acl = self
423+ dirty = acl .loaded
424+ else :
425+ dirty = True
426+
427+ if dirty :
428+ result = self .bucket .connection .api_request (
429+ method = 'PATCH' , path = self .bucket .path ,
430+ data = {self ._SUBKEY : list (acl )},
431+ query_params = {'projection' : 'full' })
432+ self .entities .clear ()
433+ for entry in result [self ._SUBKEY ]:
434+ self .entity (self .entity_from_dict (entry ))
435+ self .loaded = True
436+
437+ return self
438+
439+ def clear (self ):
440+ """Remove all ACL entries.
441+
442+ Note that this won't actually remove *ALL* the rules, but it
443+ will remove all the non-default rules. In short, you'll still
444+ have access to a bucket that you created even after you clear
445+ ACL rules with this method.
446+
447+ For example, imagine that you granted access to this bucket to a
448+ bunch of coworkers::
449+
450+ >>> acl.user('[email protected] ').grant_read() 451+ >>> acl.user('[email protected] ').grant_read() 452+ >>> acl.save()
453+
454+ Now they work in another part of the company and you want to
455+ 'start fresh' on who has access::
456+
457+ >>> acl.clear()
458+
459+ At this point all the custom rules you created have been removed.
460+
461+ :rtype: :class:`gcloud.storage.acl.BucketACL`
462+ :returns: The current ACL.
463+ """
464+ return self .save ([])
364465
365466
366467class DefaultObjectACL (BucketACL ):
367468 """A class representing the default object ACL for a bucket."""
368469
369- def save (self ):
370- """Save this ACL as the default object ACL for the current bucket."""
371-
372- return self .bucket .save_default_object_acl (acl = self )
470+ _SUBKEY = 'defaultObjectAcl'
373471
374472
375473class ObjectACL (ACL ):
@@ -383,7 +481,54 @@ def __init__(self, key):
383481 super (ObjectACL , self ).__init__ ()
384482 self .key = key
385483
386- def save (self ):
387- """Save this ACL for the current key."""
484+ def reload (self ):
485+ """Reload the ACL data from Cloud Storage.
486+
487+ :rtype: :class:`ObjectACL`
488+ :returns: The current ACL.
489+ """
490+ self .entities .clear ()
388491
389- return self .key .save_acl (acl = self )
492+ url_path = '%s/acl' % self .key .path
493+ found = self .key .connection .api_request (method = 'GET' , path = url_path )
494+ for entry in found ['items' ]:
495+ self .add_entity (self .entity_from_dict (entry ))
496+
497+ # Even if we fetch no entries, the ACL is still loaded.
498+ self .loaded = True
499+
500+ return self
501+
502+ def save (self , acl = None ):
503+ """Save the ACL data for this key.
504+
505+ :type acl: :class:`gcloud.storage.acl.ACL`
506+ :param acl: The ACL object to save. If left blank, this will
507+ save the entries set locally on the ACL.
508+ """
509+ if acl is None :
510+ acl = self
511+ dirty = acl .loaded
512+ else :
513+ dirty = True
514+
515+ if dirty :
516+ result = self .key .connection .api_request (
517+ method = 'PATCH' , path = self .key .path , data = {'acl' : list (acl )},
518+ query_params = {'projection' : 'full' })
519+ self .entities .clear ()
520+ for entry in result ['acl' ]:
521+ self .entity (self .entity_from_dict (entry ))
522+ self .loaded = True
523+
524+ return self
525+
526+ def clear (self ):
527+ """Remove all ACL rules from the key.
528+
529+ Note that this won't actually remove *ALL* the rules, but it
530+ will remove all the non-default rules. In short, you'll still
531+ have access to a key that you created even after you clear ACL
532+ rules with this method.
533+ """
534+ return self .save ([])
0 commit comments