mirror of
https://github.com/ansible/awx.git
synced 2026-06-30 02:48:03 -02:30
Restore oauth_token backward compatibility for collection token auth (#16500)
* Restore oauth_token backward compatibility for collection token auth
The aap_token rename (c8981e321e) restored module-level token auth but
left two interfaces from earlier collection releases broken:
- The lookup (controller_api) and inventory (controller) plugins
previously declared an oauth_token option. Add oauth_token as an
alias of aap_token in the auth_plugin doc fragment and in
AUTH_ARGSPEC so query(..., oauth_token=...) and inventory YAML keys
keep working.
- tower_cli.cfg-style config files used an oauth_token key under
[general]; it was silently ignored after the rename, quietly
degrading auth. load_config() now also reads the legacy oauth_token
key and maps it to aap_token, with the new aap_token key winning when
both are present. aap_token remains the canonical attribute used by
_parse_aap_token() and the Bearer header logic.
Also make the test helper compatible with ansible-core 2.21+, which
requires a serialization profile alongside _ANSIBLE_ARGS, and extend
the tests to cover the oauth_token alias and legacy config file key.
No changelog fragment added: awx_collection has no changelogs/
directory on devel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Document oauth_token alias in module auth doc fragment
The oauth_token alias was added to aap_token in AUTH_ARGSPEC but not to
the module doc fragment, failing the validate-modules sanity check
(undocumented argument alias).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Generalize version references in compat comments
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ options:
|
|||||||
- If value not set, will try environment variable C(CONTROLLER_OAUTH_TOKEN) and then config files
|
- If value not set, will try environment variable C(CONTROLLER_OAUTH_TOKEN) and then config files
|
||||||
type: raw
|
type: raw
|
||||||
version_added: "3.7.0"
|
version_added: "3.7.0"
|
||||||
aliases: [ controller_oauthtoken, tower_oauthtoken ]
|
aliases: [ oauth_token, controller_oauthtoken, tower_oauthtoken ]
|
||||||
validate_certs:
|
validate_certs:
|
||||||
description:
|
description:
|
||||||
- Whether to allow insecure connections to AWX.
|
- Whether to allow insecure connections to AWX.
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ options:
|
|||||||
why: Collection name change
|
why: Collection name change
|
||||||
alternatives: 'AAP_TOKEN'
|
alternatives: 'AAP_TOKEN'
|
||||||
- name: AAP_TOKEN
|
- name: AAP_TOKEN
|
||||||
aliases: [ controller_oauthtoken, tower_oauthtoken ]
|
aliases: [ oauth_token, controller_oauthtoken, tower_oauthtoken ]
|
||||||
verify_ssl:
|
verify_ssl:
|
||||||
description:
|
description:
|
||||||
- Specify whether Ansible should verify the SSL certificate of the controller host.
|
- Specify whether Ansible should verify the SSL certificate of the controller host.
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class ControllerModule(AnsibleModule):
|
|||||||
aap_token=dict(
|
aap_token=dict(
|
||||||
type='raw',
|
type='raw',
|
||||||
no_log=True,
|
no_log=True,
|
||||||
aliases=['controller_oauthtoken', 'tower_oauthtoken'],
|
aliases=['oauth_token', 'controller_oauthtoken', 'tower_oauthtoken'],
|
||||||
required=False,
|
required=False,
|
||||||
fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN', 'AAP_TOKEN'])
|
fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN', 'AAP_TOKEN'])
|
||||||
),
|
),
|
||||||
@@ -298,7 +298,8 @@ class ControllerModule(AnsibleModule):
|
|||||||
|
|
||||||
# If we made it here then we have values from reading the ini file, so let's pull them out into a dict
|
# If we made it here then we have values from reading the ini file, so let's pull them out into a dict
|
||||||
config_data = {}
|
config_data = {}
|
||||||
for honorred_setting in self.short_params:
|
# 'oauth_token' is the legacy (pre-aap_token) config file key, kept for backward compatibility
|
||||||
|
for honorred_setting in list(self.short_params) + ['oauth_token']:
|
||||||
try:
|
try:
|
||||||
config_data[honorred_setting] = config.get('general', honorred_setting)
|
config_data[honorred_setting] = config.get('general', honorred_setting)
|
||||||
except NoOptionError:
|
except NoOptionError:
|
||||||
@@ -310,6 +311,12 @@ class ControllerModule(AnsibleModule):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise_from(ConfigFileException("An unknown exception occured trying to load config file: {0}".format(e)), e)
|
raise_from(ConfigFileException("An unknown exception occured trying to load config file: {0}".format(e)), e)
|
||||||
|
|
||||||
|
# Backward compatibility: config files written for older collection
|
||||||
|
# releases used the oauth_token key; map it to aap_token.
|
||||||
|
# If both keys are present, the new aap_token key wins.
|
||||||
|
if 'oauth_token' in config_data and 'aap_token' not in config_data:
|
||||||
|
config_data['aap_token'] = config_data['oauth_token']
|
||||||
|
|
||||||
# If we made it here, we have a dict which has values in it from our config, any final settings logic can be performed here
|
# If we made it here, we have a dict which has values in it from our config, any final settings logic can be performed here
|
||||||
for honorred_setting in self.short_params:
|
for honorred_setting in self.short_params:
|
||||||
if honorred_setting in config_data:
|
if honorred_setting in config_data:
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ def make_module(collection_import, module_args, **kwargs):
|
|||||||
# patch the cached args directly: AnsibleModule caches sys.argv parsing in
|
# patch the cached args directly: AnsibleModule caches sys.argv parsing in
|
||||||
# basic._ANSIBLE_ARGS, so patching sys.argv would leak args between tests
|
# basic._ANSIBLE_ARGS, so patching sys.argv would leak args between tests
|
||||||
with mock.patch.object(basic, '_ANSIBLE_ARGS', to_bytes(json.dumps(cli_data))):
|
with mock.patch.object(basic, '_ANSIBLE_ARGS', to_bytes(json.dumps(cli_data))):
|
||||||
|
# ansible-core 2.21+ also requires a serialization profile alongside the args
|
||||||
|
if hasattr(basic, '_ANSIBLE_PROFILE'):
|
||||||
|
with mock.patch.object(basic, '_ANSIBLE_PROFILE', 'legacy'):
|
||||||
|
return ControllerAPIModule(argument_spec=dict(), **kwargs)
|
||||||
return ControllerAPIModule(argument_spec=dict(), **kwargs)
|
return ControllerAPIModule(argument_spec=dict(), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -70,12 +74,43 @@ def test_aap_token_sends_bearer_header(collection_import, token_value):
|
|||||||
assert module.authenticated is False
|
assert module.authenticated is False
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('param', ['controller_oauthtoken', 'tower_oauthtoken'])
|
@pytest.mark.parametrize('param', ['oauth_token', 'controller_oauthtoken', 'tower_oauthtoken'])
|
||||||
def test_aap_token_legacy_aliases(collection_import, param):
|
def test_aap_token_legacy_aliases(collection_import, param):
|
||||||
module = make_module(collection_import, {param: 'legacy-token'})
|
module = make_module(collection_import, {param: 'legacy-token'})
|
||||||
assert module.aap_token == 'legacy-token'
|
assert module.aap_token == 'legacy-token'
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_oauth_token_option_maps_to_aap_token(collection_import):
|
||||||
|
# Older lookup/inventory plugin releases pass options through as direct
|
||||||
|
# params keyed by the plugin option name; oauth_token must resolve to
|
||||||
|
# aap_token via the argspec alias.
|
||||||
|
module = make_module(collection_import, {'oauth_token': 'plugin-token'})
|
||||||
|
assert module.aap_token == 'plugin-token'
|
||||||
|
|
||||||
|
opener, calls = make_recorder()
|
||||||
|
with mock.patch('ansible.module_utils.urls.Request.open', new=opener):
|
||||||
|
module.get_endpoint('ping')
|
||||||
|
|
||||||
|
assert calls[0]['headers']['Authorization'] == 'Bearer plugin-token'
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_file_legacy_oauth_token_key(collection_import, tmp_path):
|
||||||
|
# tower_cli.cfg-style config files from older releases used the oauth_token key
|
||||||
|
config_file = tmp_path / 'tower_cli.cfg'
|
||||||
|
config_file.write_text('[general]\nhost = https://127.0.0.1\noauth_token = ini-legacy-token\n')
|
||||||
|
|
||||||
|
module = make_module(collection_import, {'controller_config_file': str(config_file)})
|
||||||
|
assert module.aap_token == 'ini-legacy-token'
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_file_aap_token_wins_over_legacy_key(collection_import, tmp_path):
|
||||||
|
config_file = tmp_path / 'tower_cli.cfg'
|
||||||
|
config_file.write_text('[general]\nhost = https://127.0.0.1\noauth_token = ini-legacy-token\naap_token = ini-new-token\n')
|
||||||
|
|
||||||
|
module = make_module(collection_import, {'controller_config_file': str(config_file)})
|
||||||
|
assert module.aap_token == 'ini-new-token'
|
||||||
|
|
||||||
|
|
||||||
def test_aap_token_dict_without_token_entry_fails(collection_import):
|
def test_aap_token_dict_without_token_entry_fails(collection_import):
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user