class FakeCommandWithCompleters(Command):
  """Command with various completer types."""

  command_spec = Command.CreateCommandSpec(
      'fake2',
      argparse_arguments=[
          CommandArgument.MakeZeroOrMoreCloudURLsArgument(),
          CommandArgument.MakeZeroOrMoreFileURLsArgument(),
          CommandArgument.MakeZeroOrMoreCloudOrFileURLsArgument(),
          CommandArgument.MakeFreeTextArgument(),
          CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument(),
          CommandArgument.MakeFileURLOrCannedACLArgument(),
      ]
  )

  help_spec = Command.HelpSpec(
      help_name='fake2',
      help_name_aliases=[],
      help_type='command_help',
      help_one_line_summary='fake command for tests',
      help_text='fake command for tests',
      subcommand_help_text={}
  )

  def __init__(self):
    pass
示例#2
0
class AclCommand(Command):
  """Implementation of gsutil acl command."""

  # Command specification. See base class for documentation.
  command_spec = Command.CreateCommandSpec(
      'acl',
      command_name_aliases=['getacl', 'setacl', 'chacl'],
      usage_synopsis=_SYNOPSIS,
      min_args=2,
      max_args=NO_MAX,
      supported_sub_args='afRrg:u:d:p:',
      file_url_ok=False,
      provider_url_ok=False,
      urls_start_arg=1,
      gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
      gs_default_api=ApiSelector.JSON,
      argparse_arguments={
          'set': [
              CommandArgument.MakeFileURLOrCannedACLArgument(),
              CommandArgument.MakeZeroOrMoreCloudURLsArgument()
          ],
          'get': [
              CommandArgument.MakeNCloudURLsArgument(1)
          ],
          'ch': [
              CommandArgument.MakeZeroOrMoreCloudURLsArgument()
          ],
      }
  )
  # Help specification. See help_provider.py for documentation.
  help_spec = Command.HelpSpec(
      help_name='acl',
      help_name_aliases=['getacl', 'setacl', 'chmod', 'chacl'],
      help_type='command_help',
      help_one_line_summary='Get, set, or change bucket and/or object ACLs',
      help_text=_DETAILED_HELP_TEXT,
      subcommand_help_text={
          'get': _get_help_text, 'set': _set_help_text, 'ch': _ch_help_text},
  )

  def _CalculateUrlsStartArg(self):
    if not self.args:
      self.RaiseWrongNumberOfArgumentsException()
    if (self.args[0].lower() == 'set') or (self.command_alias_used == 'setacl'):
      return 1
    else:
      return 0

  def _SetAcl(self):
    """Parses options and sets ACLs on the specified buckets/objects."""
    self.continue_on_error = False
    if self.sub_opts:
      for o, unused_a in self.sub_opts:
        if o == '-a':
          self.all_versions = True
        elif o == '-f':
          self.continue_on_error = True
        elif o == '-r' or o == '-R':
          self.recursion_requested = True
        else:
          self.RaiseInvalidArgumentException()
    try:
      self.SetAclCommandHelper(SetAclFuncWrapper, SetAclExceptionHandler)
    except AccessDeniedException, unused_e:
      self._WarnServiceAccounts()
      raise
    if not self.everything_set_okay:
      raise CommandException('ACLs for some objects could not be set.')
示例#3
0
class AclCommand(Command):
    """Implementation of gsutil acl command."""

    # Command specification. See base class for documentation.
    command_spec = Command.CreateCommandSpec(
        'acl',
        command_name_aliases=['getacl', 'setacl', 'chacl'],
        usage_synopsis=_SYNOPSIS,
        min_args=2,
        max_args=NO_MAX,
        supported_sub_args='afRrg:u:d:p:',
        file_url_ok=False,
        provider_url_ok=False,
        urls_start_arg=1,
        gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
        gs_default_api=ApiSelector.JSON,
        argparse_arguments={
            'set': [
                CommandArgument.MakeFileURLOrCannedACLArgument(),
                CommandArgument.MakeZeroOrMoreCloudURLsArgument()
            ],
            'get': [CommandArgument.MakeNCloudURLsArgument(1)],
            'ch': [CommandArgument.MakeZeroOrMoreCloudURLsArgument()],
        })
    # Help specification. See help_provider.py for documentation.
    help_spec = Command.HelpSpec(
        help_name='acl',
        help_name_aliases=['getacl', 'setacl', 'chmod', 'chacl'],
        help_type='command_help',
        help_one_line_summary='Get, set, or change bucket and/or object ACLs',
        help_text=_DETAILED_HELP_TEXT,
        subcommand_help_text={
            'get': _get_help_text,
            'set': _set_help_text,
            'ch': _ch_help_text
        },
    )

    def _CalculateUrlsStartArg(self):
        if not self.args:
            self.RaiseWrongNumberOfArgumentsException()
        if (self.args[0].lower() == 'set') or (self.command_alias_used
                                               == 'setacl'):
            return 1
        else:
            return 0

    def _SetAcl(self):
        """Parses options and sets ACLs on the specified buckets/objects."""
        self.continue_on_error = False
        if self.sub_opts:
            for o, unused_a in self.sub_opts:
                if o == '-a':
                    self.all_versions = True
                elif o == '-f':
                    self.continue_on_error = True
                elif o == '-r' or o == '-R':
                    self.recursion_requested = True
                else:
                    self.RaiseInvalidArgumentException()
        try:
            self.SetAclCommandHelper(SetAclFuncWrapper, SetAclExceptionHandler)
        except AccessDeniedException as unused_e:
            self._WarnServiceAccounts()
            raise
        if not self.everything_set_okay:
            raise CommandException('ACLs for some objects could not be set.')

    def _ChAcl(self):
        """Parses options and changes ACLs on the specified buckets/objects."""
        self.parse_versions = True
        self.changes = []
        self.continue_on_error = False

        if self.sub_opts:
            for o, a in self.sub_opts:
                if o == '-f':
                    self.continue_on_error = True
                elif o == '-g':
                    if 'gserviceaccount.com' in a:
                        raise CommandException(
                            'Service accounts are considered users, not groups; please use '
                            '"gsutil acl ch -u" instead of "gsutil acl ch -g"')
                    self.changes.append(
                        acl_helper.AclChange(
                            a, scope_type=acl_helper.ChangeType.GROUP))
                elif o == '-p':
                    self.changes.append(
                        acl_helper.AclChange(
                            a, scope_type=acl_helper.ChangeType.PROJECT))
                elif o == '-u':
                    self.changes.append(
                        acl_helper.AclChange(
                            a, scope_type=acl_helper.ChangeType.USER))
                elif o == '-d':
                    self.changes.append(acl_helper.AclDel(a))
                elif o == '-r' or o == '-R':
                    self.recursion_requested = True
                else:
                    self.RaiseInvalidArgumentException()

        if not self.changes:
            raise CommandException('Please specify at least one access change '
                                   'with the -g, -u, or -d flags')

        if (not UrlsAreForSingleProvider(self.args)
                or StorageUrlFromString(self.args[0]).scheme != 'gs'):
            raise CommandException(
                'The "{0}" command can only be used with gs:// URLs'.format(
                    self.command_name))

        self.everything_set_okay = True
        self.ApplyAclFunc(
            _ApplyAclChangesWrapper,
            _ApplyExceptionHandler,
            self.args,
            object_fields=['acl', 'generation', 'metageneration'])
        if not self.everything_set_okay:
            raise CommandException('ACLs for some objects could not be set.')

    def _RaiseForAccessDenied(self, url):
        self._WarnServiceAccounts()
        raise CommandException(
            'Failed to set acl for %s. Please ensure you have '
            'OWNER-role access to this resource.' % url)

    @Retry(ServiceException, tries=3, timeout_secs=1)
    def ApplyAclChanges(self, name_expansion_result, thread_state=None):
        """Applies the changes in self.changes to the provided URL.

    Args:
      name_expansion_result: NameExpansionResult describing the target object.
      thread_state: If present, gsutil Cloud API instance to apply the changes.
    """
        if thread_state:
            gsutil_api = thread_state
        else:
            gsutil_api = self.gsutil_api

        url = name_expansion_result.expanded_storage_url
        if url.IsBucket():
            bucket = gsutil_api.GetBucket(url.bucket_name,
                                          provider=url.scheme,
                                          fields=['acl', 'metageneration'])
            current_acl = bucket.acl
        elif url.IsObject():
            gcs_object = encoding.JsonToMessage(
                apitools_messages.Object,
                name_expansion_result.expanded_result)
            current_acl = gcs_object.acl

        if not current_acl:
            self._RaiseForAccessDenied(url)
        if self._ApplyAclChangesAndReturnChangeCount(url, current_acl) == 0:
            self.logger.info('No changes to %s', url)
            return

        try:
            if url.IsBucket():
                preconditions = Preconditions(
                    meta_gen_match=bucket.metageneration)
                bucket_metadata = apitools_messages.Bucket(acl=current_acl)
                gsutil_api.PatchBucket(url.bucket_name,
                                       bucket_metadata,
                                       preconditions=preconditions,
                                       provider=url.scheme,
                                       fields=['id'])
            else:  # Object
                preconditions = Preconditions(
                    gen_match=gcs_object.generation,
                    meta_gen_match=gcs_object.metageneration)
                object_metadata = apitools_messages.Object(acl=current_acl)
                try:
                    gsutil_api.PatchObjectMetadata(url.bucket_name,
                                                   url.object_name,
                                                   object_metadata,
                                                   preconditions=preconditions,
                                                   provider=url.scheme,
                                                   generation=url.generation,
                                                   fields=['id'])
                except PreconditionException as e:
                    # Special retry case where we want to do an additional step, the read
                    # of the read-modify-write cycle, to fetch the correct object
                    # metadata before reattempting ACL changes.
                    self._RefetchObjectMetadataAndApplyAclChanges(
                        url, gsutil_api)

            self.logger.info('Updated ACL on %s', url)
        except BadRequestException as e:
            # Don't retry on bad requests, e.g. invalid email address.
            raise CommandException('Received bad request from server: %s' %
                                   str(e))
        except AccessDeniedException:
            self._RaiseForAccessDenied(url)
        except PreconditionException as e:
            # For objects, retry attempts should have already been handled.
            if url.IsObject():
                raise CommandException(str(e))
            # For buckets, raise PreconditionException and continue to next retry.
            raise e

    @Retry(PreconditionException, tries=3, timeout_secs=1)
    def _RefetchObjectMetadataAndApplyAclChanges(self, url, gsutil_api):
        """Reattempts object ACL changes after a PreconditionException."""
        gcs_object = gsutil_api.GetObjectMetadata(
            url.bucket_name,
            url.object_name,
            provider=url.scheme,
            fields=['acl', 'generation', 'metageneration'])
        current_acl = gcs_object.acl

        if self._ApplyAclChangesAndReturnChangeCount(url, current_acl) == 0:
            self.logger.info('No changes to %s', url)
            return

        object_metadata = apitools_messages.Object(acl=current_acl)
        preconditions = Preconditions(gen_match=gcs_object.generation,
                                      meta_gen_match=gcs_object.metageneration)
        gsutil_api.PatchObjectMetadata(url.bucket_name,
                                       url.object_name,
                                       object_metadata,
                                       preconditions=preconditions,
                                       provider=url.scheme,
                                       generation=gcs_object.generation,
                                       fields=['id'])

    def _ApplyAclChangesAndReturnChangeCount(self, storage_url, acl_message):
        modification_count = 0
        for change in self.changes:
            modification_count += change.Execute(storage_url, acl_message,
                                                 'acl', self.logger)
        return modification_count

    def RunCommand(self):
        """Command entry point for the acl command."""
        action_subcommand = self.args.pop(0)
        self.ParseSubOpts(check_args=True)

        # Commands with both suboptions and subcommands need to reparse for
        # suboptions, so we log again.
        metrics.LogCommandParams(sub_opts=self.sub_opts)
        self.def_acl = False
        if action_subcommand == 'get':
            metrics.LogCommandParams(subcommands=[action_subcommand])
            self.GetAndPrintAcl(self.args[0])
        elif action_subcommand == 'set':
            metrics.LogCommandParams(subcommands=[action_subcommand])
            self._SetAcl()
        elif action_subcommand in ('ch', 'change'):
            metrics.LogCommandParams(subcommands=[action_subcommand])
            self._ChAcl()
        else:
            raise CommandException(
                ('Invalid subcommand "%s" for the %s command.\n'
                 'See "gsutil help acl".') %
                (action_subcommand, self.command_name))

        return 0
示例#4
0
class DefAclCommand(Command):
    """Implementation of gsutil defacl command."""

    # Command specification. See base class for documentation.
    command_spec = Command.CreateCommandSpec(
        'defacl',
        command_name_aliases=['setdefacl', 'getdefacl', 'chdefacl'],
        usage_synopsis=_SYNOPSIS,
        min_args=2,
        max_args=NO_MAX,
        supported_sub_args='fg:u:d:p:',
        file_url_ok=False,
        provider_url_ok=False,
        urls_start_arg=1,
        gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
        gs_default_api=ApiSelector.JSON,
        argparse_arguments={
            'set': [
                CommandArgument.MakeFileURLOrCannedACLArgument(),
                CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument()
            ],
            'get': [CommandArgument.MakeNCloudBucketURLsArgument(1)],
            'ch': [CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument()],
        })
    # Help specification. See help_provider.py for documentation.
    help_spec = Command.HelpSpec(
        help_name='defacl',
        help_name_aliases=[
            'default acl', 'setdefacl', 'getdefacl', 'chdefacl'
        ],
        help_type='command_help',
        help_one_line_summary='Get, set, or change default ACL on buckets',
        help_text=_DETAILED_HELP_TEXT,
        subcommand_help_text={
            'get': _get_help_text,
            'set': _set_help_text,
            'ch': _ch_help_text
        },
    )

    def _CalculateUrlsStartArg(self):
        if not self.args:
            self.RaiseWrongNumberOfArgumentsException()
        if (self.args[0].lower() == 'set'
                or self.command_alias_used == 'setdefacl'):
            return 1
        else:
            return 0

    def _SetDefAcl(self):
        if not StorageUrlFromString(self.args[-1]).IsBucket():
            raise CommandException(
                'URL must name a bucket for the %s command' %
                self.command_name)
        try:
            self.SetAclCommandHelper(SetAclFuncWrapper, SetAclExceptionHandler)
        except AccessDeniedException:
            self._WarnServiceAccounts()
            raise

    def _GetDefAcl(self):
        if not StorageUrlFromString(self.args[0]).IsBucket():
            raise CommandException(
                'URL must name a bucket for the %s command' %
                self.command_name)
        self.GetAndPrintAcl(self.args[0])

    def _ChDefAcl(self):
        """Parses options and changes default object ACLs on specified buckets."""
        self.parse_versions = True
        self.changes = []

        if self.sub_opts:
            for o, a in self.sub_opts:
                if o == '-g':
                    self.changes.append(
                        aclhelpers.AclChange(
                            a, scope_type=aclhelpers.ChangeType.GROUP))
                if o == '-u':
                    self.changes.append(
                        aclhelpers.AclChange(
                            a, scope_type=aclhelpers.ChangeType.USER))
                if o == '-p':
                    self.changes.append(
                        aclhelpers.AclChange(
                            a, scope_type=aclhelpers.ChangeType.PROJECT))
                if o == '-d':
                    self.changes.append(aclhelpers.AclDel(a))

        if not self.changes:
            raise CommandException('Please specify at least one access change '
                                   'with the -g, -u, or -d flags')

        if (not UrlsAreForSingleProvider(self.args)
                or StorageUrlFromString(self.args[0]).scheme != 'gs'):
            raise CommandException(
                'The "{0}" command can only be used with gs:// URLs'.format(
                    self.command_name))

        bucket_urls = set()
        for url_arg in self.args:
            for result in self.WildcardIterator(url_arg):
                if not result.storage_url.IsBucket():
                    raise CommandException(
                        'The defacl ch command can only be applied to buckets.'
                    )
                bucket_urls.add(result.storage_url)

        for storage_url in bucket_urls:
            self.ApplyAclChanges(storage_url)

    @Retry(ServiceException, tries=3, timeout_secs=1)
    def ApplyAclChanges(self, url):
        """Applies the changes in self.changes to the provided URL."""
        bucket = self.gsutil_api.GetBucket(
            url.bucket_name,
            provider=url.scheme,
            fields=['defaultObjectAcl', 'metageneration'])

        # Default object ACLs can be blank if the ACL was set to private, or
        # if the user doesn't have permission. We warn about this with defacl get,
        # so just try the modification here and if the user doesn't have
        # permission they'll get an AccessDeniedException.
        current_acl = bucket.defaultObjectAcl

        modification_count = 0
        for change in self.changes:
            modification_count += change.Execute(url, current_acl, 'defacl',
                                                 self.logger)
        if modification_count == 0:
            self.logger.info('No changes to %s', url)
            return

        if not current_acl:
            # Use a sentinel value to indicate a private (no entries) default
            # object ACL.
            current_acl.append(PRIVATE_DEFAULT_OBJ_ACL)

        try:
            preconditions = Preconditions(meta_gen_match=bucket.metageneration)
            bucket_metadata = apitools_messages.Bucket(
                defaultObjectAcl=current_acl)
            self.gsutil_api.PatchBucket(url.bucket_name,
                                        bucket_metadata,
                                        preconditions=preconditions,
                                        provider=url.scheme,
                                        fields=['id'])
        except BadRequestException as e:
            # Don't retry on bad requests, e.g. invalid email address.
            raise CommandException('Received bad request from server: %s' %
                                   str(e))
        except AccessDeniedException:
            self._WarnServiceAccounts()
            raise CommandException(
                'Failed to set acl for %s. Please ensure you have '
                'OWNER-role access to this resource.' % url)

        self.logger.info('Updated default ACL on %s', url)

    def RunCommand(self):
        """Command entry point for the defacl command."""
        action_subcommand = self.args.pop(0)
        self.ParseSubOpts(check_args=True)
        self.def_acl = True
        self.continue_on_error = False
        if action_subcommand == 'get':
            func = self._GetDefAcl
        elif action_subcommand == 'set':
            func = self._SetDefAcl
        elif action_subcommand in ('ch', 'change'):
            func = self._ChDefAcl
        else:
            raise CommandException(
                ('Invalid subcommand "%s" for the %s command.\n'
                 'See "gsutil help defacl".') %
                (action_subcommand, self.command_name))
        func()
        return 0