From 2d185656b36ac38d7f4bf767a71b28a08b668b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Quatremain?= Date: Mon, 5 Aug 2024 17:01:37 +0200 Subject: [PATCH] New for_user option in quay_api_token (Quay 3.12) (#5) --- CHANGELOG.rst | 13 ++++ changelogs/changelog.yaml | 10 +++ galaxy.yml | 2 +- plugins/modules/quay_api_token.py | 74 ++++++++++++++++--- plugins/modules/quay_application.py | 2 +- plugins/modules/quay_first_user.py | 2 +- plugins/modules/quay_robot.py | 4 +- plugins/modules/quay_tag_info.py | 4 +- .../targets/quay_api_token/tasks/main.yml | 19 ++++- .../targets/setup_organization/tasks/main.yml | 5 ++ 10 files changed, 116 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9bed4e0..5dce2b7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,19 @@ Quay Container Registry Collection Release Notes .. contents:: Topics +v2.2.0 +====== + +Release Summary +--------------- + +Support creating OAuth access tokens for other users. + +Minor Changes +------------- + +- Add the ``for_user`` option to the ``infra.quay_configuration.quay_api_token`` module. With this option you can assign OAuth API tokens to other users (Quay 3.12 and later). + v2.1.0 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index ca90b46..216ed26 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -244,3 +244,13 @@ releases: fragments: - PR2-v2.1.0-summary.yml release_date: '2024-07-29' + 2.2.0: + changes: + minor_changes: + - Add the ``for_user`` option to the ``infra.quay_configuration.quay_api_token`` + module. With this option you can assign OAuth API tokens to other users (Quay + 3.12 and later). + release_summary: Support creating OAuth access tokens for other users. + fragments: + - PR5-v2.2.0-summary.yml + release_date: '2024-08-05' diff --git a/galaxy.yml b/galaxy.yml index 8aecb8c..ffa7a19 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: infra name: quay_configuration -version: 2.1.0 +version: 2.2.0 readme: README.md authors: - Hervé Quatremain diff --git a/plugins/modules/quay_api_token.py b/plugins/modules/quay_api_token.py index 8a6176e..dea14a6 100644 --- a/plugins/modules/quay_api_token.py +++ b/plugins/modules/quay_api_token.py @@ -67,10 +67,23 @@ - user:read - all default: repo:read + for_user: + description: + - The username to generate an OAuth access token for. + - The user receives a notification in the web interface, which enables + the user to retrieve the token. + - When you use this option, the module does not return the token. + - Requires Quay version 3.12 or later. + type: str + required: false notes: - Supports C(check_mode). + - I(for_user) requires Quay version 3.12 or later. + - Your Quay administrator must enable the OAuth assignment capability + of your Quay installation (C(FEATURE_ASSIGN_OAUTH_TOKEN) in C(config.yaml)) + to use the I(for_user) option in Quay version 3.12 or later. - The generated OAuth access token acts on behalf of the user account you use - with the module (in I(quay_username)). + with the module (in I(for_user) if set, otherwise in I(quay_username)). - The user must have admin rights to the application's organization, by being the creator of this organization, or by belonging to a team with admin rights. @@ -97,9 +110,21 @@ register: token_details - name: Display the new OAuth access token - debug: + ansible.builtin.debug: msg: "The OAuth access token is: {{ token_details['access_token'] }}" +- name: Generate an OAuth access token for dwilde + infra.quay_configuration.quay_api_token: + quay_username: lvasquez + quay_password: vs9mrD55NP + # A notification in the web interface informs dwilde of the new OAuth + # access token. + for_user: dwilde + client_id: PZ6F80R1LCVPGYNZGSZQ + rights: + - repo:admin + quay_host: https://quay.example.com + # The following example creates an organization, an OAuth application, a user # account, and a team, and then generates an OAuth access token for this user # account. @@ -154,14 +179,14 @@ register: token_details - name: Display the new OAuth access token - debug: + ansible.builtin.debug: msg: "The OAuth access token is: {{ token_details['access_token'] }}" """ RETURN = r""" access_token: description: The OAuth access token. - returned: always + returned: only when I(for_user) is not set type: str sample: CywbRGkh1ttYkRRy9VL0Aw0yU9q7J62vIeo7WCFw """ @@ -194,6 +219,7 @@ def main(): rights=dict( type="list", elements="str", choices=allowed_rights, default=["repo:read"] ), + for_user=dict(), ) # Create a module for ourselves @@ -209,25 +235,51 @@ def main(): rights.remove("all") else: rights = set(rights) + for_user = module.params.get("for_user") - # Generate the OAuth access token - headers = { - "Accept": "*/*", - "Content-Type": "application/x-www-form-urlencoded", - } redirect_url = module.host_url._replace(path="/oauth/localapp") data = { "response_type": "token", "client_id": client_id, "redirect_uri": redirect_url.geturl(), "scope": " ".join(rights), - "_csrf_token": module.token, } - url = module.host_url._replace(path="/oauth/authorizeapp") + + # Generate an OAuth token for another user + if for_user is not None: + if module.check_mode: + module.exit_json(changed=True) + data["username"] = for_user + # The data is provided as URL query parameters + url = module.host_url._replace(path="/oauth/authorize/assignuser")._replace( + query=urlencode(data) + ) + try: + response = module.make_raw_request("POST", url) + except APIModuleError as e: + module.fail_json(msg=str(e)) + + if response["status_code"] != 200: + module.fail_json( + msg=( + "Cannot create the OAuth access token for {user}: " + "Maybe the user does not exist." + ).format(user=for_user) + ) + + module.exit_json(changed=True) + + # Generate an OAuth token for the current user if module.check_mode: module.exit_json( changed=True, access_token="NotValidCheckModeNotValidCheckModeNotVal" ) + data["_csrf_token"] = module.token + headers = { + "Accept": "*/*", + "Content-Type": "application/x-www-form-urlencoded", + } + url = module.host_url._replace(path="/oauth/authorizeapp") try: response = module.make_raw_request( "POST", diff --git a/plugins/modules/quay_application.py b/plugins/modules/quay_application.py index ad3ae7d..85c2148 100644 --- a/plugins/modules/quay_application.py +++ b/plugins/modules/quay_application.py @@ -97,7 +97,7 @@ quay_token: vgfH9zH5q6eV16Con7SvDQYSr0KPYQimMHVehZv7 register: app_details -- debug: +- ansible.builtin.debug: msg: "Client secret: {{ app_details['client_secret'] }}" - name: Ensure the application is renamed diff --git a/plugins/modules/quay_first_user.py b/plugins/modules/quay_first_user.py index 7d96e14..358d4f9 100644 --- a/plugins/modules/quay_first_user.py +++ b/plugins/modules/quay_first_user.py @@ -95,7 +95,7 @@ quay_host: https://quay.example.com register: result -- debug: +- ansible.builtin.debug: msg: "Access token: {{ result['access_token'] }}" """ diff --git a/plugins/modules/quay_robot.py b/plugins/modules/quay_robot.py index 1410f26..878ac99 100644 --- a/plugins/modules/quay_robot.py +++ b/plugins/modules/quay_robot.py @@ -84,10 +84,10 @@ quay_token: vgfH9zH5q6eV16Con7SvDQYSr0KPYQimMHVehZv7 register: robot_details -- debug: +- ansible.builtin.debug: msg: "Robot token: {{ robot_details['token'] }}" -- debug: +- ansible.builtin.debug: msg: "Docker configuration (Base64): {{ robot_details['name'] | infra.quay_configuration.quay_docker_config(robot_details['token'], 'https://quay.example.com') }}" diff --git a/plugins/modules/quay_tag_info.py b/plugins/modules/quay_tag_info.py index d7bd6b5..bcf978b 100644 --- a/plugins/modules/quay_tag_info.py +++ b/plugins/modules/quay_tag_info.py @@ -137,12 +137,12 @@ - The module only returns expired tags when the I(only_active_tags) parameter is C(no). type: int - returned: only when an expiration date has been explicitly assigned. + returned: only when an expiration date has been explicitly assigned sample: 1640336040 expiration: description: Expiration date and time in a human readable format. type: str - returned: only when an expiration date has been explicitly assigned. + returned: only when an expiration date has been explicitly assigned sample: Fri, 24 Dec 2021 08:54:00 -0000 sample: [ { diff --git a/tests/integration/targets/quay_api_token/tasks/main.yml b/tests/integration/targets/quay_api_token/tasks/main.yml index 4bd7d29..66fa0a8 100644 --- a/tests/integration/targets/quay_api_token/tasks/main.yml +++ b/tests/integration/targets/quay_api_token/tasks/main.yml @@ -34,7 +34,7 @@ validate_certs: false register: app_details -- name: Generate an OAuth access token for the user +- name: Generate an OAuth access token for the current user infra.quay_configuration.quay_api_token: quay_username: testuser1 quay_password: vs9mrD55NP @@ -50,6 +50,23 @@ that: "'access_token' in result" fail_msg: The result should have the access_token key +- name: Generate an OAuth access token for ansibletestuser1 + infra.quay_configuration.quay_api_token: + for_user: ansibletestuser1 + quay_username: testuser1 + quay_password: vs9mrD55NP + client_id: "{{ app_details['client_id'] }}" + rights: + - org:admin + - repo:admin + - repo:create + - repo:read + - repo:write + - user:admin + - user:read + quay_host: "{{ quay_url }}" + validate_certs: false + - name: Ensure testteam1 team is removed infra.quay_configuration.quay_team: name: testteam1 diff --git a/tests/integration/targets/setup_organization/tasks/main.yml b/tests/integration/targets/setup_organization/tasks/main.yml index 79dc236..ceb9914 100644 --- a/tests/integration/targets/setup_organization/tasks/main.yml +++ b/tests/integration/targets/setup_organization/tasks/main.yml @@ -11,4 +11,9 @@ # To speed up the tests, only create the user accounts, robots, and teams # when the organization does not already exist. notify: Create resources in organization + +# Ensure the user accounts, robots, and teams are created before the roles that +# might use them. +- name: Ensure the handlers run just after the role execution + ansible.builtin.meta: flush_handlers ...