Skip to content

Commit

Permalink
Add 'mint_oauth_token' method to RPC client.
Browse files Browse the repository at this point in the history
R=smut@google.com, dnj@chromium.org
BUG=730878

Review-Url: https://codereview.chromium.org/2958413002
  • Loading branch information
vadimsht authored and Commit Bot committed Jun 29, 2017
1 parent 71c3c10 commit 16835e9
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 1 deletion.
39 changes: 39 additions & 0 deletions appengine/swarming/swarming_bot/bot_code/remote_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from remote_client_errors import BotCodeError
from remote_client_errors import InitializationError
from remote_client_errors import InternalError
from remote_client_errors import MintOAuthTokenError
from remote_client_errors import PollError


Expand Down Expand Up @@ -281,3 +282,41 @@ def ping(self):
resp = net.url_read(self._server + '/swarming/api/v1/bot/server_ping')
if resp is None:
logging.error('No response from server_ping')

def mint_oauth_token(self, task_id, bot_id, account_id, scopes):
"""Asks the server to generate an access token for a service account.
Each task has two service accounts associated with it: 'system' and 'task'.
Swarming server is capable of generating oauth tokens for them (if the bot
is currently authorized to have access to them).
Args:
task_id: identifier of currently executing task.
bot_id: name of the bot.
account_id: logical identifier of the account (e.g 'system' or 'task').
scopes: list of OAuth scopes the new token should have.
Returns:
{
'service_account': <str>, # account email or 'bot', or 'none'
'access_token': <str> or None, # actual token, if using real account
'expiry': <int>, # unix timestamp in seconds
}
Raises:
InternalError if can't contact the server after many attempts or the
server consistently replies with HTTP 5** errors.
MintOAuthTokenError on fatal errors.
"""
resp = self._url_read_json('/swarming/api/v1/bot/oauth_token', data={
'account_id': account_id,
'id': bot_id,
'scopes': scopes,
'task_id': task_id,
})
if not resp:
raise InternalError('Error when minting the token')
if resp.get('error'):
raise MintOAuthTokenError(resp['error'])
return resp
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ class InternalError(Exception):


class PollError(Exception):
"""Raised on unrecoverable errors when in RemoteClient.poll."""
"""Raised on unrecoverable errors in RemoteClient.poll."""


class MintOAuthTokenError(Exception):
"""Raised on unrecoverable errors in RemoteClient.mint_oauth_token."""
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import google.protobuf.json_format
from proto_bot import swarming_bot_pb2
from remote_client_errors import InternalError
from remote_client_errors import MintOAuthTokenError
from remote_client_errors import PollError
from utils import net

Expand Down Expand Up @@ -195,6 +196,11 @@ def get_bot_code(self, new_zip_fn, bot_version, _bot_id):
def ping(self):
pass

def mint_oauth_token(self, task_id, bot_id, account_id, scopes):
# pylint: disable=unused-argument
raise MintOAuthTokenError(
'mint_oauth_token is not supported in grpc protocol')


def create_state_proto(state_dict, message):
""" Constructs a State message out of a state dict.
Expand Down
38 changes: 38 additions & 0 deletions appengine/swarming/swarming_bot/bot_code/remote_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,44 @@ def test_get_authentication_headers(self):
self.mock(time, 'time', lambda: 103500)
self.assertEqual({'Now': '103500'}, c.get_authentication_headers())

def test_mint_oauth_token_ok(self):
fake_resp = {
'service_account': 'blah@example.com',
'access_token': 'abc',
'expiry': 12345,
}

c = remote_client.RemoteClientNative('http://localhost:1', None)
def mocked_call(url_path, data):
self.assertEqual('/swarming/api/v1/bot/oauth_token', url_path)
self.assertEqual({
'account_id': 'account_id',
'id': 'bot_id',
'scopes': ['a', 'b'],
'task_id': 'task_id',
}, data)
return fake_resp
self.mock(c, '_url_read_json', mocked_call)

resp = c.mint_oauth_token('task_id', 'bot_id', 'account_id', ['a', 'b'])
self.assertEqual(fake_resp, resp)

def test_mint_oauth_token_transient_err(self):
c = remote_client.RemoteClientNative('http://localhost:1', None)
def mocked_call(*_args, **_kwargs):
return None # that's how net.url_read_json indicates HTTP 500 :-/
self.mock(c, '_url_read_json', mocked_call)
with self.assertRaises(remote_client.InternalError):
c.mint_oauth_token('task_id', 'bot_id', 'account_id', ['a', 'b'])

def test_mint_oauth_token_fatal_err(self):
c = remote_client.RemoteClientNative('http://localhost:1', None)
def mocked_call(*_args, **_kwargs):
return {'error': 'blah'}
self.mock(c, '_url_read_json', mocked_call)
with self.assertRaises(remote_client.MintOAuthTokenError):
c.mint_oauth_token('task_id', 'bot_id', 'account_id', ['a', 'b'])


if __name__ == '__main__':
logging.basicConfig(
Expand Down

0 comments on commit 16835e9

Please sign in to comment.