Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / tools / telemetry / third_party / gsutilz / gslib / commands / acl.py
bloba63dcfe717b87411df801cd938abc140a2595273
1 # -*- coding: utf-8 -*-
2 # Copyright 2011 Google Inc. All Rights Reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Implementation of acl command for cloud storage providers."""
17 from __future__ import absolute_import
19 from gslib import aclhelpers
20 from gslib.cloud_api import AccessDeniedException
21 from gslib.cloud_api import BadRequestException
22 from gslib.cloud_api import Preconditions
23 from gslib.cloud_api import ServiceException
24 from gslib.command import Command
25 from gslib.command import SetAclExceptionHandler
26 from gslib.command import SetAclFuncWrapper
27 from gslib.command_argument import CommandArgument
28 from gslib.cs_api_map import ApiSelector
29 from gslib.exception import CommandException
30 from gslib.help_provider import CreateHelpText
31 from gslib.storage_url import StorageUrlFromString
32 from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages
33 from gslib.util import NO_MAX
34 from gslib.util import Retry
35 from gslib.util import UrlsAreForSingleProvider
37 _SET_SYNOPSIS = """
38 gsutil acl set [-f] [-r] [-a] file-or-canned_acl_name url...
39 """
41 _GET_SYNOPSIS = """
42 gsutil acl get url
43 """
45 _CH_SYNOPSIS = """
46 gsutil acl ch [-f] [-r] -u|-g|-d|-p <grant>... url...
48 where each <grant> is one of the following forms:
50 -u <id|email>:<perm>
51 -g <id|email|domain|All|AllAuth>:<perm>
52 -p <viewers|editors|owners>-<project number>
53 -d <id|email|domain|All|AllAuth>
54 """
56 _GET_DESCRIPTION = """
57 <B>GET</B>
58 The "acl get" command gets the ACL text for a bucket or object, which you can
59 save and edit for the acl set command.
60 """
62 _SET_DESCRIPTION = """
63 <B>SET</B>
64 The "acl set" command allows you to set an Access Control List on one or
65 more buckets and objects. The simplest way to use it is to specify one of
66 the canned ACLs, e.g.,:
68 gsutil acl set private gs://bucket
70 If you want to make an object or bucket publicly readable or writable, it is
71 recommended to use "acl ch", to avoid accidentally removing OWNER permissions.
72 See "gsutil help acl ch" for details.
74 See "gsutil help acls" for a list of all canned ACLs.
76 If you want to define more fine-grained control over your data, you can
77 retrieve an ACL using the "acl get" command, save the output to a file, edit
78 the file, and then use the "acl set" command to set that ACL on the buckets
79 and/or objects. For example:
81 gsutil acl get gs://bucket/file.txt > acl.txt
83 Make changes to acl.txt such as adding an additional grant, then:
85 gsutil acl set acl.txt gs://cats/file.txt
87 Note that you can set an ACL on multiple buckets or objects at once,
88 for example:
90 gsutil acl set acl.txt gs://bucket/*.jpg
92 If you have a large number of ACLs to update you might want to use the
93 gsutil -m option, to perform a parallel (multi-threaded/multi-processing)
94 update:
96 gsutil -m acl set acl.txt gs://bucket/*.jpg
98 Note that multi-threading/multi-processing is only done when the named URLs
99 refer to objects. gsutil -m acl set gs://bucket1 gs://bucket2 will run the
100 acl set operations sequentially.
103 <B>SET OPTIONS</B>
104 The "set" sub-command has the following options
106 -R, -r Performs "acl set" request recursively, to all objects under
107 the specified URL.
109 -a Performs "acl set" request on all object versions.
111 -f Normally gsutil stops at the first error. The -f option causes
112 it to continue when it encounters errors. If some of the ACLs
113 couldn't be set, gsutil's exit status will be non-zero even if
114 this flag is set. This option is implicitly set when running
115 "gsutil -m acl...".
118 _CH_DESCRIPTION = """
119 <B>CH</B>
120 The "acl ch" (or "acl change") command updates access control lists, similar
121 in spirit to the Linux chmod command. You can specify multiple access grant
122 additions and deletions in a single command run; all changes will be made
123 atomically to each object in turn. For example, if the command requests
124 deleting one grant and adding a different grant, the ACLs being updated will
125 never be left in an intermediate state where one grant has been deleted but
126 the second grant not yet added. Each change specifies a user or group grant
127 to add or delete, and for grant additions, one of R, W, O (for the
128 permission to be granted). A more formal description is provided in a later
129 section; below we provide examples.
131 <B>CH EXAMPLES</B>
132 Examples for "ch" sub-command:
134 Grant anyone on the internet READ access to the object example-object:
136 gsutil acl ch -u AllUsers:R gs://example-bucket/example-object
138 NOTE: By default, publicly readable objects are served with a Cache-Control
139 header allowing such objects to be cached for 3600 seconds. If you need to
140 ensure that updates become visible immediately, you should set a
141 Cache-Control header of "Cache-Control:private, max-age=0, no-transform" on
142 such objects. For help doing this, see "gsutil help setmeta".
144 Grant anyone on the internet WRITE access to the bucket example-bucket
145 (WARNING: this is not recommended as you will be responsible for the content):
147 gsutil acl ch -u AllUsers:W gs://example-bucket
149 Grant the user john.doe@example.com WRITE access to the bucket
150 example-bucket:
152 gsutil acl ch -u john.doe@example.com:WRITE gs://example-bucket
154 Grant the group admins@example.com OWNER access to all jpg files in
155 the top level of example-bucket:
157 gsutil acl ch -g admins@example.com:O gs://example-bucket/*.jpg
159 Grant the owners of project example-project-123 WRITE access to the bucket
160 example-bucket:
162 gsutil acl ch -p owners-example-project-123:W gs://example-bucket
164 NOTE: You can replace 'owners' with 'viewers' or 'editors' to grant access
165 to a project's viewers/editors respectively.
167 Grant the user with the specified canonical ID READ access to all objects
168 in example-bucket that begin with folder/:
170 gsutil acl ch -r \\
171 -u 84fac329bceSAMPLE777d5d22b8SAMPLE785ac2SAMPLE2dfcf7c4adf34da46:R \\
172 gs://example-bucket/folder/
174 Grant the service account foo@developer.gserviceaccount.com WRITE access to
175 the bucket example-bucket:
177 gsutil acl ch -u foo@developer.gserviceaccount.com:W gs://example-bucket
179 Grant all users from the `Google Apps
180 <https://www.google.com/work/apps/business/>`_ domain my-domain.org READ
181 access to the bucket gcs.my-domain.org:
183 gsutil acl ch -g my-domain.org:R gs://gcs.my-domain.org
185 Remove any current access by john.doe@example.com from the bucket
186 example-bucket:
188 gsutil acl ch -d john.doe@example.com gs://example-bucket
190 If you have a large number of objects to update, enabling multi-threading
191 with the gsutil -m flag can significantly improve performance. The
192 following command adds OWNER for admin@example.org using
193 multi-threading:
195 gsutil -m acl ch -r -u admin@example.org:O gs://example-bucket
197 Grant READ access to everyone from my-domain.org and to all authenticated
198 users, and grant OWNER to admin@mydomain.org, for the buckets
199 my-bucket and my-other-bucket, with multi-threading enabled:
201 gsutil -m acl ch -r -g my-domain.org:R -g AllAuth:R \\
202 -u admin@mydomain.org:O gs://my-bucket/ gs://my-other-bucket
204 <B>CH ROLES</B>
205 You may specify the following roles with either their shorthand or
206 their full name:
208 R: READ
209 W: WRITE
210 O: OWNER
212 <B>CH ENTITIES</B>
213 There are four different entity types: Users, Groups, All Authenticated Users,
214 and All Users.
216 Users are added with -u and a plain ID or email address, as in
217 "-u john-doe@gmail.com:r". Note: Service Accounts are considered to be users.
219 Groups are like users, but specified with the -g flag, as in
220 "-g power-users@example.com:fc". Groups may also be specified as a full
221 domain, as in "-g my-company.com:r".
223 AllAuthenticatedUsers and AllUsers are specified directly, as
224 in "-g AllUsers:R" or "-g AllAuthenticatedUsers:O". These are case
225 insensitive, and may be shortened to "all" and "allauth", respectively.
227 Removing roles is specified with the -d flag and an ID, email
228 address, domain, or one of AllUsers or AllAuthenticatedUsers.
230 Many entities' roles can be specified on the same command line, allowing
231 bundled changes to be executed in a single run. This will reduce the number of
232 requests made to the server.
234 <B>CH OPTIONS</B>
235 The "ch" sub-command has the following options
237 -d Remove all roles associated with the matching entity.
239 -f Normally gsutil stops at the first error. The -f option causes
240 it to continue when it encounters errors. With this option the
241 gsutil exit status will be 0 even if some ACLs couldn't be
242 changed.
244 -g Add or modify a group entity's role.
246 -p Add or modify a project viewers/editors/owners role.
248 -R, -r Performs acl ch request recursively, to all objects under the
249 specified URL.
251 -u Add or modify a user entity's role.
254 _SYNOPSIS = (_SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') +
255 _CH_SYNOPSIS.lstrip('\n') + '\n\n')
257 _DESCRIPTION = ("""
258 The acl command has three sub-commands:
259 """ + '\n'.join([_GET_DESCRIPTION, _SET_DESCRIPTION, _CH_DESCRIPTION]))
261 _DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION)
263 _get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION)
264 _set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION)
265 _ch_help_text = CreateHelpText(_CH_SYNOPSIS, _CH_DESCRIPTION)
268 def _ApplyExceptionHandler(cls, exception):
269 cls.logger.error('Encountered a problem: %s', exception)
270 cls.everything_set_okay = False
273 def _ApplyAclChangesWrapper(cls, url_or_expansion_result, thread_state=None):
274 cls.ApplyAclChanges(url_or_expansion_result, thread_state=thread_state)
277 class AclCommand(Command):
278 """Implementation of gsutil acl command."""
280 # Command specification. See base class for documentation.
281 command_spec = Command.CreateCommandSpec(
282 'acl',
283 command_name_aliases=['getacl', 'setacl', 'chacl'],
284 usage_synopsis=_SYNOPSIS,
285 min_args=2,
286 max_args=NO_MAX,
287 supported_sub_args='afRrg:u:d:p:',
288 file_url_ok=False,
289 provider_url_ok=False,
290 urls_start_arg=1,
291 gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
292 gs_default_api=ApiSelector.JSON,
293 argparse_arguments={
294 'set': [
295 CommandArgument.MakeFileURLOrCannedACLArgument(),
296 CommandArgument.MakeZeroOrMoreCloudURLsArgument()
298 'get': [
299 CommandArgument.MakeNCloudURLsArgument(1)
301 'ch': [
302 CommandArgument.MakeZeroOrMoreCloudURLsArgument()
306 # Help specification. See help_provider.py for documentation.
307 help_spec = Command.HelpSpec(
308 help_name='acl',
309 help_name_aliases=['getacl', 'setacl', 'chmod', 'chacl'],
310 help_type='command_help',
311 help_one_line_summary='Get, set, or change bucket and/or object ACLs',
312 help_text=_DETAILED_HELP_TEXT,
313 subcommand_help_text={
314 'get': _get_help_text, 'set': _set_help_text, 'ch': _ch_help_text},
317 def _CalculateUrlsStartArg(self):
318 if not self.args:
319 self.RaiseWrongNumberOfArgumentsException()
320 if (self.args[0].lower() == 'set') or (self.command_alias_used == 'setacl'):
321 return 1
322 else:
323 return 0
325 def _SetAcl(self):
326 """Parses options and sets ACLs on the specified buckets/objects."""
327 self.continue_on_error = False
328 if self.sub_opts:
329 for o, unused_a in self.sub_opts:
330 if o == '-a':
331 self.all_versions = True
332 elif o == '-f':
333 self.continue_on_error = True
334 elif o == '-r' or o == '-R':
335 self.recursion_requested = True
336 else:
337 self.RaiseInvalidArgumentException()
338 try:
339 self.SetAclCommandHelper(SetAclFuncWrapper, SetAclExceptionHandler)
340 except AccessDeniedException, unused_e:
341 self._WarnServiceAccounts()
342 raise
343 if not self.everything_set_okay:
344 raise CommandException('ACLs for some objects could not be set.')
346 def _ChAcl(self):
347 """Parses options and changes ACLs on the specified buckets/objects."""
348 self.parse_versions = True
349 self.changes = []
350 self.continue_on_error = False
352 if self.sub_opts:
353 for o, a in self.sub_opts:
354 if o == '-f':
355 self.continue_on_error = True
356 elif o == '-g':
357 if 'gserviceaccount.com' in a:
358 raise CommandException(
359 'Service accounts are considered users, not groups; please use '
360 '"gsutil acl ch -u" instead of "gsutil acl ch -g"')
361 self.changes.append(
362 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.GROUP))
363 elif o == '-p':
364 self.changes.append(
365 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.PROJECT))
366 elif o == '-u':
367 self.changes.append(
368 aclhelpers.AclChange(a, scope_type=aclhelpers.ChangeType.USER))
369 elif o == '-d':
370 self.changes.append(aclhelpers.AclDel(a))
371 elif o == '-r' or o == '-R':
372 self.recursion_requested = True
373 else:
374 self.RaiseInvalidArgumentException()
376 if not self.changes:
377 raise CommandException(
378 'Please specify at least one access change '
379 'with the -g, -u, or -d flags')
381 if (not UrlsAreForSingleProvider(self.args) or
382 StorageUrlFromString(self.args[0]).scheme != 'gs'):
383 raise CommandException(
384 'The "{0}" command can only be used with gs:// URLs'.format(
385 self.command_name))
387 self.everything_set_okay = True
388 self.ApplyAclFunc(_ApplyAclChangesWrapper, _ApplyExceptionHandler,
389 self.args)
390 if not self.everything_set_okay:
391 raise CommandException('ACLs for some objects could not be set.')
393 def _RaiseForAccessDenied(self, url):
394 self._WarnServiceAccounts()
395 raise CommandException('Failed to set acl for %s. Please ensure you have '
396 'OWNER-role access to this resource.' % url)
398 @Retry(ServiceException, tries=3, timeout_secs=1)
399 def ApplyAclChanges(self, name_expansion_result, thread_state=None):
400 """Applies the changes in self.changes to the provided URL.
402 Args:
403 name_expansion_result: NameExpansionResult describing the target object.
404 thread_state: If present, gsutil Cloud API instance to apply the changes.
406 if thread_state:
407 gsutil_api = thread_state
408 else:
409 gsutil_api = self.gsutil_api
411 url = name_expansion_result.expanded_storage_url
413 if url.IsBucket():
414 bucket = gsutil_api.GetBucket(url.bucket_name, provider=url.scheme,
415 fields=['acl', 'metageneration'])
416 current_acl = bucket.acl
417 elif url.IsObject():
418 gcs_object = gsutil_api.GetObjectMetadata(
419 url.bucket_name, url.object_name, provider=url.scheme,
420 generation=url.generation,
421 fields=['acl', 'generation', 'metageneration'])
422 current_acl = gcs_object.acl
423 if not current_acl:
424 self._RaiseForAccessDenied(url)
426 modification_count = 0
427 for change in self.changes:
428 modification_count += change.Execute(url, current_acl, 'acl', self.logger)
429 if modification_count == 0:
430 self.logger.info('No changes to %s', url)
431 return
433 try:
434 if url.IsBucket():
435 preconditions = Preconditions(meta_gen_match=bucket.metageneration)
436 bucket_metadata = apitools_messages.Bucket(acl=current_acl)
437 gsutil_api.PatchBucket(url.bucket_name, bucket_metadata,
438 preconditions=preconditions,
439 provider=url.scheme, fields=['id'])
440 else: # Object
441 preconditions = Preconditions(gen_match=gcs_object.generation,
442 meta_gen_match=gcs_object.metageneration)
444 object_metadata = apitools_messages.Object(acl=current_acl)
445 gsutil_api.PatchObjectMetadata(
446 url.bucket_name, url.object_name, object_metadata,
447 preconditions=preconditions, provider=url.scheme,
448 generation=url.generation)
449 except BadRequestException as e:
450 # Don't retry on bad requests, e.g. invalid email address.
451 raise CommandException('Received bad request from server: %s' % str(e))
452 except AccessDeniedException:
453 self._RaiseForAccessDenied(url)
455 self.logger.info('Updated ACL on %s', url)
457 def RunCommand(self):
458 """Command entry point for the acl command."""
459 action_subcommand = self.args.pop(0)
460 self.ParseSubOpts(check_args=True)
461 self.def_acl = False
462 if action_subcommand == 'get':
463 self.GetAndPrintAcl(self.args[0])
464 elif action_subcommand == 'set':
465 self._SetAcl()
466 elif action_subcommand in ('ch', 'change'):
467 self._ChAcl()
468 else:
469 raise CommandException(('Invalid subcommand "%s" for the %s command.\n'
470 'See "gsutil help acl".') %
471 (action_subcommand, self.command_name))
473 return 0