Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FTD HttpApi Plugin: Fix refresh token issue #45598

Merged
merged 1 commit into from
Sep 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 5 additions & 15 deletions lib/ansible/plugins/httpapi/ftd.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def refresh_token_payload(refresh_token):
try:
self.refresh_token = response['refresh_token']
self.access_token = response['access_token']
self.connection._auth = {'Authorization': 'Bearer %s' % self.access_token}
except KeyError:
raise ConnectionError(
'Server returned response without token info during connection authentication: %s' % response)
Expand All @@ -121,7 +122,7 @@ def logout(self):
}
self.connection.send(
self._get_api_token_path(), json.dumps(auth_payload), method=HTTPMethod.POST,
headers=self._authorized_headers()
headers=BASE_HEADERS
)
self.refresh_token = None
self.access_token = None
Expand All @@ -134,10 +135,7 @@ def send_request(self, url_path, http_method, body_params=None, path_params=None
url = construct_url_path(url_path, path_params, query_params)
data = json.dumps(body_params) if body_params else None
try:
response, response_data = self.connection.send(
url, data, method=http_method,
headers=self._authorized_headers()
)
response, response_data = self.connection.send(url, data, method=http_method, headers=BASE_HEADERS)
return {
ResponseParams.SUCCESS: True,
ResponseParams.STATUS_CODE: response.getcode(),
Expand All @@ -159,7 +157,7 @@ def upload_file(self, from_path, to_url):
rf.make_multipart()
body, content_type = encode_multipart_formdata([rf])

headers = self._authorized_headers()
headers = dict(BASE_HEADERS)
headers['Content-Type'] = content_type
headers['Content-Length'] = len(body)

Expand All @@ -168,10 +166,7 @@ def upload_file(self, from_path, to_url):

def download_file(self, from_url, to_path, path_params=None):
url = construct_url_path(from_url, path_params=path_params)
response, response_data = self.connection.send(
url, data=None, method=HTTPMethod.GET,
headers=self._authorized_headers()
)
response, response_data = self.connection.send(url, data=None, method=HTTPMethod.GET, headers=BASE_HEADERS)

if os.path.isdir(to_path):
filename = extract_filename_from_headers(response.info())
Expand All @@ -188,11 +183,6 @@ def handle_httperror(self, exc):
# None means that the exception will be passed further to the caller
return None

def _authorized_headers(self):
headers = dict(BASE_HEADERS)
headers['Authorization'] = 'Bearer %s' % self.access_token
return headers

def _get_api_spec_path(self):
return self.get_option('spec_path')

Expand Down
23 changes: 12 additions & 11 deletions test/units/plugins/httpapi/test_ftd.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
else:
BUILTINS_NAME = '__builtin__'

EXPECTED_BASE_HEADERS = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}


class FakeFtdHttpApiPlugin(HttpApi):
def __init__(self, conn):
Expand Down Expand Up @@ -67,6 +72,7 @@ def test_login_should_request_tokens_when_no_refresh_token(self):

assert 'ACCESS_TOKEN' == self.ftd_plugin.access_token
assert 'REFRESH_TOKEN' == self.ftd_plugin.refresh_token
assert {'Authorization': 'Bearer ACCESS_TOKEN'} == self.ftd_plugin.connection._auth
expected_body = json.dumps({'grant_type': 'password', 'username': 'foo', 'password': 'bar'})
self.connection_mock.send.assert_called_once_with(mock.ANY, expected_body, headers=mock.ANY, method=mock.ANY)

Expand All @@ -80,6 +86,7 @@ def test_login_should_update_tokens_when_refresh_token_exists(self):

assert 'NEW_ACCESS_TOKEN' == self.ftd_plugin.access_token
assert 'NEW_REFRESH_TOKEN' == self.ftd_plugin.refresh_token
assert {'Authorization': 'Bearer NEW_ACCESS_TOKEN'} == self.ftd_plugin.connection._auth
expected_body = json.dumps({'grant_type': 'refresh_token', 'refresh_token': 'REFRESH_TOKEN'})
self.connection_mock.send.assert_called_once_with(mock.ANY, expected_body, headers=mock.ANY, method=mock.ANY)

Expand All @@ -92,7 +99,8 @@ def test_login_should_use_host_variable_when_set(self):

self.ftd_plugin.login('foo', 'bar')

self.connection_mock.send.assert_called_once_with('/testFakeLoginUrl', mock.ANY, headers=mock.ANY, method=mock.ANY)
self.connection_mock.send.assert_called_once_with('/testFakeLoginUrl', mock.ANY, headers=mock.ANY,
method=mock.ANY)
self.ftd_plugin.hostvars['token_path'] = temp_token_path

def test_login_raises_exception_when_no_refresh_token_and_no_credentials(self):
Expand Down Expand Up @@ -135,7 +143,7 @@ def test_send_request_should_send_correct_request(self):
assert {ResponseParams.SUCCESS: True, ResponseParams.STATUS_CODE: 200,
ResponseParams.RESPONSE: exp_resp} == resp
self.connection_mock.send.assert_called_once_with('/test/123?at=0', '{"name": "foo"}', method=HTTPMethod.PUT,
headers=self._expected_headers())
headers=EXPECTED_BASE_HEADERS)

def test_send_request_should_return_empty_dict_when_no_response_data(self):
self.connection_mock.send.return_value = self._connection_response(None)
Expand All @@ -144,7 +152,7 @@ def test_send_request_should_return_empty_dict_when_no_response_data(self):

assert {ResponseParams.SUCCESS: True, ResponseParams.STATUS_CODE: 200, ResponseParams.RESPONSE: {}} == resp
self.connection_mock.send.assert_called_once_with('/test', None, method=HTTPMethod.GET,
headers=self._expected_headers())
headers=EXPECTED_BASE_HEADERS)

def test_send_request_should_return_error_info_when_http_error_raises(self):
self.connection_mock.send.side_effect = HTTPError('http://testhost.com', 500, '', {},
Expand Down Expand Up @@ -215,7 +223,7 @@ def test_upload_file(self):
resp = self.ftd_plugin.upload_file('/tmp/test.txt', '/files')

assert {'id': '123'} == resp
exp_headers = self._expected_headers()
exp_headers = dict(EXPECTED_BASE_HEADERS)
exp_headers['Content-Length'] = len('--Encoded data--')
exp_headers['Content-Type'] = 'multipart/form-data'
self.connection_mock.send.assert_called_once_with('/files', data='--Encoded data--',
Expand Down Expand Up @@ -262,10 +270,3 @@ def _connection_response(response, status=200):
response_text = json.dumps(response) if type(response) is dict else response
response_data = BytesIO(response_text.encode() if response_text else ''.encode())
return response_mock, response_data

def _expected_headers(self):
return {
'Accept': 'application/json',
'Authorization': 'Bearer %s' % self.ftd_plugin.access_token,
'Content-Type': 'application/json'
}