Merge "Add token validation to GET command endpoints"
This commit is contained in:
@@ -84,6 +84,14 @@ def format_exception(value):
|
|||||||
|
|
||||||
class Application(object):
|
class Application(object):
|
||||||
|
|
||||||
|
def require_agent_token(func):
|
||||||
|
def wrapper(self, request, *args, **kwargs):
|
||||||
|
token = request.args.get('agent_token', None)
|
||||||
|
if not self.agent.validate_agent_token(token):
|
||||||
|
raise http_exc.Unauthorized('Token invalid.')
|
||||||
|
return func(self, request, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
def __init__(self, agent, conf):
|
def __init__(self, agent, conf):
|
||||||
"""Set up the API app.
|
"""Set up the API app.
|
||||||
|
|
||||||
@@ -199,11 +207,13 @@ class Application(object):
|
|||||||
status = self.agent.get_status()
|
status = self.agent.get_status()
|
||||||
return jsonify(status)
|
return jsonify(status)
|
||||||
|
|
||||||
|
@require_agent_token
|
||||||
def api_list_commands(self, request):
|
def api_list_commands(self, request):
|
||||||
with metrics_utils.get_metrics_logger(__name__).timer('list_commands'):
|
with metrics_utils.get_metrics_logger(__name__).timer('list_commands'):
|
||||||
results = self.agent.list_command_results()
|
results = self.agent.list_command_results()
|
||||||
return jsonify({'commands': results})
|
return jsonify({'commands': results})
|
||||||
|
|
||||||
|
@require_agent_token
|
||||||
def api_get_command(self, request, cmd):
|
def api_get_command(self, request, cmd):
|
||||||
with metrics_utils.get_metrics_logger(__name__).timer('get_command'):
|
with metrics_utils.get_metrics_logger(__name__).timer('get_command'):
|
||||||
result = self.agent.get_command_result(cmd)
|
result = self.agent.get_command_result(cmd)
|
||||||
@@ -214,16 +224,13 @@ class Application(object):
|
|||||||
|
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|
||||||
|
@require_agent_token
|
||||||
def api_run_command(self, request):
|
def api_run_command(self, request):
|
||||||
body = request.get_json(force=True)
|
body = request.get_json(force=True)
|
||||||
if ('name' not in body or 'params' not in body
|
if ('name' not in body or 'params' not in body
|
||||||
or not isinstance(body['params'], dict)):
|
or not isinstance(body['params'], dict)):
|
||||||
raise http_exc.BadRequest('Missing or invalid name or params')
|
raise http_exc.BadRequest('Missing or invalid name or params')
|
||||||
|
|
||||||
token = request.args.get('agent_token', None)
|
|
||||||
if not self.agent.validate_agent_token(token):
|
|
||||||
raise http_exc.Unauthorized(
|
|
||||||
'Token invalid.')
|
|
||||||
with metrics_utils.get_metrics_logger(__name__).timer('run_command'):
|
with metrics_utils.get_metrics_logger(__name__).timer('run_command'):
|
||||||
result = self.agent.execute_command(body['name'], **body['params'])
|
result = self.agent.execute_command(body['name'], **body['params'])
|
||||||
wait = request.args.get('wait')
|
wait = request.args.get('wait')
|
||||||
|
|||||||
@@ -274,6 +274,61 @@ class TestIronicAPI(ironic_agent_base.IronicAgentTest):
|
|||||||
data = response.json
|
data = response.json
|
||||||
self.assertEqual(serialized_cmd_result, data)
|
self.assertEqual(serialized_cmd_result, data)
|
||||||
|
|
||||||
|
def test_list_commands_with_token(self):
|
||||||
|
agent_token = str('0123456789' * 10)
|
||||||
|
cmd_result = base.SyncCommandResult('do_things',
|
||||||
|
{'key': 'value'},
|
||||||
|
True,
|
||||||
|
{'test': 'result'})
|
||||||
|
self.mock_agent.list_command_results.return_value = [cmd_result]
|
||||||
|
self.mock_agent.validate_agent_token.return_value = True
|
||||||
|
|
||||||
|
response = self.get_json('/commands?agent_token=%s' % agent_token)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(1, self.mock_agent.validate_agent_token.call_count)
|
||||||
|
self.assertEqual(1, self.mock_agent.list_command_results.call_count)
|
||||||
|
|
||||||
|
def test_get_command_with_token(self):
|
||||||
|
agent_token = str('0123456789' * 10)
|
||||||
|
cmd_result = base.SyncCommandResult('do_things',
|
||||||
|
{'key': 'value'},
|
||||||
|
True,
|
||||||
|
{'test': 'result'})
|
||||||
|
self.mock_agent.get_command_result.return_value = cmd_result
|
||||||
|
self.mock_agent.validate_agent_token.return_value = True
|
||||||
|
|
||||||
|
response = self.get_json(
|
||||||
|
'/commands/abc123?agent_token=%s' % agent_token)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(cmd_result.serialize(), response.json)
|
||||||
|
self.assertEqual(1, self.mock_agent.validate_agent_token.call_count)
|
||||||
|
self.assertEqual(1, self.mock_agent.get_command_result.call_count)
|
||||||
|
|
||||||
|
def test_list_commands_with_token_invalid(self):
|
||||||
|
agent_token = str('0123456789' * 10)
|
||||||
|
self.mock_agent.validate_agent_token.return_value = False
|
||||||
|
|
||||||
|
response = self.get_json('/commands?agent_token=%s' % agent_token,
|
||||||
|
expect_errors=True)
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(1, self.mock_agent.validate_agent_token.call_count)
|
||||||
|
self.assertEqual(0, self.mock_agent.list_command_results.call_count)
|
||||||
|
|
||||||
|
def test_get_command_with_token_invalid(self):
|
||||||
|
agent_token = str('0123456789' * 10)
|
||||||
|
self.mock_agent.validate_agent_token.return_value = False
|
||||||
|
|
||||||
|
response = self.get_json(
|
||||||
|
'/commands/abc123?agent_token=%s' % agent_token,
|
||||||
|
expect_errors=True)
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(1, self.mock_agent.validate_agent_token.call_count)
|
||||||
|
self.assertEqual(0, self.mock_agent.get_command_result.call_count)
|
||||||
|
|
||||||
def test_execute_agent_command_with_token(self):
|
def test_execute_agent_command_with_token(self):
|
||||||
agent_token = str('0123456789' * 10)
|
agent_token = str('0123456789' * 10)
|
||||||
command = {
|
command = {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
In rescued state, prevent IPA from restarting to avoid exposing its API
|
||||||
|
on tenant networks.
|
||||||
Reference in New Issue
Block a user