diff --git a/galaxy.yml b/galaxy.yml index 4923d25..cc0ae87 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: infra name: quay_configuration -version: 2.4.0 +version: 2.5.0 readme: README.md authors: - Hervé Quatremain diff --git a/plugins/modules/quay_notification.py b/plugins/modules/quay_notification.py index 4450fa3..e78e2b9 100644 --- a/plugins/modules/quay_notification.py +++ b/plugins/modules/quay_notification.py @@ -34,9 +34,12 @@ description: - Name of the repository which contains the notifications to manage. The format for the name is C(namespace)/C(shortname). The namespace can be - an organization or a personal namespace. + an organization or your personal namespace. - If you omit the namespace part in the name, then the module looks for the repository in your personal namespace. + - You can manage notifications for repositories in your personal + namespace, but not in the personal namespace of other users. The token + you use in O(quay_token) determines the user account you are using. required: true type: str title: @@ -453,6 +456,18 @@ def main(): module.fail_json( msg="The {namespace} namespace does not exist.".format(namespace=namespace) ) + # Make sure that the current user is the owner of that namespace + if ( + not namespace_details.get("is_organization") + and namespace_details.get("name") != my_name + ): + if my_name: + msg = "You ({user}) are not the owner of {namespace}'s namespace.".format( + user=my_name, namespace=namespace + ) + else: + msg = "You cannot access {namespace}'s namespace.".format(namespace=namespace) + module.fail_json(msg=msg) full_repo_name = "{namespace}/{repository}".format( namespace=namespace, repository=repo_shortname diff --git a/plugins/modules/quay_repository.py b/plugins/modules/quay_repository.py index e6d2454..8228b11 100644 --- a/plugins/modules/quay_repository.py +++ b/plugins/modules/quay_repository.py @@ -34,10 +34,13 @@ description: - Name of the repository to create, remove, or modify. The format for the name is C(namespace)/C(shortname). The namespace can be an organization - or a personal namespace. + or your personal namespace. - The name must be in lowercase and must not contain white spaces. - If you omit the namespace part in the name, then the module uses your personal namespace. + - You can manage repositories in your personal namespace, + but not in the personal namespace of other users. The token you use in + O(quay_token) determines the user account you are using. required: true type: str visibility: @@ -326,6 +329,27 @@ def main(): ).format(name=name) ) + # Check whether namespace exists (organization or user account) + namespace_details = module.get_namespace(namespace) + if not namespace_details: + if state == "absent": + module.exit_json(changed=False) + module.fail_json( + msg="The {namespace} namespace does not exist.".format(namespace=namespace) + ) + # Make sure that the current user is the owner of that namespace + if ( + not namespace_details.get("is_organization") + and namespace_details.get("name") != my_name + ): + if my_name: + msg = "You ({user}) are not the owner of {namespace}'s namespace.".format( + user=my_name, namespace=namespace + ) + else: + msg = "You cannot access {namespace}'s namespace.".format(namespace=namespace) + module.fail_json(msg=msg) + full_repo_name = "{namespace}/{repository}".format( namespace=namespace, repository=repo_shortname ) @@ -351,9 +375,7 @@ def main(): # "can_admin": true # } repo_details = module.get_object_path( - "repository/{full_repo_name}", - ok_error_codes=[404, 403], - full_repo_name=full_repo_name, + "repository/{full_repo_name}", full_repo_name=full_repo_name ) # Remove the repository @@ -366,13 +388,6 @@ def main(): full_repo_name=full_repo_name, ) - # Check whether namespace exists (organization or user account) - namespace_details = module.get_namespace(namespace) - if not namespace_details: - module.fail_json( - msg="The {namespace} namespace does not exist.".format(namespace=namespace) - ) - changed = False if not repo_details: # Create the repository diff --git a/plugins/modules/quay_repository_mirror.py b/plugins/modules/quay_repository_mirror.py index a1e240e..e619ad3 100644 --- a/plugins/modules/quay_repository_mirror.py +++ b/plugins/modules/quay_repository_mirror.py @@ -33,8 +33,13 @@ name: description: - Name of the existing repository for which the mirror parameters are - configured. The format for the name is C(namespace)/C(shortname). The - namespace can only be an organization namespace. + configured. The format for the name is C(namespace)/C(shortname).The + namespace can be an organization or your personal namespace. + - If you omit the namespace part in the name, then the module looks for + the repository in your personal namespace. + - You can manage mirrors for repositories in your personal + namespace, but not in the personal namespace of other users. The token + you use in O(quay_token) determines the user account you are using. required: true type: str is_enabled: @@ -235,6 +240,18 @@ def main(): module.fail_json( msg="The {namespace} namespace does not exist.".format(namespace=namespace) ) + # Make sure that the current user is the owner of that namespace + if ( + not namespace_details.get("is_organization") + and namespace_details.get("name") != my_name + ): + if my_name: + msg = "You ({user}) are not the owner of {namespace}'s namespace.".format( + user=my_name, namespace=namespace + ) + else: + msg = "You cannot access {namespace}'s namespace.".format(namespace=namespace) + module.fail_json(msg=msg) full_repo_name = "{namespace}/{repository}".format( namespace=namespace, repository=repo_shortname diff --git a/plugins/modules/quay_repository_prune.py b/plugins/modules/quay_repository_prune.py index 4bacd73..60522d1 100644 --- a/plugins/modules/quay_repository_prune.py +++ b/plugins/modules/quay_repository_prune.py @@ -34,10 +34,13 @@ repository: description: - Name of the existing repository to configure. The format for the name is - C(namespace)/C(shortname). The namespace can be an organization or a + C(namespace)/C(shortname). The namespace can be an organization or your personal namespace. - If you omit the namespace part in the name, then the module looks for the repository in your personal namespace. + - You can manage auto-pruning policies for repositories in your personal + namespace, but not in the personal namespace of other users. The token + you use in O(quay_token) determines the user account you are using. required: true type: str append: @@ -193,6 +196,18 @@ def main(): module.fail_json( msg="The {namespace} namespace does not exist.".format(namespace=namespace) ) + # Make sure that the current user is the owner of that namespace + if ( + not namespace_details.get("is_organization") + and namespace_details.get("name") != my_name + ): + if my_name: + msg = "You ({user}) are not the owner of {namespace}'s namespace.".format( + user=my_name, namespace=namespace + ) + else: + msg = "You cannot access {namespace}'s namespace.".format(namespace=namespace) + module.fail_json(msg=msg) full_repo_name = "{namespace}/{repository}".format( namespace=namespace, repository=repo_shortname diff --git a/plugins/modules/quay_robot.py b/plugins/modules/quay_robot.py index d86d40b..0c63f1e 100644 --- a/plugins/modules/quay_robot.py +++ b/plugins/modules/quay_robot.py @@ -38,7 +38,7 @@ name: description: - Name of the robot account to create or remove, in the format - C(namespace)+C(shortname). The namespace can be an organization or a + C(namespace)+C(shortname). The namespace can be an organization or your personal namespace. - The short name (the part after the C(+) sign) must be in lowercase, must not contain white spaces, must not start by a digit, and must be diff --git a/tests/integration/targets/check_mode/tasks/main.yml b/tests/integration/targets/check_mode/tasks/main.yml index b1d252e..a20b747 100644 --- a/tests/integration/targets/check_mode/tasks/main.yml +++ b/tests/integration/targets/check_mode/tasks/main.yml @@ -114,7 +114,7 @@ that: result['changed'] fail_msg: The preceding task should have deleted the user account -- name: Ensure user lvasquez does not exist (check mode) +- name: Ensure user lvasquez does not exist infra.quay_configuration.quay_user: username: lvasquez email: newemail@example.com @@ -128,4 +128,68 @@ ansible.builtin.assert: that: result['changed'] fail_msg: The preceding task should have deleted the user account + +# Expected errors +- name: ERROR EXPECTED Ensure the user exists (host does not exist) + infra.quay_configuration.quay_user: + username: lvasquez + email: newemail@example.com + state: present + quay_host: http://doesnotexists.local + quay_token: "{{ quay_token }}" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed (host does not exist) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (host does not exist) + +- name: ERROR EXPECTED Ensure the user exists (cannot connect) + infra.quay_configuration.quay_user: + username: lvasquez + email: newemail@example.com + state: present + quay_host: https://locahost:12345 + quay_token: "{{ quay_token }}" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed (cannot connect to the API) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (cannot connect) + +- name: ERROR EXPECTED Ensure the user exists (SSL validation) + infra.quay_configuration.quay_user: + username: lvasquez + email: newemail@example.com + state: present + quay_host: "{{ quay_host }}" + quay_token: "{{ quay_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (SSL validation) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (SSL validation) + +- name: ERROR EXPECTED Ensure the user exists (credentials) + infra.quay_configuration.quay_user: + username: lvasquez + email: newemail@example.com + state: present + quay_host: "{{ quay_host }}" + quay_token: "AABBCCDDEEFFGGHH" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong credentials) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong credentials) ... diff --git a/tests/integration/targets/quay_notification/tasks/main.yml b/tests/integration/targets/quay_notification/tasks/main.yml index 8f7bfcb..2bff8da 100644 --- a/tests/integration/targets/quay_notification/tasks/main.yml +++ b/tests/integration/targets/quay_notification/tasks/main.yml @@ -72,6 +72,27 @@ that: result['failed'] fail_msg: The preceding task should have failed (missing parameters) +- name: ERROR EXPECTED Access to another user namespace + infra.quay_configuration.quay_notification: + repository: ansibletestuser1/ansibletestrepo + title: Test Quay Notification + event: repo_push + method: quay_notification + config: + name: ansibletestteam1 + type: team + state: present + quay_host: "{{ quay_url }}" + quay_token: "{{ quay_token }}" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (not allowed) + - name: Ensure notification of type Quay Notification exists infra.quay_configuration.quay_notification: repository: ansibletestorg/ansibletestrepo diff --git a/tests/integration/targets/quay_repository/tasks/main.yml b/tests/integration/targets/quay_repository/tasks/main.yml index 4103d1b..f813a4f 100644 --- a/tests/integration/targets/quay_repository/tasks/main.yml +++ b/tests/integration/targets/quay_repository/tasks/main.yml @@ -1,4 +1,19 @@ --- +- name: ERROR EXPECTED Access to another user namespace + infra.quay_configuration.quay_repository: + name: ansibletestuser1/ansibletestrepo1 + state: present + quay_host: "{{ quay_url }}" + quay_token: "{{ quay_token }}" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (not allowed) + - name: Ensure repository ansibletestrepo1 exists infra.quay_configuration.quay_repository: name: ansibletestorg/ansibletestrepo1 diff --git a/tests/integration/targets/quay_repository_mirror/tasks/main.yml b/tests/integration/targets/quay_repository_mirror/tasks/main.yml index 6b074ea..89a858e 100644 --- a/tests/integration/targets/quay_repository_mirror/tasks/main.yml +++ b/tests/integration/targets/quay_repository_mirror/tasks/main.yml @@ -29,6 +29,26 @@ that: result['failed'] fail_msg: The preceding task should have failed (missing parameters) +- name: ERROR EXPECTED Access to another user namespace + infra.quay_configuration.quay_repository_mirror: + name: ansibletestuser1/ansibletestrepo1 + external_reference: docker.io/library/hello-world + robot_username: ansibletestorg+ansibletestrobot1 + image_tags: + - latest + sync_interval: 43200 + sync_start_date: "2021-01-01T12:00:00Z" + quay_host: "{{ quay_url }}" + quay_token: "{{ quay_token }}" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (not allowed) + - name: Ensure repository mirror configuration for ansibletestrepo1 exists infra.quay_configuration.quay_repository_mirror: name: ansibletestorg/ansibletestrepo1 diff --git a/tests/integration/targets/quay_repository_prune/tasks/main.yml b/tests/integration/targets/quay_repository_prune/tasks/main.yml index 95989a5..2840a08 100644 --- a/tests/integration/targets/quay_repository_prune/tasks/main.yml +++ b/tests/integration/targets/quay_repository_prune/tasks/main.yml @@ -63,6 +63,24 @@ that: result['failed'] fail_msg: The preceding task should have failed (wrong date) +- name: ERROR EXPECTED Access to another user namespace + infra.quay_configuration.quay_repository_prune: + repository: ansibletestuser1/ansibletestrepo + method: tags + value: 5 + tag_pattern: "unstable" + tag_pattern_matches: false + quay_host: "{{ quay_url }}" + quay_token: "{{ quay_token }}" + validate_certs: false + ignore_errors: true + register: result + +- name: Ensure that the task failed + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (not allowed) + - name: Ensure a policy does not exist in a non-existing repository (no change) infra.quay_configuration.quay_repository_prune: repository: ansibletestorg/nonexisting