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(''))
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))
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)
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)))
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)