def test_token(self): calls = [] def mocked(**kwargs): calls.append(kwargs) return 'fake-token', utils.time_time() + 3600 self.mock(auth, 'get_access_token', mocked) tok = service_accounts.AccessToken('fake-token', utils.time_time() + 3600) self.assertEqual( ('*****@*****.**', tok), service_accounts.get_system_account_token('*****@*****.**', ['scope'])) self.assertEqual([{ 'act_as': '*****@*****.**', 'min_lifetime_sec': service_accounts.MIN_TOKEN_LIFETIME_SEC, 'scopes': ['scope'], }], calls)
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})
def test_bot(self): self.assertEqual( ('bot', None), service_accounts.get_system_account_token('bot', ['scope']))
def test_none(self): self.assertEqual( ('none', None), service_accounts.get_system_account_token(None, ['scope']))