コード例 #1
0
 def test_task_account_is_none(self):
     task_id = self.make_task_request(service_account='none',
                                      service_account_token=None)
     account, tok = service_accounts.get_task_account_token(
         task_id, 'bot-id', ['scope1', 'scope2'])
     self.assertEqual('none', account)
     self.assertIsNone(tok)
コード例 #2
0
    def test_ok_with_realm(self):
        now = datetime.datetime(2010, 1, 2, 3, 4, 5)
        self.mock_now(now)
        self.mock(auth, 'has_permission', lambda *_args, **_kwargs: True)

        # Initial attempt.
        task_id = self.make_task_request(
            service_account='*****@*****.**', realm='test:realm')

        expiry = now + datetime.timedelta(seconds=3600)
        self.mock_json_request(
            expected_url='https://tokens.example.com/prpc/'
            'tokenserver.minter.TokenMinter/MintServiceAccountToken',
            expected_payload={
                'tokenKind':
                1,
                'serviceAccount':
                '*****@*****.**',
                'realm':
                'test:realm',
                'oauthScope': ['scope1', 'scope2'],
                'minValidityDuration':
                300,
                'auditTags': [
                    'swarming:gae_request_id:7357B3D7091D',
                    'swarming:service_version:sample-app/v1a',
                    'swarming:bot_id:bot-id',
                    'swarming:task_id:' + task_id,
                    'swarming:task_name:Request with [email protected]',
                ],
            },
            expected_project_id='test',
            response={
                'token': 'totally_real_token',
                'serviceVersion': 'token-server-id/ver',
                'expiry': expiry.isoformat() + 'Z',
            })

        tok = service_accounts.AccessToken('totally_real_token',
                                           int(utils.time_time() + 3600))
        self.assertEqual(
            ('*****@*****.**', tok),
            service_accounts.get_task_account_token(task_id, 'bot-id',
                                                    ['scope1', 'scope2']))
コード例 #3
0
    def test_happy_path(self):
        now = datetime.datetime(2010, 1, 2, 3, 4, 5)
        self.mock_now(now)

        # Initial attempt and a retry.
        for try_number in (1, 2):
            task_id = self.make_task_request(
                service_account='*****@*****.**',
                service_account_token='mocked-oauth-token-grant',
                try_number=try_number)

            expiry = now + datetime.timedelta(seconds=3600)
            self.mock_json_request(
                expected_url='https://tokens.example.com/prpc/'
                'tokenserver.minter.TokenMinter/MintOAuthTokenViaGrant',
                expected_payload={
                    'grantToken':
                    'mocked-oauth-token-grant',
                    'oauthScope': ['scope1', 'scope2'],
                    'minValidityDuration':
                    300,
                    'auditTags': [
                        'swarming:gae_request_id:7357B3D7091D',
                        'swarming:service_version:sample-app/v1a',
                        'swarming:bot_id:bot-id',
                        'swarming:task_id:' + task_id,
                        'swarming:task_name:Request with [email protected]',
                    ],
                },
                response={
                    'accessToken': 'totally_real_token',
                    'serviceVersion': 'token-server-id/ver',
                    'expiry': expiry.isoformat() + 'Z',
                })

            tok = service_accounts.AccessToken('totally_real_token',
                                               int(utils.time_time() + 3600))
            self.assertEqual(('*****@*****.**', tok),
                             service_accounts.get_task_account_token(
                                 task_id, 'bot-id', ['scope1', 'scope2']))
コード例 #4
0
ファイル: handlers_bot.py プロジェクト: rmoorman/luci-py
    def post(self):
        request = self.parse_body()
        logging.debug('Request body: %s', request)
        msg = log_unexpected_subset_keys(self.ACCEPTED_KEYS,
                                         self.REQUIRED_KEYS, request,
                                         self.request, 'bot', 'keys')
        if msg:
            self.abort_with_error(400, error=msg)

        account_id = request['account_id']
        bot_id = request['id']
        scopes = request['scopes']
        task_id = request.get('task_id')

        # Scopes should be a list of strings, always.
        if (not scopes or not isinstance(scopes, list)
                or not all(isinstance(s, basestring) for s in scopes)):
            self.abort_with_error(400,
                                  error='"scopes" must be a list of strings')

        # Only two flavors of accounts are supported.
        if account_id not in ('system', 'task'):
            self.abort_with_error(
                400,
                error='Unknown "account_id", expecting "task" or "system"')

        # If using 'task' account, task_id is required. We'll double check the bot
        # still executes this task (based on data in datastore), and if so, will
        # use a service account associated with this particular task.
        if account_id == 'task' and not task_id:
            self.abort_with_error(
                400,
                error='"task_id" is required when using "account_id" == "task"'
            )

        # Need machine type associated with the bot for bots.cfg query below.
        # BotInfo also contains ID of a task the bot currently executes (to compare
        # with 'task_id' request parameter).
        machine_type = None
        current_task_id = None
        bot_info = bot_management.get_info_key(bot_id).get()
        if bot_info:
            machine_type = bot_info.machine_type
            current_task_id = bot_info.task_id

        # Make sure bot self-reported ID matches the authentication token. Raises
        # auth.AuthorizationError if not. Also fetches corresponding BotGroupConfig
        # that contains system service account email for this bot.
        bot_group_cfg = bot_auth.validate_bot_id_and_fetch_config(
            bot_id, machine_type)

        # At this point, the request is valid structurally, and the bot used proper
        # authentication when making it.
        logging.info('Requesting a "%s" token with scopes %s', account_id,
                     scopes)

        # This is mostly a precaution against confused bot processes. We can always
        # just use 'current_task_id' to look up per-task service account. Datastore
        # is the source of truth here, not whatever bot reports.
        if account_id == 'task' and task_id != current_task_id:
            logging.error(
                'Bot %s requested "task" access token for task %s, but runs %s',
                bot_id, task_id, current_task_id)
            self.abort_with_error(
                400, error='Wrong task_id: the bot is not executing this task')

        account = None  # an email or 'bot' or 'none'
        token = None  # service_accounts.AccessToken
        try:
            if account_id == 'task':
                account, token = service_accounts.get_task_account_token(
                    task_id, bot_id, scopes)
            elif account_id == 'system':
                account, token = service_accounts.get_system_account_token(
                    bot_group_cfg.system_service_account, scopes)
            else:
                raise AssertionError('Impossible, there is a check above')
        except auth.AccessTokenError as exc:
            # Note: no need to log this, it is already logged at the source. Also
            # we cautiously do not return any error details to the bot, just in case
            # they may contain something we don't want to disclose.
            if exc.transient:
                self.abort_with_error(
                    500, error='Transient error when generating the token')
            self.abort_with_error(
                403,
                error='Fatal error when generating the token, see server logs')

        # Note: the token info is already logged by service_accounts.get_*_token.
        if token:
            self.send_response({
                'service_account': account,
                'access_token': token.access_token,
                'expiry': token.expiry,
            })
        else:
            assert account in ('bot', 'none'), account
            self.send_response({'service_account': account})
コード例 #5
0
 def test_missing_task_id(self):
   with self.assertRaises(auth.AccessTokenError):
     service_accounts.get_task_account_token(
         '382b353612985111', 'bot-id', ['scope1', 'scope2'])
コード例 #6
0
 def test_malformed_task_id(self):
   with self.assertRaises(auth.AccessTokenError):
     service_accounts.get_task_account_token(
         'bad-task-id', 'bot-id', ['scope1', 'scope2'])
コード例 #7
0
 def test_missing_task_id(self):
     with self.assertRaises(service_accounts.MisconfigurationError):
         service_accounts.get_task_account_token('382b353612985111',
                                                 'bot-id',
                                                 ['scope1', 'scope2'])
コード例 #8
0
 def test_malformed_task_id(self):
     with self.assertRaises(service_accounts.MisconfigurationError):
         service_accounts.get_task_account_token('bad-task-id', 'bot-id',
                                                 ['scope1', 'scope2'])