def test_is_service_account(self):
   self.assertTrue(
       service_accounts.is_service_account('*****@*****.**'))
   self.assertFalse(
     service_accounts.is_service_account('bot:something'))
   self.assertFalse(
     service_accounts.is_service_account('user:something@something'))
   self.assertFalse(service_accounts.is_service_account(''))
Example #2
0
def check_schedule_request_acl(request):
  """Verifies the current caller can schedule a given task request.

  Arguments:
  - request: TaskRequest entity with information about the new task.

  Raises:
    auth.AuthorizationError if the caller is not allowed to schedule this task.
  """
  # Only terminate tasks don't have a pool. ACLs for them are handled through
  # 'acl.can_edit_bot', see 'terminate' RPC handler. Such tasks do not end up
  # hitting this function, and so we can assume there's a pool set (this is
  # checked in TaskProperties's pre put hook).
  #
  # It is guaranteed that at most one item can be specified in 'pool' and that
  # every TaskSlice property dimensions use the same pool and id dimensions.
  pool = request.task_slice(0).properties.dimensions[u'pool'][0]
  pool_cfg = pools_config.get_pool_config(pool)

  # request.service_account can be 'bot' or 'none'. We don't care about these,
  # they are always allowed. We care when the service account is a real email.
  has_service_account = service_accounts.is_service_account(
      request.service_account)

  if not pool_cfg:
    logging.info('Pool "%s" is not in pools.cfg', pool)
    # Deployments that use pools.cfg may forbid unknown pools completely.
    # This is a safeguard against anyone executing tasks in a new not fully
    # configured pool.
    if pools_config.forbid_unknown_pools():
      raise auth.AuthorizationError(
          'Can\'t submit tasks to pool "%s" not defined in pools.cfg' % pool)
    # To be backward compatible with Swarming deployments that do not care about
    # pools isolation pools without pools.cfg entry are relatively wide open
    # (anyone with 'can_create_task' permission can use them). As a downside,
    # they are not allowed to use service accounts, since Swarming doesn't know
    # what accounts are allowed in such unknown pools.
    if has_service_account:
      raise auth.AuthorizationError(
          'Can\'t use account "%s" in the pool "%s" not defined in pools.cfg' %
          (request.service_account, pool))
    return

  logging.info(
      'Looking at the pool "%s" in pools.cfg, rev "%s"', pool, pool_cfg.rev)

  # Verify the caller can use the pool at all.
  if not _is_allowed_to_schedule(pool_cfg):
    raise auth.AuthorizationError(
        'User "%s" is not allowed to schedule tasks in the pool "%s", '
        'see pools.cfg' % (auth.get_current_identity().to_bytes(), pool))

  # Verify the requested task service account is allowed in this pool.
  if (has_service_account and
      not _is_allowed_service_account(request.service_account, pool_cfg)):
    raise auth.AuthorizationError(
        'Service account "%s" is not allowed in the pool "%s", see pools.cfg' %
        (request.service_account, pool))
Example #3
0
    def new(self, request):
        """Creates a new task.

    The task will be enqueued in the tasks list and will be executed at the
    earliest opportunity by a bot that has at least the dimensions as described
    in the task request.
    """
        sb = (request.properties.secret_bytes
              if request.properties is not None else None)
        if sb is not None:
            request.properties.secret_bytes = "HIDDEN"
        logging.debug('%s', request)
        if sb is not None:
            request.properties.secret_bytes = sb

        try:
            request_obj, secret_bytes = message_conversion.new_task_request_from_rpc(
                request, utils.utcnow())
            for index in xrange(request_obj.num_task_slices):
                apply_server_property_defaults(
                    request_obj.task_slice(index).properties)
            task_request.init_new_request(
                request_obj, acl.can_schedule_high_priority_tasks())
            # We need to call the ndb.Model pre-put check earlier because the
            # following checks assume that the request itself is valid and could crash
            # otherwise.
            request_obj._pre_put_hook()
        except (datastore_errors.BadValueError, TypeError, ValueError) as e:
            logging.exception(
                'Here\'s what was wrong in the user new task request:')
            raise endpoints.BadRequestException(e.message)

        # Make sure the caller is actually allowed to schedule the task before
        # asking the token server for a service account token.
        task_scheduler.check_schedule_request_acl(request_obj)

        # If request_obj.service_account is an email, contact the token server to
        # generate "OAuth token grant" (or grab a cached one). By doing this we
        # check that the given service account usage is allowed by the token server
        # rules at the time the task is posted. This check is also performed later
        # (when running the task), when we get the actual OAuth access token.
        if service_accounts.is_service_account(request_obj.service_account):
            if not service_accounts.has_token_server():
                raise endpoints.BadRequestException(
                    'This Swarming server doesn\'t support task service accounts '
                    'because Token Server URL is not configured')
            max_lifetime_secs = request_obj.max_lifetime_secs
            try:
                # Note: this raises AuthorizationError if the user is not allowed to use
                # the requested account or service_accounts.InternalError if something
                # unexpected happens.
                duration = datetime.timedelta(seconds=max_lifetime_secs)
                request_obj.service_account_token = (
                    service_accounts.get_oauth_token_grant(
                        service_account=request_obj.service_account,
                        validity_duration=duration))
            except service_accounts.InternalError as exc:
                raise endpoints.InternalServerErrorException(exc.message)

        # If the user only wanted to evaluate scheduling the task, but not actually
        # schedule it, return early without a task_id.
        if request.evaluate_only:
            request_obj._pre_put_hook()
            return swarming_rpcs.TaskRequestMetadata(
                request=message_conversion.task_request_to_rpc(request_obj))

        try:
            result_summary = task_scheduler.schedule_request(
                request_obj, secret_bytes)
        except (datastore_errors.BadValueError, TypeError, ValueError) as e:
            raise endpoints.BadRequestException(e.message)

        previous_result = None
        if result_summary.deduped_from:
            previous_result = message_conversion.task_result_to_rpc(
                result_summary, False)

        return swarming_rpcs.TaskRequestMetadata(
            request=message_conversion.task_request_to_rpc(request_obj),
            task_id=task_pack.pack_result_summary_key(result_summary.key),
            task_result=previous_result)
Example #4
0
def _validate_pools_cfg(cfg, ctx):
  """Validates pools.cfg file."""

  template_map = _resolve_task_template_inclusions(
      ctx, cfg.task_template)
  deployment_map = _resolve_task_template_deployments(
      ctx, template_map, cfg.task_template_deployment)
  bot_monitorings = _resolve_bot_monitoring(ctx, cfg.bot_monitoring)
  bot_monitoring_unreferred = set(bot_monitorings)

  # Currently optional
  if cfg.HasField("default_external_services"):
    _validate_external_services(ctx, cfg.default_external_services)

  pools = set()
  for i, msg in enumerate(cfg.pool):
    with ctx.prefix('pool #%d (%s): ', i, '|'.join(msg.name) or 'unnamed'):
      # Validate names.
      if not msg.name:
        ctx.error('at least one pool name must be given')
      for name in msg.name:
        if not local_config.validate_dimension_value(name):
          ctx.error('bad pool name "%s", not a valid dimension value', name)
        elif name in pools:
          ctx.error('pool "%s" was already declared', name)
        else:
          pools.add(name)

      # Validate schedulers.user.
      for u in msg.schedulers.user:
        _validate_ident(ctx, 'user', u)

      # Validate schedulers.group.
      for g in msg.schedulers.group:
        if not auth.is_valid_group_name(g):
          ctx.error('bad group name "%s"', g)

      # Validate schedulers.trusted_delegation.
      seen_peers = set()
      for d in msg.schedulers.trusted_delegation:
        with ctx.prefix('trusted_delegation #%d (%s): ', i, d.peer_id):
          if not d.peer_id:
            ctx.error('"peer_id" is required')
          else:
            peer_id = _validate_ident(ctx, 'peer_id', d.peer_id)
            if peer_id in seen_peers:
              ctx.error('peer "%s" was specified twice', d.peer_id)
            elif peer_id:
              seen_peers.add(peer_id)
          for i, tag in enumerate(d.require_any_of.tag):
            if ':' not in tag:
              ctx.error('bad tag #%d "%s" - must be <key>:<value>', i, tag)

      # Validate service accounts.
      for i, account in enumerate(msg.allowed_service_account):
        if not service_accounts.is_service_account(account):
          ctx.error('bad allowed_service_account #%d "%s"', i, account)

      # Validate service account groups.
      for i, group in enumerate(msg.allowed_service_account_group):
        if not auth.is_valid_group_name(group):
          ctx.error('bad allowed_service_account_group #%d "%s"', i, group)

      # Validate external schedulers.
      for i, es in enumerate(msg.external_schedulers):
        if not es.address:
          ctx.error('%sth external scheduler config had no address', i)

      _resolve_deployment(ctx, msg, template_map, deployment_map)

      if msg.bot_monitoring:
        if msg.bot_monitoring not in bot_monitorings:
          ctx.error('refer to missing bot_monitoring %r', msg.bot_monitoring)
        else:
          bot_monitoring_unreferred.discard(msg.bot_monitoring)
  if bot_monitoring_unreferred:
    ctx.error(
        'bot_monitoring not referred to: %s',
        ', '.join(sorted(bot_monitoring_unreferred)))
Example #5
0
def _validate_pools_cfg(cfg, ctx):
    """Validates pools.cfg file."""

    template_map = _resolve_task_template_inclusions(ctx, cfg.task_template)
    deployment_map = _resolve_task_template_deployments(
        ctx, template_map, cfg.task_template_deployment)

    pools = set()
    for i, msg in enumerate(cfg.pool):
        with ctx.prefix('pool #%d (%s): ', i, '|'.join(msg.name) or 'unnamed'):
            # Validate names.
            if not msg.name:
                ctx.error('at least one pool name must be given')
            for name in msg.name:
                if not local_config.validate_dimension_value(name):
                    ctx.error(
                        'bad pool name "%s", not a valid dimension value',
                        name)
                elif name in pools:
                    ctx.error('pool "%s" was already declared', name)
                else:
                    pools.add(name)

            # Validate schedulers.user.
            for u in msg.schedulers.user:
                _validate_ident(ctx, 'user', u)

            # Validate schedulers.group.
            for g in msg.schedulers.group:
                if not auth.is_valid_group_name(g):
                    ctx.error('bad group name "%s"', g)

            # Validate schedulers.trusted_delegation.
            seen_peers = set()
            for d in msg.schedulers.trusted_delegation:
                with ctx.prefix('trusted_delegation #%d (%s): ', i, d.peer_id):
                    if not d.peer_id:
                        ctx.error('"peer_id" is required')
                    else:
                        peer_id = _validate_ident(ctx, 'peer_id', d.peer_id)
                        if peer_id in seen_peers:
                            ctx.error('peer "%s" was specified twice',
                                      d.peer_id)
                        elif peer_id:
                            seen_peers.add(peer_id)
                    for i, tag in enumerate(d.require_any_of.tag):
                        if ':' not in tag:
                            ctx.error(
                                'bad tag #%d "%s" - must be <key>:<value>', i,
                                tag)

            # Validate service accounts.
            for i, account in enumerate(msg.allowed_service_account):
                if not service_accounts.is_service_account(account):
                    ctx.error('bad allowed_service_account #%d "%s"', i,
                              account)

            # Validate service account groups.
            for i, group in enumerate(msg.allowed_service_account_group):
                if not auth.is_valid_group_name(group):
                    ctx.error('bad allowed_service_account_group #%d "%s"', i,
                              group)

            _resolve_deployment(ctx, msg, template_map, deployment_map)