Enable ansible-galaxy to specify client id override with Keycloak Token (#75601)

* Enable ansible-galaxy to specify client id override with Keycloak Token

* Specify ability to provide override of client_id

* Test client_id can be configured for individual servers

* Add issue link to changelog

* Document client_id as a config option and add an example

Co-authored-by: s-hertel <19572925+s-hertel@users.noreply.github.com>
pull/75743/head
Chris Hambridge 3 years ago committed by GitHub
parent 54a795896a
commit 1353678f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
minor_changes:
- ansible-galaxy - Allow specification of client_id override value for Keycloak Token (https://github.com/ansible/ansible/issues/75593).

@ -28,7 +28,7 @@ The following example shows how to configure multiple servers:
.. code-block:: ini .. code-block:: ini
[galaxy] [galaxy]
server_list = automation_hub, my_org_hub, release_galaxy, test_galaxy server_list = automation_hub, my_org_hub, release_galaxy, test_galaxy, my_galaxy_ng
[galaxy_server.automation_hub] [galaxy_server.automation_hub]
url=https://cloud.redhat.com/api/automation-hub/ url=https://cloud.redhat.com/api/automation-hub/
@ -48,6 +48,12 @@ The following example shows how to configure multiple servers:
url=https://galaxy-dev.ansible.com/ url=https://galaxy-dev.ansible.com/
token=my_test_token token=my_test_token
[galaxy_server.my_galaxy_ng]
url=http://my_galaxy_ng:8000/api/automation-hub/
auth_url=http://my_keycloak:8080/auth/realms/myco/protocol/openid-connect/token
client_id=galaxy-ng
token=my_keycloak_access_token
.. note:: .. note::
You can use the ``--server`` command line argument to select an explicit Galaxy server in the ``server_list`` and You can use the ``--server`` command line argument to select an explicit Galaxy server in the ``server_list`` and
the value of this argument should match the name of the server. To use a server not in the server list, set the value to the URL to access that server (all servers in the server list will be ignored). Also you cannot use the ``--api-key`` argument for any of the predefined servers. You can only use the ``api_key`` argument if you did not define a server list or if you specify a URL in the the value of this argument should match the name of the server. To use a server not in the server list, set the value to the URL to access that server (all servers in the server list will be ignored). Also you cannot use the ``--api-key`` argument for any of the predefined servers. You can only use the ``api_key`` argument if you did not define a server list or if you specify a URL in the
@ -67,6 +73,7 @@ define the following keys:
* ``password``: The password to use, in conjunction with ``username``, for basic authentication. * ``password``: The password to use, in conjunction with ``username``, for basic authentication.
* ``auth_url``: The URL of a Keycloak server 'token_endpoint' if using SSO authentication (for example, Automation Hub). Mutually exclusive with ``username``. Requires ``token``. * ``auth_url``: The URL of a Keycloak server 'token_endpoint' if using SSO authentication (for example, Automation Hub). Mutually exclusive with ``username``. Requires ``token``.
* ``validate_certs``: Whether or not to verify TLS certificates for the Galaxy server. This defaults to True unless the ``--ignore-certs`` option is provided or ``GALAXY_IGNORE_CERTS`` is configured to True. * ``validate_certs``: Whether or not to verify TLS certificates for the Galaxy server. This defaults to True unless the ``--ignore-certs`` option is provided or ``GALAXY_IGNORE_CERTS`` is configured to True.
* ``client_id``: The Keycloak token's client_id to use for authentication. Requires ``auth_url`` and ``token``. The default ``client_id`` is cloud-services to work with Red Hat SSO.
As well as defining these server options in the ``ansible.cfg`` file, you can also define them as environment variables. As well as defining these server options in the ``ansible.cfg`` file, you can also define them as environment variables.
The environment variable is in the form ``ANSIBLE_GALAXY_SERVER_{{ id }}_{{ key }}`` where ``{{ id }}`` is the upper The environment variable is in the form ``ANSIBLE_GALAXY_SERVER_{{ id }}_{{ key }}`` where ``{{ id }}`` is the upper

@ -62,7 +62,8 @@ SERVER_DEF = [
('token', False), ('token', False),
('auth_url', False), ('auth_url', False),
('v3', False), ('v3', False),
('validate_certs', False) ('validate_certs', False),
('client_id', False),
] ]
@ -498,6 +499,7 @@ class GalaxyCLI(CLI):
# auth_url is used to create the token, but not directly by GalaxyAPI, so # auth_url is used to create the token, but not directly by GalaxyAPI, so
# it doesn't need to be passed as kwarg to GalaxyApi # it doesn't need to be passed as kwarg to GalaxyApi
auth_url = server_options.pop('auth_url', None) auth_url = server_options.pop('auth_url', None)
client_id = server_options.pop('client_id', None)
token_val = server_options['token'] or NoTokenSentinel token_val = server_options['token'] or NoTokenSentinel
username = server_options['username'] username = server_options['username']
available_api_versions = None available_api_versions = None
@ -524,7 +526,8 @@ class GalaxyCLI(CLI):
if auth_url: if auth_url:
server_options['token'] = KeycloakToken(access_token=token_val, server_options['token'] = KeycloakToken(access_token=token_val,
auth_url=auth_url, auth_url=auth_url,
validate_certs=validate_certs) validate_certs=validate_certs,
client_id=client_id)
else: else:
# The galaxy v1 / github / django / 'Token' # The galaxy v1 / github / django / 'Token'
server_options['token'] = GalaxyToken(token=token_val) server_options['token'] = GalaxyToken(token=token_val)

@ -50,14 +50,18 @@ class KeycloakToken(object):
token_type = 'Bearer' token_type = 'Bearer'
def __init__(self, access_token=None, auth_url=None, validate_certs=True): def __init__(self, access_token=None, auth_url=None, validate_certs=True, client_id=None):
self.access_token = access_token self.access_token = access_token
self.auth_url = auth_url self.auth_url = auth_url
self._token = None self._token = None
self.validate_certs = validate_certs self.validate_certs = validate_certs
self.client_id = client_id
if self.client_id is None:
self.client_id = 'cloud-services'
def _form_payload(self): def _form_payload(self):
return 'grant_type=refresh_token&client_id=cloud-services&refresh_token=%s' % self.access_token return 'grant_type=refresh_token&client_id=%s&refresh_token=%s' % (self.client_id,
self.access_token)
def get(self): def get(self):
if self._token: if self._token:

@ -8,8 +8,10 @@ __metaclass__ = type
import os import os
import pytest import pytest
from units.compat.mock import MagicMock
import ansible.constants as C import ansible.constants as C
from ansible.cli.galaxy import GalaxyCLI, SERVER_DEF
from ansible.galaxy.token import GalaxyToken, NoTokenSentinel from ansible.galaxy.token import GalaxyToken, NoTokenSentinel
from ansible.module_utils._text import to_bytes, to_text from ansible.module_utils._text import to_bytes, to_text
@ -32,6 +34,47 @@ def b_token_file(request, tmp_path_factory):
C.GALAXY_TOKEN_PATH = orig_token_path C.GALAXY_TOKEN_PATH = orig_token_path
def test_client_id(monkeypatch):
monkeypatch.setattr(C, 'GALAXY_SERVER_LIST', ['server1', 'server2'])
test_server_config = {option[0]: None for option in SERVER_DEF}
test_server_config.update(
{
'url': 'http://my_galaxy_ng:8000/api/automation-hub/',
'auth_url': 'http://my_keycloak:8080/auth/realms/myco/protocol/openid-connect/token',
'client_id': 'galaxy-ng',
'token': 'access_token',
}
)
test_server_default = {option[0]: None for option in SERVER_DEF}
test_server_default.update(
{
'url': 'https://cloud.redhat.com/api/automation-hub/',
'auth_url': 'https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token',
'token': 'access_token',
}
)
get_plugin_options = MagicMock(side_effect=[test_server_config, test_server_default])
monkeypatch.setattr(C.config, 'get_plugin_options', get_plugin_options)
cli_args = [
'ansible-galaxy',
'collection',
'install',
'namespace.collection:1.0.0',
]
galaxy_cli = GalaxyCLI(args=cli_args)
mock_execute_install = MagicMock()
monkeypatch.setattr(galaxy_cli, '_execute_install_collection', mock_execute_install)
galaxy_cli.run()
assert galaxy_cli.api_servers[0].token.client_id == 'galaxy-ng'
assert galaxy_cli.api_servers[1].token.client_id == 'cloud-services'
def test_token_explicit(b_token_file): def test_token_explicit(b_token_file):
assert GalaxyToken(token="explicit").get() == "explicit" assert GalaxyToken(token="explicit").get() == "explicit"

Loading…
Cancel
Save