-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix #324: Move ACL back-end manipulations from bucket / key to acl objects #326
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
Merged
tseaver
merged 7 commits into
googleapis:master
from
tseaver:324-move_acl_manip_from_bucket_key_to_acl
Nov 3, 2014
Merged
Changes from 4 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
18c1b25
Use ACL-specific endpoints where feasible for buckets.
tseaver 65d9962
Use ACL-specific endpoints where feasible for keys.
tseaver 417333c
Fix save/clear ACL operations.
tseaver 9cfe627
Move APIs for reload/save/clear ACLs onto ACL objects themselves.
tseaver d7a1beb
Rename 'dirty'->'save_to_backend'.
tseaver 5689c02
Use 'add_entity' directly when populating from scratch.
tseaver bb9a3a8
Improve class attr name: '_SUBKEY'->'_URL_PATH_ELEM'.
tseaver File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -172,10 +172,6 @@ class ACL(object): | |
| def __init__(self): | ||
| self.entities = {} | ||
|
|
||
| def clear(self): | ||
| """Remove all entities from the ACL.""" | ||
| self.entities.clear() | ||
|
|
||
| def reset(self): | ||
| """Remove all entities from the ACL, and clear the ``loaded`` flag.""" | ||
| self.entities.clear() | ||
|
|
@@ -338,17 +334,35 @@ def get_entities(self): | |
| """ | ||
| return self.entities.values() | ||
|
|
||
| def save(self): | ||
| def reload(self): | ||
| """Reload the ACL data from Cloud Storage. | ||
|
|
||
| :rtype: :class:`ACL` | ||
| :returns: The current ACL. | ||
| """ | ||
| raise NotImplementedError | ||
|
|
||
| def save(self, acl=None): | ||
| """A method to be overridden by subclasses. | ||
|
|
||
| :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. | ||
|
|
||
| :raises: NotImplementedError | ||
| """ | ||
| raise NotImplementedError | ||
|
|
||
| def clear(self): | ||
| """Remove all entities from the ACL.""" | ||
| raise NotImplementedError | ||
|
|
||
|
|
||
| class BucketACL(ACL): | ||
| """An ACL specifically for a bucket.""" | ||
|
|
||
| _SUBKEY = 'acl' | ||
|
|
||
| def __init__(self, bucket): | ||
| """ | ||
| :type bucket: :class:`gcloud.storage.bucket.Bucket` | ||
|
|
@@ -357,19 +371,103 @@ def __init__(self, bucket): | |
| super(BucketACL, self).__init__() | ||
| self.bucket = bucket | ||
|
|
||
| def save(self): | ||
| """Save this ACL for the current bucket.""" | ||
| def reload(self): | ||
| """Reload the ACL data from Cloud Storage. | ||
|
|
||
| :rtype: :class:`gcloud.storage.acl.BucketACL` | ||
| :returns: The current ACL. | ||
| """ | ||
| self.entities.clear() | ||
|
|
||
| url_path = '%s/%s' % (self.bucket.path, self._SUBKEY) | ||
| found = self.bucket.connection.api_request(method='GET', path=url_path) | ||
| for entry in found['items']: | ||
| self.add_entity(self.entity_from_dict(entry)) | ||
|
|
||
| # Even if we fetch no entries, the ACL is still loaded. | ||
| self.loaded = True | ||
|
|
||
| return self | ||
|
|
||
| def save(self, acl=None): | ||
| """Save this ACL for the current bucket. | ||
|
|
||
| If called without arguments, this will save the entries | ||
| currently stored on this ACL:: | ||
|
|
||
| return self.bucket.save_acl(acl=self) | ||
| >>> 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 = connection.get_bucket(bucket1_name) | ||
| >>> bucket2 = connection.get_bucket(bucket2_name) | ||
| >>> bucket2.acl.save(bucket1.get_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. | ||
|
|
||
| :rtype: :class:`gcloud.storage.acl.BucketACL` | ||
| :returns: The current ACL. | ||
| """ | ||
| # We do things in this weird way because [] and None | ||
This comment was marked as spam.
Sorry, something went wrong. |
||
| # both evaluate to False, but mean very different things. | ||
| if acl is None: | ||
| acl = self | ||
| dirty = acl.loaded | ||
This comment was marked as spam.
Sorry, something went wrong. |
||
| else: | ||
| dirty = True | ||
|
|
||
| if dirty: | ||
| result = self.bucket.connection.api_request( | ||
| method='PATCH', path=self.bucket.path, | ||
| data={self._SUBKEY: list(acl)}, | ||
| query_params={'projection': 'full'}) | ||
| self.entities.clear() | ||
| for entry in result[self._SUBKEY]: | ||
| self.entity(self.entity_from_dict(entry)) | ||
This comment was marked as spam.
Sorry, something went wrong. |
||
| self.loaded = True | ||
|
|
||
| return self | ||
|
|
||
| def clear(self): | ||
| """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('[email protected]').grant_read() | ||
| >>> acl.user('[email protected]').grant_read() | ||
| >>> acl.save() | ||
|
|
||
| Now they work in another part of the company and you want to | ||
| 'start fresh' on who has access:: | ||
|
|
||
| >>> acl.clear() | ||
|
|
||
| At this point all the custom rules you created have been removed. | ||
|
|
||
| :rtype: :class:`gcloud.storage.acl.BucketACL` | ||
| :returns: The current ACL. | ||
| """ | ||
| return self.save([]) | ||
|
|
||
|
|
||
| class DefaultObjectACL(BucketACL): | ||
| """A class representing the default object ACL for a bucket.""" | ||
|
|
||
| def save(self): | ||
| """Save this ACL as the default object ACL for the current bucket.""" | ||
|
|
||
| return self.bucket.save_default_object_acl(acl=self) | ||
| _SUBKEY = 'defaultObjectAcl' | ||
|
|
||
|
|
||
| class ObjectACL(ACL): | ||
|
|
@@ -383,7 +481,54 @@ def __init__(self, key): | |
| super(ObjectACL, self).__init__() | ||
| self.key = key | ||
|
|
||
| def save(self): | ||
| """Save this ACL for the current key.""" | ||
| def reload(self): | ||
| """Reload the ACL data from Cloud Storage. | ||
|
|
||
| :rtype: :class:`ObjectACL` | ||
| :returns: The current ACL. | ||
| """ | ||
| self.entities.clear() | ||
|
|
||
| return self.key.save_acl(acl=self) | ||
| url_path = '%s/acl' % self.key.path | ||
| found = self.key.connection.api_request(method='GET', path=url_path) | ||
| for entry in found['items']: | ||
| self.add_entity(self.entity_from_dict(entry)) | ||
|
|
||
| # Even if we fetch no entries, the ACL is still loaded. | ||
| self.loaded = True | ||
|
|
||
| return self | ||
|
|
||
| def save(self, acl=None): | ||
| """Save the ACL data for this key. | ||
|
|
||
| :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 | ||
| dirty = acl.loaded | ||
| else: | ||
| dirty = True | ||
|
|
||
| if dirty: | ||
| result = self.key.connection.api_request( | ||
| method='PATCH', path=self.key.path, data={'acl': list(acl)}, | ||
| query_params={'projection': 'full'}) | ||
| self.entities.clear() | ||
| for entry in result['acl']: | ||
| self.entity(self.entity_from_dict(entry)) | ||
| self.loaded = True | ||
|
|
||
| return self | ||
|
|
||
| def clear(self): | ||
| """Remove all ACL rules from the key. | ||
|
|
||
| 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 key that you created even after you clear ACL | ||
| rules with this method. | ||
| """ | ||
| return self.save([]) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.