Exemplo n.º 1
0
def ValidateUserName(user_name):
    """Validates the initial user account name.

  Args:
    user_name: The user account name to be validated.

  Raises:
    CommandError: The user name is invalid.
  """
    # Validates according to
    # http://technet.microsoft.com/en-us/library/cc770642.aspx
    # We also ban the builtin administrator account name.
    if not user_name:
        raise gcutil_errors.CommandError('The user name is missing.')
    if user_name.lower() == _BUILTIN_ADMINISTRATOR:
        raise gcutil_errors.CommandError(
            'Using "%s" as initial user name is not allowed. '
            'Please choose a different user name by setting metadata entry %s.'
            % (_BUILTIN_ADMINISTRATOR,
               metadata.INITIAL_WINDOWS_USER_METADATA_NAME))
    if len(user_name) > _MAX_USER_NAME_LENGTH:
        raise gcutil_errors.CommandError(
            'User name length must not exceed %d characters: %s' %
            (_MAX_USER_NAME_LENGTH, user_name))
    if any(c in _INVALID_USER_NAME_CHARS for c in user_name):
        raise gcutil_errors.CommandError(
            'User name %s contains invalid characters.' % user_name)
    if all(c in ' .' for c in user_name):
        raise gcutil_errors.CommandError(
            'User name cannot consist solely of periods or spaces.')
Exemplo n.º 2
0
def ValidateStrongPasswordRequirement(password, user_account_name):
  """Validates that a password meets strong password requirement.

  The strong password must be at least 8 chars long and meet the
  Windows password complexity requirement documented at
  http://technet.microsoft.com/en-us/library/cc786468(v=ws.10).aspx

  Args:
    password: Password to be validated.
    user_account_name: The user account name.

  Raises:
    CommandError: The password does not meet the strong password requirement.
  """
  if not password or len(password) < MIN_PASSWORD_LENGTH:
    raise gcutil_errors.CommandError(
        'Windows password must be at least %d characters long.' %
        MIN_PASSWORD_LENGTH)

  categories = 0
  uppercase = False
  lowercase = False
  digit = False
  nonalphanum = False
  alpha = False
  for x in password:
    if x.isupper():
      uppercase = True
    elif x.islower():
      lowercase = True
    elif x.isdigit():
      digit = True
    elif x in NON_ALPHA_NUM_CHARS:
      nonalphanum = True
    elif x.isalpha():
      alpha = True
    categories = uppercase + lowercase + digit + nonalphanum + alpha
    if categories >= MIN_CHAR_CATEGORIES:
      break
  if categories < MIN_CHAR_CATEGORIES:
    raise gcutil_errors.CommandError(
        'Windows password must contain at least 3 types of characters. See '
        'http://technet.microsoft.com/en-us/library/cc786468(v=ws.10).aspx')

  # Currently, we do not set the user display name.  So we only need to
  # check and make sure that the password does not contain the user account
  # name.
  if (len(user_account_name) >= 3
      and user_account_name.lower() in password.lower()):
    raise gcutil_errors.CommandError(
        'Windows password cannot contain the user account name: %s.' %
        user_account_name)
    def Handle(self, target_pool_name):
        """Set the backup pool and failover ratio for the target pool.

    Args:
      target_pool_name: The name of the target pool to update.

    Returns:
      The result of inserting the forwarding rule.
    """
        self._AutoDetectRegion()

        target_pool_context = self._context_parser.ParseContextOrPrompt(
            'targetPools', target_pool_name)

        if (self._flags['failover_ratio'].present !=
                self._flags['backup_pool'].present):
            raise gcutil_errors.CommandError(
                '--failover_ratio and --backup_pool '
                'must be either both set or both not '
                'set.')

        kwargs = self._PrepareRequestArgs(target_pool_context)

        request_body = {}
        if self._flags.backup_pool:
            kwargs['failoverRatio'] = self._flags.failover_ratio
            backup_pool = self._context_parser.NormalizeOrPrompt(
                'targetPools', self._flags.backup_pool)
            request_body['target'] = backup_pool

        set_backup_request = self.api.target_pools.setBackup(body=request_body,
                                                             **kwargs)
        return set_backup_request.execute()
    def Handle(self, target_pool_name):
        """Remove the health check from the target pool.

    Args:
      target_pool_name: The name of the target_pool to update.

    Returns:
      The result of removing the health check from the target_pool.
    """
        self._AutoDetectRegion()

        target_pool_context = self._context_parser.ParseContextOrPrompt(
            'targetPools', target_pool_name)

        if self._flags.health_check is None:
            raise gcutil_errors.CommandError(
                'Please specify a --health_check.')

        health_check = self._context_parser.NormalizeOrPrompt(
            'httpHealthChecks', self._flags.health_check)

        target_pool_resource = {
            'healthChecks': [{
                'healthCheck': health_check
            }]
        }

        kwargs = self._PrepareRequestArgs(target_pool_context)
        target_pool_request = self.api.target_pools.removeHealthCheck(
            body=target_pool_resource, **kwargs)
        return target_pool_request.execute()
Exemplo n.º 5
0
    def GetPublicKey():
        """Returns the standard Compute key for the current user.

    If the key doesn't exist, it will be created and will
    interactively prompt the user.

    Returns:
      A dictionary of an user/key pair for the user's ssh key.

    Raises:
      gcutil_errors.CommandError:  Requires --permit_root_ssh flag to log in
      as root.
    """
        if FLAGS.ssh_user == 'root' and not FLAGS.permit_root_ssh:
            raise gcutil_errors.CommandError(
                'Logging into instances as root is not recommended. If you actually '
                'wish to log in as root, you must provide the --permit_root_ssh '
                'flag.')

        SshKeys.EnsureSshKeyCreated()

        return {
            'user': FLAGS.ssh_user,
            'key': SshKeys.GetKeyFromFile(FLAGS.public_key_file)
        }
Exemplo n.º 6
0
def GeneratePassword(user_account_name):
  """Generates a random password for Windows user account.

  Args:
    user_account_name: The user account name that we generate password for.

  Returns:
    The generated password.
  """
  r = random.SystemRandom()
  for _ in xrange(_MAX_GENERATION_ATTEMPT):
    # First pick 12 chars randomly from letters and digits.
    char_list = list(r.choice(_CANDIDATES_ALPHA_NUM)
                     for _ in xrange(12))
    # Split the list into 3 groups of 4 chars each.
    # Add a random non-alpha-num char between the groups.
    char_list.insert(4, r.choice(_CANDIDATES_NON_ALPHA_NUM))
    char_list.insert(9, r.choice(_CANDIDATES_NON_ALPHA_NUM))
    password = ''.join(char_list)

    try:
      ValidateStrongPasswordRequirement(password, user_account_name)
      return password
    except gcutil_errors.CommandError:
      pass
  raise gcutil_errors.CommandError(
      'Failed to generate password after %d attempts.'
      % _MAX_GENERATION_ATTEMPT)
Exemplo n.º 7
0
def GenerateLocalUserNameBasedOnProject(project_id_or_number, api):
    """Generates the Windows user account name based on the project ID.

  Args:
    project_id_or_number: The string that is project ID or project number.
    api: The Google Compute Engine API client.

  Returns:
    Generated user account name.
  """
    project_id = utils.GetProjectId(project_id_or_number, api)

    # We first remove the domain part in the project ID, and then take up to
    # the first 20 chars in the remaining project ID as the user name.
    # (Windows local user name should not be more than 20 char long.)
    user_name = project_id.split(':')[-1][:_MAX_USER_NAME_LENGTH]

    # Char set for project id is more restrictive than Windows user name, but we
    # still do the check just in case.
    try:
        ValidateUserName(user_name)
    except gcutil_errors.CommandError as e:
        raise gcutil_errors.CommandError(
            'The user name %s generated from project id %s is invalid. '
            'This is unexpected.  Please double check the project id. '
            'Error: %s' % (user_name, project_id, e.message))

    return user_name
Exemplo n.º 8
0
    def Handle(self, firewall_name):
        """Add the specified firewall.

    Args:
      firewall_name: The name of the firewall to add.

    Returns:
      The result of inserting the firewall.

    Raises:
      gcutil_errors.CommandError: If the passed flag values cannot be
          interpreted.
    """
        if not self._flags.allowed:
            raise gcutil_errors.CommandError(
                'You must specify at least one rule through --allowed.')

        firewall_context = self._context_parser.ParseContextOrPrompt(
            'firewalls', firewall_name)

        firewall_resource = {
            'kind': self._GetResourceApiKind('firewall'),
            'name': firewall_context['firewall'],
            'description': self._flags.description,
        }

        if self._flags.network is not None:
            firewall_resource[
                'network'] = self._context_parser.NormalizeOrPrompt(
                    'networks', self._flags.network)

        if (not self._flags.allowed_ip_sources
                and not self._flags.allowed_tag_sources):
            self._flags.allowed_ip_sources.append('0.0.0.0/0')

        try:
            firewall_rules = FirewallRules(self._flags.allowed,
                                           self._flags.allowed_ip_sources)
            firewall_rules.SetTags(self._flags.allowed_tag_sources,
                                   self._flags.target_tags)
            firewall_rules.AddToFirewall(firewall_resource)
            firewall_request = self.api.firewalls.insert(
                project=firewall_context['project'], body=firewall_resource)
            return firewall_request.execute()
        except ValueError, e:
            raise gcutil_errors.CommandError(e)
Exemplo n.º 9
0
  def _VerifyAndGetTargetFromFlags(
      self, target_pool_flag, target_instance_flag):
    """Gets forwarding rule target from flag values.

    Args:
      target_pool_flag: the value of --target_pool flag
      target_instance_flag: the value of --target_instance flag
    Returns:
      (target_pool, target_instance) pair, with one and only one field set.
    """
    if (target_pool_flag is None) == (target_instance_flag is None):
      raise gcutil_errors.CommandError('Please specify exactly one of '
                                       '--target_pool or --target_instance.')
    elif target_pool_flag:
      return target_pool_flag, None
    else:
      if self.api.version < version.get('v1'):
        raise gcutil_errors.CommandError(
            'Version does not support target instance.')
      return None, target_instance_flag
    def Handle(self, target_pool_name):
        """Remove the instance from the target pool.

    Args:
      target_pool_name: The name of the target_pool to update.

    Returns:
      The result of removing an instance from the target_pool.
    """
        self._AutoDetectRegion()
        self._AutoDetectZoneForInstances()

        target_pool_context = self._context_parser.ParseContextOrPrompt(
            'targetPools', target_pool_name)

        if not self._flags.instances and not self._flags.instance:
            raise gcutil_errors.CommandError('Please specify --instances.')
        if self._flags.instance:
            gcutil_logging.LOGGER.warn(
                '--instance flag is deprecated; use --instances to remove one or more'
                ' instances')
            instances = [self._flags.instance]
        else:
            instances = self._flags.instances
        requests = []
        kwargs = self._PrepareRequestArgs(target_pool_context)
        instance_urls = []
        for zone_instance in instances:
            try:
                path = self._context_parser.NormalizeOrPrompt(
                    'instances', zone_instance)
            except ValueError:
                zone, instance = self.ParseZoneInstancePair(zone_instance)
                path = self.NormalizePerZoneResourceName(
                    self._project, zone, 'instances', instance)

            instance_urls.append(path)

        remove_instance_request_resource = {
            'instances': [{
                'instance': instance_url
            } for instance_url in instance_urls]
        }
        requests.append(
            self.api.target_pools.removeInstance(
                body=remove_instance_request_resource, **kwargs))

        # This may be multiple API calls.
        return self.ExecuteRequests(requests)
Exemplo n.º 11
0
  def HandleCommand(self):
    """Set the metadata common to all instances in the specified project.

    Args:
      None.

    Returns:
      The result of setting the project wide metadata.

    Raises:

      gcutil_errors.CommandError: If the update would cause some metadata to
        be deleted.
    """
    new_metadata = self._metadata_flags_processor.GatherMetadata()

    project = self._flags.project
    project_context = self._context_parser.ParseContextOrPrompt('projects',
                                                                project)

    if not self._flags.force:
      get_request = self.api.projects.get(project=project_context['project'])
      project_resource = get_request.execute()
      project_metadata = project_resource.get('commonInstanceMetadata', [])
      if 'kind' in project_metadata:
        project_metadata = project_metadata.get('items', [])
      existing_keys = set([entry['key'] for entry in project_metadata])
      new_keys = set([entry['key'] for entry in new_metadata])
      dropped_keys = existing_keys - new_keys
      if dropped_keys:
        raise gcutil_errors.CommandError(
            'Discarding update that would wipe out the following metadata: %s.'
            '\n\nRe-run with the -f flag to force the update.' %
            ', '.join(list(dropped_keys)))

    metadata_resource = {'kind': self._GetResourceApiKind('metadata'),
                         'items': new_metadata}
    if self._flags.fingerprint:
      metadata_resource['fingerprint'] = self._flags.fingerprint

    project_request = self.api.projects.setCommonInstanceMetadata(
        project=project_context['project'],
        body=metadata_resource)
    return project_request.execute()
Exemplo n.º 12
0
    def Handle(self, snapshot_name):
        """Add the specified snapshot.

    Args:
      snapshot_name: The name of the snapshot to add

    Returns:
      The result of inserting the snapshot.
    """
        self._AutoDetectZone()
        snapshot_context = self._context_parser.ParseContextOrPrompt(
            'snapshots', snapshot_name)

        if not self._flags.source_disk:
            disk = self._presenter.PromptForDisk(self.api.disks)
            if not disk:
                raise gcutil_errors.CommandError(
                    'You cannot create a snapshot if you have no disks.')
            self._flags.source_disk = disk['selfLink']

        disk_context = self._context_parser.ParseContextOrPrompt(
            'disks', self._flags.source_disk)

        snapshot_resource = {
            'kind': self._GetResourceApiKind('snapshot'),
            'name': snapshot_context['snapshot'],
            'description': self._flags.description
        }

        kwargs = {
            'project': snapshot_context['project'],
            'zone': disk_context['zone'],
            'disk': disk_context['disk'],
            'body': snapshot_resource
        }

        snapshot_request = self.api.disks.createSnapshot(**kwargs)
        result = snapshot_request.execute()

        result = self._WaitForCompletionIfNeeded(result, snapshot_context,
                                                 'disks')
        return result
Exemplo n.º 13
0
    def RunWithFlagsAndPositionalArgs(self, flag_values,
                                      unused_pos_arg_values):
        """Run the command, returning the result.

    Args:
      flag_values: The parsed FlagValues instance.
      unused_pos_arg_values: The positional args.

    Raises:
      gcutil_errors.CommandError: If valid credentials cannot be retrieved.

    Returns:
      0 if the command completes successfully, otherwise 1.

    Raises:
      CommandError: if valid credentials are not located.
    """
        cred = auth_helper.GetCredentialFromStore(
            scopes.DEFAULT_AUTH_SCOPES,
            ask_user=not flag_values.just_check_auth,
            force_reauth=flag_values.force_reauth)
        if not cred:
            raise gcutil_errors.CommandError(
                'Could not get valid credentials for API.')

        if flag_values.confirm_email:
            http = self._AuthenticateWrapper(utils.GetHttp())
            resp, content = http.request(
                'https://www.googleapis.com/userinfo/v2/me')
            if resp.status != 200:
                LOGGER.info('Could not get user info for token.  <%d %s>',
                            resp.status, resp.reason)
            userinfo = json.loads(content)
            if 'email' in userinfo and userinfo['email']:
                LOGGER.info('Authorization succeeded for user %s',
                            userinfo['email'])
            else:
                LOGGER.info('Could not get email for token.')
        else:
            LOGGER.info('Authentication succeeded.')
        return (None, [])
    def Handle(self, target_pool_name):
        """Add the specified target pool.

    Args:
      target_pool_name: The name of the target pool to add.

    Returns:
      The result of inserting the target pool.
    """

        self._AutoDetectZoneForInstances()

        target_pool_context = self._context_parser.ParseContextOrPrompt(
            'targetPools', target_pool_name)

        # Under no circumstance should we prompt for region again if we already
        # have. If it was otherwise specified in a fully qualified path,
        # assume that that's what the user meant for the backupPool.
        if not self._flags.region:
            self._flags.region = target_pool_context['region']

        target_pool_resource = {
            'kind': self._GetResourceApiKind('targetPool'),
            'name': target_pool_context['targetPool'],
            'description': self._flags.description,
        }

        target_pool_resource['sessionAffinity'] = self._flags.session_affinity
        if (self._flags['failover_ratio'].present !=
                self._flags['backup_pool'].present):
            raise gcutil_errors.CommandError(
                '--failover_ratio and --backup_pool '
                'must be either both set or both not '
                'set.')
        if self._flags.failover_ratio:
            target_pool_resource['failoverRatio'] = self._flags.failover_ratio

            if self._flags.backup_pool:
                backup_pool = self._context_parser.NormalizeOrPrompt(
                    'targetPools', self._flags.backup_pool)
                target_pool_resource['backupPool'] = backup_pool

        health_checks = []
        for health_check in self._flags.health_checks:
            health_checks.append(
                self._context_parser.NormalizeOrPrompt('httpHealthChecks',
                                                       health_check))
        target_pool_resource['healthChecks'] = health_checks

        instances = []
        for zone_instance in self._flags.instances:
            try:
                path = self._context_parser.NormalizeOrPrompt(
                    'instances', zone_instance)
            except ValueError:
                zone, instance = self.ParseZoneInstancePair(zone_instance)
                path = self.NormalizePerZoneResourceName(
                    self._project, zone, 'instances', instance)

            instances.append(path)

        target_pool_resource['instances'] = instances

        kwargs = {'region': target_pool_context['region']}

        target_pool_request = (self.api.target_pools.insert(
            project=target_pool_context['project'],
            body=target_pool_resource,
            **kwargs))

        return target_pool_request.execute()
Exemplo n.º 15
0
def CreateComputeApi(
    http,
    api_version,
    download=False,
    server=None):
  """Builds the Google Compute Engine API to use.

  Args:
    http: a httplib2.Http like object for communication.
    api_version: the version of the API to create.
    download: bool. Download the discovery document from the discovery service.
    server: URL of the API service host.

  Returns:
    The ComputeApi object to use.

  Raises:
    gcutil_errors.CommandError: If loading the discovery doc fails.
  """
  # Use default server if none provided.
  default_server = 'https://www.googleapis.com/'
  server = server or default_server

  # Load the discovery document.
  discovery_document = None

  # Try to download the discovery document
  if download or server != default_server:
    url = '{server}/discovery/v1/apis/compute/{version}/rest'.format(
        server=server.rstrip('/'),
        version=api_version)
    response, content = http.request(url)

    if response.status == 200:
      discovery_document = content
    else:
      raise gcutil_errors.CommandError(
          'Could not load discovery document from %s:\n%s' % (
              url, content))
  else:
    # Try to load locally
    discovery_path = os.path.join(
        os.path.dirname(__file__), 'compute', '%s.json' % api_version)
    try:
      with open(discovery_path) as discovery_file:
        discovery_document = discovery_file.read()
    except IOError as err:
      raise gcutil_errors.CommandError(
          'Could not load discovery document from %s:\n%s' % (
              discovery_path, err))

  try:
    discovery_document = json.loads(discovery_document)
  except ValueError:
    raise errors.InvalidJsonError()

  api = discovery.build_from_document(
      discovery_document,
      http=http,
      model=model.JsonModel())
  api = WrapApiIfNeeded(api)

  return ComputeApi(
      api,
      version.get(discovery_document.get('version')),
      GetSetOfApiMethods(discovery_document))