def test_check_tasks_act_as_legacy_not_allowed(self):
     self.mock(task_scheduler, '_is_allowed_service_account',
               lambda *_: False)
     task_request_mock = _gen_task_request_mock(realm=None)
     pool_cfg_mock = _gen_pool_config()
     with self.assertRaises(auth.AuthorizationError):
         realms.check_tasks_act_as(task_request_mock, pool_cfg_mock, False)
 def test_check_tasks_act_as_enforced_no_realm(self):
     self._has_permission_mock.return_value = False
     task_request_mock = _gen_task_request_mock(realm=None)
     pool_cfg_mock = _gen_pool_config(enforced_realm_permissions=[
         realms_pb2.REALM_PERMISSION_TASKS_ACT_AS
     ])
     with self.assertRaises(auth.AuthorizationError):
         realms.check_tasks_act_as(task_request_mock, pool_cfg_mock, True)
     self._has_permission_mock.assert_not_called()
 def test_check_tasks_act_as_enforced_not_allowed(self):
     self.mock(realms, 'is_enforced_permission', lambda *_: True)
     self._has_permission_mock.return_value = False
     task_request_mock = _gen_task_request_mock(realm='test:realm')
     pool_cfg_mock = _gen_pool_config()
     with self.assertRaises(auth.AuthorizationError):
         realms.check_tasks_act_as(task_request_mock, pool_cfg_mock, True)
     self._has_permission_mock.assert_called_once_with(
         _PERM_TASKS_ACT_AS, [u'test:realm'],
         identity=_TASK_SERVICE_ACCOUNT_IDENTITY)
 def test_check_tasks_act_as_legacy_allowed_no_realm(self):
     self.mock(task_scheduler, '_is_allowed_service_account',
               lambda *_: True)
     task_request_mock = _gen_task_request_mock(realm=None)
     pool_cfg_mock = _gen_pool_config()
     used_realms = realms.check_tasks_act_as(task_request_mock,
                                             pool_cfg_mock, False)
     self.assertFalse(used_realms)
     self._has_permission_dryrun_mock.assert_not_called()
 def test_check_tasks_act_as_enforced_allowed(self):
     self._has_permission_mock.return_value = True
     task_request_mock = _gen_task_request_mock(realm='test:realm')
     pool_cfg_mock = _gen_pool_config()
     used_realms = realms.check_tasks_act_as(task_request_mock,
                                             pool_cfg_mock, True)
     self.assertTrue(used_realms)
     self._has_permission_mock.assert_called_once_with(
         _PERM_TASKS_ACT_AS, [u'test:realm'],
         identity=_TASK_SERVICE_ACCOUNT_IDENTITY)
 def test_check_tasks_act_as_legacy_allowed_with_realm(self):
     self.mock(task_scheduler, '_is_allowed_service_account',
               lambda *_: True)
     task_request_mock = _gen_task_request_mock(realm='test:realm')
     pool_cfg_mock = _gen_pool_config()
     used_realms = realms.check_tasks_act_as(task_request_mock,
                                             pool_cfg_mock, False)
     self.assertFalse(used_realms)
     self._has_permission_dryrun_mock.assert_called_once_with(
         _PERM_TASKS_ACT_AS, [u'test:realm'],
         True,
         identity=_TASK_SERVICE_ACCOUNT_IDENTITY,
         tracking_bug=realms._TRACKING_BUG)
def get_task_account_token(task_id, bot_id, scopes):
    """Returns an access token for a service account associated with a task.

  Assumes authorization checks have been made already. If the task is not
  configured to use service account returns ('none', None). If the task is
  configured to use whatever bot is using when calling Swarming, returns
  ('bot', None).

  Otherwise returns (<email>, AccessToken with valid token for <email>).

  If the task has realm, it calls MintServiceAccountToken rpc using the realm.
  Otherwise, it calls MintOAuthTokenViaGrant with grant token. The legacy path
  will be deprecated after migrating to Realm-based configurations.

  Args:
    task_id: ID of the task.
    bot_id: ID of the bot that executes the task, for logs.
    scopes: list of requested OAuth scopes.

  Returns:
    (<service account email> or 'bot' or 'none', AccessToken or None).

  Raises:
    PermissionError if the token server forbids the usage.
    MisconfigurationError if the service account is misconfigured.
    InternalError if the RPC fails unexpectedly.
  """
    # Grab corresponding TaskRequest.
    try:
        result_summary_key = task_pack.run_result_key_to_result_summary_key(
            task_pack.unpack_run_result_key(task_id))
        task_request_key = task_pack.result_summary_key_to_request_key(
            result_summary_key)
    except ValueError as exc:
        logging.error('Unexpectedly bad task_id: %s', exc)
        raise MisconfigurationError('Bad task_id: %s' % task_id)

    task_request = task_request_key.get()
    if not task_request:
        raise MisconfigurationError('No such task request: %s' % task_id)

    # 'none' or 'bot' cases are handled by the bot locally, no token for them.
    if task_request.service_account in ('none', 'bot'):
        return task_request.service_account, None

    # The only possible case is a service account email. Double check this.
    if not service_accounts_utils.is_service_account(
            task_request.service_account):
        raise MisconfigurationError('Not a service account email: %s' %
                                    task_request.service_account)

    # Additional information for Token Server's logs.
    audit_tags = [
        'swarming:bot_id:%s' % bot_id,
        'swarming:task_id:%s' % task_id,
        'swarming:task_name:%s' % task_request.name,
    ]

    # task_request.service_account_token can be empty here only when the task has
    # a realm and the service account was authorized via realm ACLs. Use
    # MintServiceAccountToken RPC for such tasks.
    if not task_request.service_account_token:
        assert task_request.realm
        # Re-check if the service account is still allowed to run in the realm,
        # because it may have changed since the last check.
        pool_cfg = pools_config.get_pool_config(task_request.pool)
        realms.check_tasks_act_as(task_request, pool_cfg, enforce=True)
        access_token, expiry = _mint_service_account_token(
            task_request.service_account, task_request.realm, scopes,
            audit_tags)
    else:
        # Use grant token to grab the real OAuth token. Note that the bot caches the
        # resulting OAuth token internally, so we don't bother to cache it here.
        access_token, expiry = _mint_oauth_token_via_grant(
            task_request.service_account_token, scopes, audit_tags)

    # Log and return the token.
    token = AccessToken(access_token,
                        int(utils.datetime_to_timestamp(expiry) / 1e6))
    _check_and_log_token('task associated', task_request.service_account,
                         token)
    return task_request.service_account, token