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

Side by Side Diff: gslib/commands/acl.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/commands/__init__.py ('k') | gslib/commands/cat.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 2011 Google Inc. All Rights Reserved. 2 # Copyright 2011 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.
15 """Implementation of acl command for cloud storage providers."""
16
17 from __future__ import absolute_import
14 18
15 import getopt 19 import getopt
16 20
17 from boto.exception import GSResponseError
18 from gslib import aclhelpers 21 from gslib import aclhelpers
19 from gslib import name_expansion 22 from gslib.cloud_api import AccessDeniedException
23 from gslib.cloud_api import BadRequestException
24 from gslib.cloud_api import Preconditions
25 from gslib.cloud_api import ServiceException
20 from gslib.command import Command 26 from gslib.command import Command
21 from gslib.command import COMMAND_NAME 27 from gslib.command import SetAclExceptionHandler
22 from gslib.command import COMMAND_NAME_ALIASES 28 from gslib.command import SetAclFuncWrapper
23 from gslib.command import FILE_URIS_OK 29 from gslib.cs_api_map import ApiSelector
24 from gslib.command import MAX_ARGS
25 from gslib.command import MIN_ARGS
26 from gslib.command import PROVIDER_URIS_OK
27 from gslib.command import SUPPORTED_SUB_ARGS
28 from gslib.command import URIS_START_ARG
29 from gslib.exception import CommandException 30 from gslib.exception import CommandException
30 from gslib.help_provider import CreateHelpText 31 from gslib.help_provider import CreateHelpText
31 from gslib.help_provider import HELP_NAME 32 from gslib.storage_url import StorageUrlFromString
32 from gslib.help_provider import HELP_NAME_ALIASES 33 from gslib.third_party.storage_apitools import storage_v1_messages as apitools_m essages
33 from gslib.help_provider import HELP_ONE_LINE_SUMMARY
34 from gslib.help_provider import HELP_TEXT
35 from gslib.help_provider import HELP_TYPE
36 from gslib.help_provider import HelpType
37 from gslib.help_provider import SUBCOMMAND_HELP_TEXT
38 from gslib.util import NO_MAX 34 from gslib.util import NO_MAX
39 from gslib.util import Retry 35 from gslib.util import Retry
36 from gslib.util import UrlsAreForSingleProvider
40 37
41 _SET_SYNOPSIS = """ 38 _SET_SYNOPSIS = """
42 gsutil acl set [-f] [-R] [-a] file-or-canned_acl_name uri... 39 gsutil acl set [-f] [-R] [-a] file-or-canned_acl_name url...
43 """ 40 """
44 41
45 _GET_SYNOPSIS = """ 42 _GET_SYNOPSIS = """
46 gsutil acl get uri 43 gsutil acl get url
47 """ 44 """
48 45
49 _CH_SYNOPSIS = """ 46 _CH_SYNOPSIS = """
50 gsutil acl ch [-R] -u|-g|-d <grant>... uri... 47 gsutil acl ch [-R] -u|-g|-d <grant>... url...
51 48
52 where each <grant> is one of the following forms: 49 where each <grant> is one of the following forms:
53 50
54 -u <id|email>:<perm> 51 -u <id|email>:<perm>
55 -g <id|email|domain|All|AllAuth>:<perm> 52 -g <id|email|domain|All|AllAuth>:<perm>
56 -d <id|email|domain|All|AllAuth> 53 -d <id|email|domain|All|AllAuth>
57 """ 54 """
58 55
59 _GET_DESCRIPTION = """ 56 _GET_DESCRIPTION = """
60 <B>GET</B> 57 <B>GET</B>
61 The "acl get" command gets the ACL XML for a bucket or object, which you can 58 The "acl get" command gets the ACL text for a bucket or object, which you can
62 save and edit for the acl set command. 59 save and edit for the acl set command.
63 """ 60 """
64 61
65 _SET_DESCRIPTION = """ 62 _SET_DESCRIPTION = """
66 <B>SET</B> 63 <B>SET</B>
67 The "acl set" command allows you to set an Access Control List on one or 64 The "acl set" command allows you to set an Access Control List on one or
68 more buckets and objects. The simplest way to use it is to specify one of 65 more buckets and objects. The simplest way to use it is to specify one of
69 the canned ACLs, e.g.,: 66 the canned ACLs, e.g.,:
70 67
71 gsutil acl set private gs://bucket 68 gsutil acl set private gs://bucket
(...skipping 25 matching lines...) Expand all
97 for example: 94 for example:
98 95
99 gsutil acl set acl.txt gs://bucket/*.jpg 96 gsutil acl set acl.txt gs://bucket/*.jpg
100 97
101 If you have a large number of ACLs to update you might want to use the 98 If you have a large number of ACLs to update you might want to use the
102 gsutil -m option, to perform a parallel (multi-threaded/multi-processing) 99 gsutil -m option, to perform a parallel (multi-threaded/multi-processing)
103 update: 100 update:
104 101
105 gsutil -m acl set acl.txt gs://bucket/*.jpg 102 gsutil -m acl set acl.txt gs://bucket/*.jpg
106 103
107 Note that multi-threading/multi-processing is only done when the named URIs 104 Note that multi-threading/multi-processing is only done when the named URLs
108 refer to objects. gsutil -m acl set gs://bucket1 gs://bucket2 will run the 105 refer to objects. gsutil -m acl set gs://bucket1 gs://bucket2 will run the
109 acl set operations sequentially. 106 acl set operations sequentially.
110 107
111 108
112 <B>SET OPTIONS</B> 109 <B>SET OPTIONS</B>
113 The "set" sub-command has the following options 110 The "set" sub-command has the following options
114 111
115 -R, -r Performs "acl set" request recursively, to all objects under 112 -R, -r Performs "acl set" request recursively, to all objects under
116 the specified URI. 113 the specified URL.
117 114
118 -a Performs "acl set" request on all object versions. 115 -a Performs "acl set" request on all object versions.
119 116
120 -f Normally gsutil stops at the first error. The -f option causes 117 -f Normally gsutil stops at the first error. The -f option causes
121 it to continue when it encounters errors. With this option the 118 it to continue when it encounters errors. If some of the ACLs
122 gsutil exit status will be 0 even if some ACLs couldn't be 119 couldn't be set, gsutil's exit status will be non-zero even if
123 set. 120 this flag is set. This option is implicitly set when running
121 "gsutil -m acl...".
124 """ 122 """
125 123
126 _CH_DESCRIPTION = """ 124 _CH_DESCRIPTION = """
127 <B>CH</B> 125 <B>CH</B>
128 The "acl ch" (or "acl change") command updates access control lists, similar 126 The "acl ch" (or "acl change") command updates access control lists, similar
129 in spirit to the Linux chmod command. You can specify multiple access grant 127 in spirit to the Linux chmod command. You can specify multiple access grant
130 additions and deletions in a single command run; all changes will be made 128 additions and deletions in a single command run; all changes will be made
131 atomically to each object in turn. For example, if the command requests 129 atomically to each object in turn. For example, if the command requests
132 deleting one grant and adding a different grant, the ACLs being updated will 130 deleting one grant and adding a different grant, the ACLs being updated will
133 never be left in an intermediate state where one grant has been deleted but 131 never be left in an intermediate state where one grant has been deleted but
134 the second grant not yet added. Each change specifies a user or group grant 132 the second grant not yet added. Each change specifies a user or group grant
135 to add or delete, and for grant additions, one of R, W, FC (for the 133 to add or delete, and for grant additions, one of R, W, O (for the
136 permission to be granted). A more formal description is provided in a later 134 permission to be granted). A more formal description is provided in a later
137 section; below we provide examples. 135 section; below we provide examples.
138 136
139 <B>CH EXAMPLES</B> 137 <B>CH EXAMPLES</B>
140 Examples for "ch" sub-command: 138 Examples for "ch" sub-command:
141 139
142 Grant the user john.doe@example.com WRITE access to the bucket 140 Grant the user john.doe@example.com WRITE access to the bucket
143 example-bucket: 141 example-bucket:
144 142
145 gsutil acl ch -u john.doe@example.com:WRITE gs://example-bucket 143 gsutil acl ch -u john.doe@example.com:WRITE gs://example-bucket
146 144
147 Grant the group admins@example.com FULL_CONTROL access to all jpg files in 145 Grant the group admins@example.com OWNER access to all jpg files in
148 the top level of example-bucket: 146 the top level of example-bucket:
149 147
150 gsutil acl ch -g admins@example.com:FC gs://example-bucket/*.jpg 148 gsutil acl ch -g admins@example.com:O gs://example-bucket/*.jpg
151 149
152 Grant the user with the specified canonical ID READ access to all objects 150 Grant the user with the specified canonical ID READ access to all objects
153 in example-bucket that begin with folder/: 151 in example-bucket that begin with folder/:
154 152
155 gsutil acl ch -R \\ 153 gsutil acl ch -R \\
156 -u 84fac329bceSAMPLE777d5d22b8SAMPLE785ac2SAMPLE2dfcf7c4adf34da46:R \\ 154 -u 84fac329bceSAMPLE777d5d22b8SAMPLE785ac2SAMPLE2dfcf7c4adf34da46:R \\
157 gs://example-bucket/folder/ 155 gs://example-bucket/folder/
158 156
157 Grant the service account foo@developer.gserviceaccount.com WRITE access to
158 the bucket example-bucket:
159
160 gsutil acl ch -u foo@developer.gserviceaccount.com:W gs://example-bucket
161
159 Grant all users from my-domain.org READ access to the bucket 162 Grant all users from my-domain.org READ access to the bucket
160 gcs.my-domain.org: 163 gcs.my-domain.org:
161 164
162 gsutil acl ch -g my-domain.org:R gs://gcs.my-domain.org 165 gsutil acl ch -g my-domain.org:R gs://gcs.my-domain.org
163 166
164 Remove any current access by john.doe@example.com from the bucket 167 Remove any current access by john.doe@example.com from the bucket
165 example-bucket: 168 example-bucket:
166 169
167 gsutil acl ch -d john.doe@example.com gs://example-bucket 170 gsutil acl ch -d john.doe@example.com gs://example-bucket
168 171
169 If you have a large number of objects to update, enabling multi-threading 172 If you have a large number of objects to update, enabling multi-threading
170 with the gsutil -m flag can significantly improve performance. The 173 with the gsutil -m flag can significantly improve performance. The
171 following command adds FULL_CONTROL for admin@example.org using 174 following command adds OWNER for admin@example.org using
172 multi-threading: 175 multi-threading:
173 176
174 gsutil -m acl ch -R -u admin@example.org:FC gs://example-bucket 177 gsutil -m acl ch -R -u admin@example.org:O gs://example-bucket
175 178
176 Grant READ access to everyone from my-domain.org and to all authenticated 179 Grant READ access to everyone from my-domain.org and to all authenticated
177 users, and grant FULL_CONTROL to admin@mydomain.org, for the buckets 180 users, and grant OWNER to admin@mydomain.org, for the buckets
178 my-bucket and my-other-bucket, with multi-threading enabled: 181 my-bucket and my-other-bucket, with multi-threading enabled:
179 182
180 gsutil -m acl ch -R -g my-domain.org:R -g AllAuth:R \\ 183 gsutil -m acl ch -R -g my-domain.org:R -g AllAuth:R \\
181 -u admin@mydomain.org:FC gs://my-bucket/ gs://my-other-bucket 184 -u admin@mydomain.org:O gs://my-bucket/ gs://my-other-bucket
182 185
183 <B>CH PERMISSIONS</B> 186 <B>CH ROLES</B>
184 You may specify the following permissions with either their shorthand or 187 You may specify the following roles with either their shorthand or
185 their full name: 188 their full name:
186 189
187 R: READ 190 R: READ
188 W: WRITE 191 W: WRITE
189 FC: FULL_CONTROL 192 O: OWNER
190 193
191 <B>CH SCOPES</B> 194 <B>CH ENTITIES</B>
192 There are four different scopes: Users, Groups, All Authenticated Users, 195 There are four different entity types: Users, Groups, All Authenticated Users,
193 and All Users. 196 and All Users.
194 197
195 Users are added with -u and a plain ID or email address, as in 198 Users are added with -u and a plain ID or email address, as in
196 "-u john-doe@gmail.com:r" 199 "-u john-doe@gmail.com:r". Note: Service Accounts are considered to be users.
197 200
198 Groups are like users, but specified with the -g flag, as in 201 Groups are like users, but specified with the -g flag, as in
199 "-g power-users@example.com:fc". Groups may also be specified as a full 202 "-g power-users@example.com:fc". Groups may also be specified as a full
200 domain, as in "-g my-company.com:r". 203 domain, as in "-g my-company.com:r".
201 204
202 AllAuthenticatedUsers and AllUsers are specified directly, as 205 AllAuthenticatedUsers and AllUsers are specified directly, as
203 in "-g AllUsers:R" or "-g AllAuthenticatedUsers:FC". These are case 206 in "-g AllUsers:R" or "-g AllAuthenticatedUsers:O". These are case
204 insensitive, and may be shortened to "all" and "allauth", respectively. 207 insensitive, and may be shortened to "all" and "allauth", respectively.
205 208
206 Removing permissions is specified with the -d flag and an ID, email 209 Removing roles is specified with the -d flag and an ID, email
207 address, domain, or one of AllUsers or AllAuthenticatedUsers. 210 address, domain, or one of AllUsers or AllAuthenticatedUsers.
208 211
209 Many scopes can be specified on the same command line, allowing bundled 212 Many entities' roles can be specified on the same command line, allowing
210 changes to be executed in a single run. This will reduce the number of 213 bundled changes to be executed in a single run. This will reduce the number of
211 requests made to the server. 214 requests made to the server.
212 215
213 <B>CH OPTIONS</B> 216 <B>CH OPTIONS</B>
214 The "ch" sub-command has the following options 217 The "ch" sub-command has the following options
215 218
216 -R, -r Performs acl ch request recursively, to all objects under the 219 -R, -r Performs acl ch request recursively, to all objects under the
217 specified URI. 220 specified URL.
218 221
219 -u Add or modify a user permission as specified in the SCOPES 222 -u Add or modify a user entity's role.
220 and PERMISSIONS sections.
221 223
222 -g Add or modify a group permission as specified in the SCOPES 224 -g Add or modify a group entity's role.
223 and PERMISSIONS sections.
224 225
225 -d Remove all permissions associated with the matching argument, 226 -d Remove all roles associated with the matching entity.
226 as specified in the SCOPES and PERMISSIONS sections 227
228 -f Normally gsutil stops at the first error. The -f option causes
229 it to continue when it encounters errors. With this option the
230 gsutil exit status will be 0 even if some ACLs couldn't be
231 changed.
227 """ 232 """
228 233
229 _SYNOPSIS = (_SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') + 234 _SYNOPSIS = (_SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') +
230 _CH_SYNOPSIS.lstrip('\n') + '\n\n') 235 _CH_SYNOPSIS.lstrip('\n') + '\n\n')
231 236
232 _DESCRIPTION = (""" 237 _DESCRIPTION = ("""
233 The acl command has three sub-commands: 238 The acl command has three sub-commands:
234 """ + '\n'.join([_GET_DESCRIPTION, _SET_DESCRIPTION, _CH_DESCRIPTION])) 239 """ + '\n'.join([_GET_DESCRIPTION, _SET_DESCRIPTION, _CH_DESCRIPTION]))
235 240
236 _detailed_help_text = CreateHelpText(_SYNOPSIS, _DESCRIPTION) 241 _DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION)
237 242
238 _get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION) 243 _get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION)
239 _set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION) 244 _set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION)
240 _ch_help_text = CreateHelpText(_CH_SYNOPSIS, _CH_DESCRIPTION) 245 _ch_help_text = CreateHelpText(_CH_SYNOPSIS, _CH_DESCRIPTION)
241 246
247
242 def _ApplyExceptionHandler(cls, exception): 248 def _ApplyExceptionHandler(cls, exception):
243 cls.logger.error('Encountered a problem: {0}'.format(exception)) 249 cls.logger.error('Encountered a problem: %s', exception)
244 cls.everything_set_okay = False 250 cls.everything_set_okay = False
245 251
246 def _ApplyAclChangesWrapper(cls, uri_or_expansion_result): 252
247 cls.ApplyAclChanges(uri_or_expansion_result) 253 def _ApplyAclChangesWrapper(cls, url_or_expansion_result, thread_state=None):
254 cls.ApplyAclChanges(url_or_expansion_result, thread_state=thread_state)
248 255
249 256
250 class AclCommand(Command): 257 class AclCommand(Command):
251 """Implementation of gsutil acl command.""" 258 """Implementation of gsutil acl command."""
252 259
253 # Command specification (processed by parent class). 260 # Command specification. See base class for documentation.
254 command_spec = { 261 command_spec = Command.CreateCommandSpec(
255 # Name of command. 262 'acl',
256 COMMAND_NAME : 'acl', 263 command_name_aliases=['getacl', 'setacl', 'chacl'],
257 # List of command name aliases. 264 min_args=2,
258 COMMAND_NAME_ALIASES : ['getacl', 'setacl', 'chacl'], 265 max_args=NO_MAX,
259 # Min number of args required by this command. 266 supported_sub_args='afRrg:u:d:',
260 MIN_ARGS : 2, 267 file_url_ok=False,
261 # Max number of args required by this command, or NO_MAX. 268 provider_url_ok=False,
262 MAX_ARGS : NO_MAX, 269 urls_start_arg=1,
263 # Getopt-style string specifying acceptable sub args. 270 gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
264 SUPPORTED_SUB_ARGS : 'afRrvg:u:d:', 271 gs_default_api=ApiSelector.JSON,
265 # True if file URIs acceptable for this command. 272 )
266 FILE_URIS_OK : False, 273 # Help specification. See help_provider.py for documentation.
267 # True if provider-only URIs acceptable for this command. 274 help_spec = Command.HelpSpec(
268 PROVIDER_URIS_OK : False, 275 help_name='acl',
269 # Index in args of first URI arg. 276 help_name_aliases=['getacl', 'setacl', 'chmod', 'chacl'],
270 URIS_START_ARG : 1, 277 help_type='command_help',
271 } 278 help_one_line_summary='Get, set, or change bucket and/or object ACLs',
272 help_spec = { 279 help_text=_DETAILED_HELP_TEXT,
273 # Name of command or auxiliary help info for which this help applies. 280 subcommand_help_text={
274 HELP_NAME : 'acl', 281 'get': _get_help_text, 'set': _set_help_text, 'ch': _ch_help_text},
275 # List of help name aliases. 282 )
276 HELP_NAME_ALIASES : ['getacl', 'setacl', 'chmod', 'chacl'],
277 # Type of help:
278 HELP_TYPE : HelpType.COMMAND_HELP,
279 # One line summary of this help.
280 HELP_ONE_LINE_SUMMARY : 'Get, set, or change bucket and/or object ACLs',
281 # The full help text.
282 HELP_TEXT : _detailed_help_text,
283 # Help text for sub-commands.
284 SUBCOMMAND_HELP_TEXT : {'get' : _get_help_text,
285 'set' : _set_help_text,
286 'ch' : _ch_help_text},
287 }
288 283
289 def _CalculateUrisStartArg(self): 284 def _CalculateUrlsStartArg(self):
290 if not self.args: 285 if not self.args:
291 self._RaiseWrongNumberOfArgumentsException() 286 self._RaiseWrongNumberOfArgumentsException()
292 if (self.args[0].lower() == 'set') or (self.command_alias_used == 'setacl'): 287 if (self.args[0].lower() == 'set') or (self.command_alias_used == 'setacl'):
293 return 1 288 return 1
294 else: 289 else:
295 return 0 290 return 0
296 291
297 def _SetAcl(self): 292 def _SetAcl(self):
293 """Parses options and sets ACLs on the specified buckets/objects."""
298 self.continue_on_error = False 294 self.continue_on_error = False
299 if self.sub_opts: 295 if self.sub_opts:
300 for o, unused_a in self.sub_opts: 296 for o, unused_a in self.sub_opts:
301 if o == '-a': 297 if o == '-a':
302 self.all_versions = True 298 self.all_versions = True
303 elif o == '-f': 299 elif o == '-f':
304 self.continue_on_error = True 300 self.continue_on_error = True
305 elif o == '-r' or o == '-R': 301 elif o == '-r' or o == '-R':
306 self.recursion_requested = True 302 self.recursion_requested = True
307 elif o == '-v':
308 self.logger.warning('WARNING: The %s -v option is no longer'
309 ' needed, and will eventually be '
310 'removed.\n' % self.command_name)
311 try: 303 try:
312 self.SetAclCommandHelper() 304 self.SetAclCommandHelper(SetAclFuncWrapper, SetAclExceptionHandler)
313 except GSResponseError as e: 305 except AccessDeniedException, unused_e:
314 if e.code == 'AccessDenied' and e.reason == 'Forbidden' \ 306 self._WarnServiceAccounts()
315 and e.status == 403:
316 self._WarnServiceAccounts()
317 raise 307 raise
308 if not self.everything_set_okay:
309 raise CommandException('ACLs for some objects could not be set.')
318 310
319 def _GetAcl(self):
320 try:
321 self.GetAclCommandHelper()
322 except GSResponseError as e:
323 if e.code == 'AccessDenied' and e.reason == 'Forbidden' \
324 and e.status == 403:
325 self._WarnServiceAccounts()
326 raise
327
328 def _ChAcl(self): 311 def _ChAcl(self):
312 """Parses options and changes ACLs on the specified buckets/objects."""
313 self.parse_versions = True
329 self.changes = [] 314 self.changes = []
315 self.continue_on_error = False
330 316
331 if self.sub_opts: 317 if self.sub_opts:
332 for o, a in self.sub_opts: 318 for o, a in self.sub_opts:
319 if o == '-f':
320 self.continue_on_error = True
333 if o == '-g': 321 if o == '-g':
334 self.changes.append( 322 self.changes.append(
335 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.GROUP)) 323 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.GROUP))
336 if o == '-u': 324 if o == '-u':
337 self.changes.append( 325 self.changes.append(
338 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.USER)) 326 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.USER))
339 if o == '-d': 327 if o == '-d':
340 self.changes.append(aclhelpers.AclDel(a)) 328 self.changes.append(aclhelpers.AclDel(a))
341 if o == '-r' or o == '-R': 329 if o == '-r' or o == '-R':
342 self.recursion_requested = True 330 self.recursion_requested = True
343 331
344 if not self.changes: 332 if not self.changes:
345 raise CommandException( 333 raise CommandException(
346 'Please specify at least one access change ' 334 'Please specify at least one access change '
347 'with the -g, -u, or -d flags') 335 'with the -g, -u, or -d flags')
348 336
349 storage_uri = self.UrisAreForSingleProvider(self.args) 337 if (not UrlsAreForSingleProvider(self.args) or
350 if not (storage_uri and storage_uri.get_provider().name == 'google'): 338 StorageUrlFromString(self.args[0]).scheme != 'gs'):
351 raise CommandException( 339 raise CommandException(
352 'The "{0}" command can only be used with gs:// URIs'.format( 340 'The "{0}" command can only be used with gs:// URLs'.format(
353 self.command_name)) 341 self.command_name))
354 342
355 bulk_uris = set()
356 for uri_arg in self.args:
357 for result in self.WildcardIterator(uri_arg):
358 uri = result.uri
359 if uri.names_bucket():
360 if self.recursion_requested:
361 bulk_uris.add(uri.clone_replace_name('*').uri)
362 else:
363 # If applying to a bucket directly, the threading machinery will
364 # break, so we have to apply now, in the main thread.
365 self.ApplyAclChanges(uri)
366 else:
367 bulk_uris.add(uri_arg)
368
369 try:
370 name_expansion_iterator = name_expansion.NameExpansionIterator(
371 self.command_name, self.proj_id_handler, self.headers, self.debug,
372 self.logger, self.bucket_storage_uri_class, bulk_uris,
373 self.recursion_requested)
374 except CommandException as e:
375 # NameExpansionIterator will complain if there are no URIs, but we don't
376 # want to throw an error if we handled bucket URIs.
377 if e.reason == 'No URIs matched':
378 return 0
379 else:
380 raise e
381
382 self.everything_set_okay = True 343 self.everything_set_okay = True
383 self.Apply(_ApplyAclChangesWrapper, 344 self.ApplyAclFunc(_ApplyAclChangesWrapper, _ApplyExceptionHandler,
384 name_expansion_iterator, 345 self.args)
385 _ApplyExceptionHandler)
386 if not self.everything_set_okay: 346 if not self.everything_set_okay:
387 raise CommandException('ACLs for some objects could not be set.') 347 raise CommandException('ACLs for some objects could not be set.')
388 348
389 @Retry(GSResponseError, tries=3, timeout_secs=1) 349 @Retry(ServiceException, tries=3, timeout_secs=1)
390 def ApplyAclChanges(self, uri_or_expansion_result): 350 def ApplyAclChanges(self, name_expansion_result, thread_state=None):
391 """Applies the changes in self.changes to the provided URI.""" 351 """Applies the changes in self.changes to the provided URL.
392 if isinstance(uri_or_expansion_result, name_expansion.NameExpansionResult): 352
393 uri = self.suri_builder.StorageUri( 353 Args:
394 uri_or_expansion_result.expanded_uri_str) 354 name_expansion_result: NameExpansionResult describing the target object.
355 thread_state: If present, gsutil Cloud API instance to apply the changes.
356 """
357 if thread_state:
358 gsutil_api = thread_state
395 else: 359 else:
396 uri = uri_or_expansion_result 360 gsutil_api = self.gsutil_api
397 361
398 try: 362 url = name_expansion_result.expanded_storage_url
399 current_acl = uri.get_acl() 363
400 except GSResponseError as e: 364 if url.IsBucket():
401 if (e.code == 'AccessDenied' and e.reason == 'Forbidden' 365 bucket = gsutil_api.GetBucket(url.bucket_name, provider=url.scheme,
402 and e.status == 403): 366 fields=['acl', 'metageneration'])
403 self._WarnServiceAccounts() 367 current_acl = bucket.acl
404 self.logger.warning('Failed to set acl for {0}: {1}' 368 elif url.IsObject():
405 .format(uri, e.reason)) 369 gcs_object = gsutil_api.GetObjectMetadata(
370 url.bucket_name, url.object_name, provider=url.scheme,
371 generation=url.generation,
372 fields=['acl', 'generation', 'metageneration'])
373 current_acl = gcs_object.acl
374 if not current_acl:
375 self._WarnServiceAccounts()
376 self.logger.warning('Failed to set acl for %s. Please ensure you have '
377 'OWNER-role access to this resource.', url)
406 return 378 return
407 379
408 modification_count = 0 380 modification_count = 0
409 for change in self.changes: 381 for change in self.changes:
410 modification_count += change.Execute(uri, current_acl, self.logger) 382 modification_count += change.Execute(url, current_acl, 'acl', self.logger)
411 if modification_count == 0: 383 if modification_count == 0:
412 self.logger.info('No changes to {0}'.format(uri)) 384 self.logger.info('No changes to %s', url)
413 return 385 return
414 386
415 # TODO: Remove the concept of forcing when boto provides access to 387 try:
416 # bucket generation and metageneration. 388 if url.IsBucket():
417 headers = dict(self.headers) 389 preconditions = Preconditions(meta_gen_match=bucket.metageneration)
418 force = uri.names_bucket() 390 bucket_metadata = apitools_messages.Bucket(acl=current_acl)
419 if not force: 391 gsutil_api.PatchBucket(url.bucket_name, bucket_metadata,
420 key = uri.get_key() 392 preconditions=preconditions,
421 headers['x-goog-if-generation-match'] = key.generation 393 provider=url.scheme, fields=['id'])
422 headers['x-goog-if-metageneration-match'] = key.metageneration 394 else: # Object
395 preconditions = Preconditions(gen_match=gcs_object.generation,
396 meta_gen_match=gcs_object.metageneration)
423 397
424 # If this fails because of a precondition, it will raise a 398 object_metadata = apitools_messages.Object(acl=current_acl)
425 # GSResponseError for @Retry to handle. 399 gsutil_api.PatchObjectMetadata(
426 try: 400 url.bucket_name, url.object_name, object_metadata,
427 uri.set_acl(current_acl, uri.object_name, False, headers) 401 preconditions=preconditions, provider=url.scheme,
428 except GSResponseError as e: 402 generation=url.generation)
403 except BadRequestException as e:
429 # Don't retry on bad requests, e.g. invalid email address. 404 # Don't retry on bad requests, e.g. invalid email address.
430 if getattr(e, 'status', None) == 400: 405 raise CommandException('Received bad request from server: %s' % str(e))
431 raise CommandException('Received bad request from server: %s' % str(e))
432 raise
433 self.logger.info('Updated ACL on {0}'.format(uri))
434 406
435 # Command entry point. 407 self.logger.info('Updated ACL on %s', url)
408
436 def RunCommand(self): 409 def RunCommand(self):
410 """Command entry point for the acl command."""
437 action_subcommand = self.args.pop(0) 411 action_subcommand = self.args.pop(0)
438 (self.sub_opts, self.args) = getopt.getopt(self.args, 412 self.sub_opts, self.args = getopt.getopt(
439 self.command_spec[SUPPORTED_SUB_ARGS]) 413 self.args, self.command_spec.supported_sub_args)
440 self.CheckArguments() 414 self.CheckArguments()
415 self.def_acl = False
441 if action_subcommand == 'get': 416 if action_subcommand == 'get':
442 func = self._GetAcl 417 self.GetAndPrintAcl(self.args[0])
443 elif action_subcommand == 'set': 418 elif action_subcommand == 'set':
444 func = self._SetAcl 419 self._SetAcl()
445 elif action_subcommand in ('ch', 'change'): 420 elif action_subcommand in ('ch', 'change'):
446 func = self._ChAcl 421 self._ChAcl()
447 else: 422 else:
448 raise CommandException(('Invalid subcommand "%s" for the %s command.\n' 423 raise CommandException(('Invalid subcommand "%s" for the %s command.\n'
449 'See "gsutil help acl".') % 424 'See "gsutil help acl".') %
450 (action_subcommand, self.command_name)) 425 (action_subcommand, self.command_name))
451 426
452 func()
453 return 0 427 return 0
OLDNEW
« no previous file with comments | « gslib/commands/__init__.py ('k') | gslib/commands/cat.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698