Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: gslib/aclhelpers.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gslib/__main__.py ('k') | gslib/addlhelp/__init__.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # -*- coding: utf-8 -*-
1 # Copyright 2013 Google Inc. All Rights Reserved. 2 # Copyright 2013 Google Inc. All Rights Reserved.
2 # 3 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License. 5 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at 6 # You may obtain a copy of the License at
6 # 7 #
7 # http://www.apache.org/licenses/LICENSE-2.0 8 # http://www.apache.org/licenses/LICENSE-2.0
8 # 9 #
9 # Unless required by applicable law or agreed to in writing, software 10 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and 13 # See the License for the specific language governing permissions and
13 # limitations under the License. 14 # limitations under the License.
14 """Contains helper objects for changing and deleting ACLs.""" 15 """Contains helper objects for changing and deleting ACLs."""
15 16
17 from __future__ import absolute_import
18
16 import re 19 import re
17 from xml.dom import minidom
18
19 from boto.gs import acl
20 20
21 from gslib.exception import CommandException 21 from gslib.exception import CommandException
22 22
23 23
24 class ChangeType(object): 24 class ChangeType(object):
25 USER = 'User' 25 USER = 'User'
26 GROUP = 'Group' 26 GROUP = 'Group'
27 27
28 28
29 class AclChange(object): 29 class AclChange(object):
30 """Represents a logical change to an access control list.""" 30 """Represents a logical change to an access control list."""
31 public_scopes = ['AllAuthenticatedUsers', 'AllUsers'] 31 public_scopes = ['AllAuthenticatedUsers', 'AllUsers']
32 id_scopes = ['UserById', 'GroupById'] 32 id_scopes = ['UserById', 'GroupById']
33 email_scopes = ['UserByEmail', 'GroupByEmail'] 33 email_scopes = ['UserByEmail', 'GroupByEmail']
34 domain_scopes = ['GroupByDomain'] 34 domain_scopes = ['GroupByDomain']
35 scope_types = public_scopes + id_scopes + email_scopes + domain_scopes 35 scope_types = public_scopes + id_scopes + email_scopes + domain_scopes
36 36
37 public_entity_all_users = 'allUsers'
38 public_entity_all_auth_users = 'allAuthenticatedUsers'
39 public_entity_types = (public_entity_all_users, public_entity_all_auth_users)
40 project_entity_prefixes = ('project-editors-', 'project-owners-',
41 'project-viewers-')
42 group_entity_prefix = 'group-'
43 user_entity_prefix = 'user-'
44 domain_entity_prefix = 'domain-'
45
37 permission_shorthand_mapping = { 46 permission_shorthand_mapping = {
38 'R': 'READ', 47 'R': 'READER',
39 'W': 'WRITE', 48 'W': 'WRITER',
40 'FC': 'FULL_CONTROL', 49 'FC': 'OWNER',
50 'O': 'OWNER',
51 'READ': 'READER',
52 'WRITE': 'WRITER',
53 'FULL_CONTROL': 'OWNER'
41 } 54 }
42 55
43 def __init__(self, acl_change_descriptor, scope_type): 56 def __init__(self, acl_change_descriptor, scope_type):
44 """Creates an AclChange object. 57 """Creates an AclChange object.
45 58
46 Args: 59 Args:
47 acl_change_descriptor: An acl change as described in the "ch" section of 60 acl_change_descriptor: An acl change as described in the "ch" section of
48 the "acl" command's help. 61 the "acl" command's help.
49 scope_type: Either ChangeType.USER or ChangeType.GROUP, specifying the 62 scope_type: Either ChangeType.USER or ChangeType.GROUP, specifying the
50 extent of the scope. 63 extent of the scope.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 self.perm = self.permission_shorthand_mapping[perm_token] 98 self.perm = self.permission_shorthand_mapping[perm_token]
86 else: 99 else:
87 self.perm = perm_token 100 self.perm = perm_token
88 101
89 scope_class = _ClassifyScopeIdentifier(scope_string) 102 scope_class = _ClassifyScopeIdentifier(scope_string)
90 if scope_class == 'Domain': 103 if scope_class == 'Domain':
91 # This may produce an invalid UserByDomain scope, 104 # This may produce an invalid UserByDomain scope,
92 # which is good because then validate can complain. 105 # which is good because then validate can complain.
93 self.scope_type = '{0}ByDomain'.format(scope_type) 106 self.scope_type = '{0}ByDomain'.format(scope_type)
94 self.identifier = scope_string 107 self.identifier = scope_string
95 elif scope_class in ['Email', 'Id']: 108 elif scope_class in ('Email', 'Id'):
96 self.scope_type = '{0}By{1}'.format(scope_type, scope_class) 109 self.scope_type = '{0}By{1}'.format(scope_type, scope_class)
97 self.identifier = scope_string 110 self.identifier = scope_string
98 elif scope_class == 'AllAuthenticatedUsers': 111 elif scope_class == 'AllAuthenticatedUsers':
99 self.scope_type = 'AllAuthenticatedUsers' 112 self.scope_type = 'AllAuthenticatedUsers'
100 elif scope_class == 'AllUsers': 113 elif scope_class == 'AllUsers':
101 self.scope_type = 'AllUsers' 114 self.scope_type = 'AllUsers'
102 else: 115 else:
103 # This is just a fallback, so we set it to something 116 # This is just a fallback, so we set it to something
104 # and the validate step has something to go on. 117 # and the validate step has something to go on.
105 self.scope_type = scope_string 118 self.scope_type = scope_string
(...skipping 21 matching lines...) Expand all
127 _ThrowError('{0} requires domain'.format(self.scope_type)) 140 _ThrowError('{0} requires domain'.format(self.scope_type))
128 141
129 if self.perm not in self.permission_shorthand_mapping.values(): 142 if self.perm not in self.permission_shorthand_mapping.values():
130 perms = ', '.join(self.permission_shorthand_mapping.values()) 143 perms = ', '.join(self.permission_shorthand_mapping.values())
131 _ThrowError('Allowed permissions are {0}'.format(perms)) 144 _ThrowError('Allowed permissions are {0}'.format(perms))
132 145
133 def _YieldMatchingEntries(self, current_acl): 146 def _YieldMatchingEntries(self, current_acl):
134 """Generator that yields entries that match the change descriptor. 147 """Generator that yields entries that match the change descriptor.
135 148
136 Args: 149 Args:
137 current_acl: An instance of bogo.gs.acl.ACL which will be searched 150 current_acl: A list of apitools_messages.BucketAccessControls or
138 for matching entries. 151 ObjectAccessControls which will be searched for matching
152 entries.
139 153
140 Yields: 154 Yields:
141 An instance of boto.gs.acl.Entry. 155 An apitools_messages.BucketAccessControl or ObjectAccessControl.
142 """ 156 """
143 for entry in current_acl.entries.entry_list: 157 for entry in current_acl:
144 if entry.scope.type == self.scope_type: 158 if (self.scope_type in ('UserById', 'GroupById') and
145 if self.scope_type in ['UserById', 'GroupById']: 159 entry.entityId and self.identifier == entry.entityId):
146 if self.identifier == entry.scope.id: 160 yield entry
147 yield entry 161 elif (self.scope_type in ('UserByEmail', 'GroupByEmail')
148 elif self.scope_type in ['UserByEmail', 'GroupByEmail']: 162 and entry.email and self.identifier == entry.email):
149 if self.identifier == entry.scope.email_address: 163 yield entry
150 yield entry 164 elif (self.scope_type == 'GroupByDomain' and
151 elif self.scope_type == 'GroupByDomain': 165 entry.domain and self.identifier == entry.domain):
152 if self.identifier == entry.scope.domain: 166 yield entry
153 yield entry 167 elif (self.scope_type == 'AllUsers' and
154 elif self.scope_type in ['AllUsers', 'AllAuthenticatedUsers']: 168 entry.entity.lower() == self.public_entity_all_users.lower()):
155 yield entry 169 yield entry
156 else: 170 elif (self.scope_type == 'AllAuthenticatedUsers' and
157 raise CommandException('Found an unrecognized ACL ' 171 entry.entity.lower() == self.public_entity_all_auth_users.lower()):
158 'entry type, aborting.') 172 yield entry
159 173
160 def _AddEntry(self, current_acl): 174 def _AddEntry(self, current_acl, entry_class):
161 """Adds an entry to an ACL.""" 175 """Adds an entry to current_acl."""
162 if self.scope_type in ['UserById', 'UserById', 'GroupById']: 176 if self.scope_type == 'UserById':
163 entry = acl.Entry(type=self.scope_type, permission=self.perm, 177 entry = entry_class(entityId=self.identifier, role=self.perm,
164 id=self.identifier) 178 entity=self.user_entity_prefix + self.identifier)
165 elif self.scope_type in ['UserByEmail', 'GroupByEmail']: 179 elif self.scope_type == 'GroupById':
166 entry = acl.Entry(type=self.scope_type, permission=self.perm, 180 entry = entry_class(entityId=self.identifier, role=self.perm,
167 email_address=self.identifier) 181 entity=self.group_entity_prefix + self.identifier)
182 elif self.scope_type == 'UserByEmail':
183 entry = entry_class(email=self.identifier, role=self.perm,
184 entity=self.user_entity_prefix + self.identifier)
185 elif self.scope_type == 'GroupByEmail':
186 entry = entry_class(email=self.identifier, role=self.perm,
187 entity=self.group_entity_prefix + self.identifier)
168 elif self.scope_type == 'GroupByDomain': 188 elif self.scope_type == 'GroupByDomain':
169 entry = acl.Entry(type=self.scope_type, permission=self.perm, 189 entry = entry_class(domain=self.identifier, role=self.perm,
170 domain=self.identifier) 190 entity=self.domain_entity_prefix + self.identifier)
191 elif self.scope_type == 'AllAuthenticatedUsers':
192 entry = entry_class(entity=self.public_entity_all_auth_users,
193 role=self.perm)
194 elif self.scope_type == 'AllUsers':
195 entry = entry_class(entity=self.public_entity_all_users, role=self.perm)
171 else: 196 else:
172 entry = acl.Entry(type=self.scope_type, permission=self.perm) 197 raise CommandException('Add entry to ACL got unexpected scope type %s.' %
198 self.scope_type)
199 current_acl.append(entry)
173 200
174 current_acl.entries.entry_list.append(entry) 201 def _GetEntriesClass(self, current_acl):
202 # Entries will share the same class, so just return the first one.
203 for acl_entry in current_acl:
204 return acl_entry.__class__
175 205
176 def Execute(self, uri, current_acl, logger): 206 def Execute(self, storage_url, current_acl, command_name, logger):
177 """Executes the described change on an ACL. 207 """Executes the described change on an ACL.
178 208
179 Args: 209 Args:
180 uri: The URI object to change. 210 storage_url: StorageUrl representing the object to change.
181 current_acl: An instance of boto.gs.acl.ACL to permute. 211 current_acl: A list of ObjectAccessControls or
212 BucketAccessControls to permute.
213 command_name: String name of comamnd being run (e.g., 'acl').
182 logger: An instance of logging.Logger. 214 logger: An instance of logging.Logger.
183 215
184 Returns: 216 Returns:
185 The number of changes that were made. 217 The number of changes that were made.
186 """ 218 """
187 logger.debug('Executing {0} on {1}'.format(self.raw_descriptor, uri)) 219 logger.debug(
220 'Executing %s %s on %s', command_name, self.raw_descriptor, storage_url)
188 221
189 if self.perm == 'WRITE' and uri.names_object(): 222 if self.perm == 'WRITER':
190 logger.warning( 223 if command_name == 'acl' and storage_url.IsObject():
191 'Skipping {0} on {1}, as WRITE does not apply to objects' 224 logger.warning(
192 .format(self.raw_descriptor, uri)) 225 'Skipping %s on %s, as WRITER does not apply to objects',
193 return 0 226 self.raw_descriptor, storage_url)
227 return 0
228 elif command_name == 'defacl':
229 raise CommandException('WRITER cannot be set as a default object ACL '
230 'because WRITER does not apply to objects')
194 231
232 entry_class = self._GetEntriesClass(current_acl)
195 matching_entries = list(self._YieldMatchingEntries(current_acl)) 233 matching_entries = list(self._YieldMatchingEntries(current_acl))
196 change_count = 0 234 change_count = 0
197 if matching_entries: 235 if matching_entries:
198 for entry in matching_entries: 236 for entry in matching_entries:
199 if entry.permission != self.perm: 237 if entry.role != self.perm:
200 entry.permission = self.perm 238 entry.role = self.perm
201 change_count += 1 239 change_count += 1
202 else: 240 else:
203 self._AddEntry(current_acl) 241 self._AddEntry(current_acl, entry_class)
204 change_count = 1 242 change_count = 1
205 243
206 parsed_acl = minidom.parseString(current_acl.to_xml()) 244 logger.debug('New Acl:\n%s', str(current_acl))
207 logger.debug('New Acl:\n{0}'.format(parsed_acl.toprettyxml()))
208 return change_count 245 return change_count
209 246
210 247
211 class AclDel(AclChange): 248 class AclDel(object):
212 """Represents a logical change from an access control list.""" 249 """Represents a logical change from an access control list."""
213 scope_regexes = { 250 scope_regexes = {
214 r'All(Users)?$': 'AllUsers', 251 r'All(Users)?$': 'AllUsers',
215 r'AllAuth(enticatedUsers)?$': 'AllAuthenticatedUsers', 252 r'AllAuth(enticatedUsers)?$': 'AllAuthenticatedUsers',
216 } 253 }
217 254
218 def __init__(self, identifier): 255 def __init__(self, identifier):
219 self.raw_descriptor = '-d {0}'.format(identifier) 256 self.raw_descriptor = '-d {0}'.format(identifier)
220 self.identifier = identifier 257 self.identifier = identifier
221 for regex, scope in self.scope_regexes.items(): 258 for regex, scope in self.scope_regexes.items():
222 if re.match(regex, self.identifier, re.IGNORECASE): 259 if re.match(regex, self.identifier, re.IGNORECASE):
223 self.identifier = scope 260 self.identifier = scope
224 self.scope_type = 'Any' 261 self.scope_type = 'Any'
225 self.perm = 'NONE' 262 self.perm = 'NONE'
226 263
227 def _YieldMatchingEntries(self, current_acl): 264 def _YieldMatchingEntries(self, current_acl):
228 for entry in current_acl.entries.entry_list: 265 """Generator that yields entries that match the change descriptor.
229 if self.identifier == entry.scope.id: 266
267 Args:
268 current_acl: An instance of apitools_messages.BucketAccessControls or
269 ObjectAccessControls which will be searched for matching
270 entries.
271
272 Yields:
273 An apitools_messages.BucketAccessControl or ObjectAccessControl.
274 """
275 for entry in current_acl:
276 if entry.entityId and self.identifier == entry.entityId:
230 yield entry 277 yield entry
231 elif self.identifier == entry.scope.email_address: 278 elif entry.email and self.identifier == entry.email:
232 yield entry 279 yield entry
233 elif self.identifier == entry.scope.domain: 280 elif entry.domain and self.identifier == entry.domain:
234 yield entry 281 yield entry
235 elif self.identifier == 'AllUsers' and entry.scope.type == 'AllUsers': 282 elif entry.entity.lower() == 'allusers' and self.identifier == 'AllUsers':
236 yield entry 283 yield entry
237 elif (self.identifier == 'AllAuthenticatedUsers' 284 elif (entry.entity.lower() == 'allauthenticatedusers' and
238 and entry.scope.type == 'AllAuthenticatedUsers'): 285 self.identifier == 'AllAuthenticatedUsers'):
239 yield entry 286 yield entry
240 287
241 def Execute(self, uri, current_acl, logger): 288 def Execute(self, storage_url, current_acl, command_name, logger):
242 logger.debug('Executing {0} on {1}'.format(self.raw_descriptor, uri)) 289 logger.debug(
290 'Executing %s %s on %s', command_name, self.raw_descriptor, storage_url)
243 matching_entries = list(self._YieldMatchingEntries(current_acl)) 291 matching_entries = list(self._YieldMatchingEntries(current_acl))
244 for entry in matching_entries: 292 for entry in matching_entries:
245 current_acl.entries.entry_list.remove(entry) 293 current_acl.remove(entry)
246 parsed_acl = minidom.parseString(current_acl.to_xml()) 294 logger.debug('New Acl:\n%s', str(current_acl))
247 logger.debug('New Acl:\n{0}'.format(parsed_acl.toprettyxml()))
248 return len(matching_entries) 295 return len(matching_entries)
OLDNEW
« no previous file with comments | « gslib/__main__.py ('k') | gslib/addlhelp/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698