diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index d39e56e4..f330b992 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -52,6 +52,38 @@ type: bool default: False version_added: "1.2.0" + permissions: + description: + - Set the permissions of the folder. + - Able to define it for a I(role), I(teamId) and I(userId) + - This will remove existing permissions if they are not included. + type: list + elements: dict + suboptions: + role: + description: + - The name of the role. + choices: + - Viewer + - Editor + type: str + username: + description: + - The name of the user. + type: str + team: + description: + - The name of the team. + type: str + permission: + description: + - The permission to be added. + choices: + - view + - edit + - admin + required: true + type: str extends_documentation_fragment: - community.grafana.basic_auth - community.grafana.api_key @@ -66,6 +98,20 @@ title: "grafana_working_group" state: present +- name: Create a folder and set permissions + community.grafana.grafana_folder: + url: "https://grafana.example.com" + grafana_api_key: "{{ some_api_token_value }}" + title: "grafana_working_group" + state: present + permissions: + - role: "Editor" + permission: "admin" + - username: "myuser" + permission: "edit" + - team: "myteam" + permission: "view" + - name: Delete a folder community.grafana.grafana_folder: url: "https://grafana.example.com" @@ -246,6 +292,70 @@ def delete_folder(self, folder_uid): response = self._send_request(url, headers=self.headers, method="DELETE") return response + def get_folder_permission(self, folder_uid): + url = "/api/folders/{folder_uid}/permissions".format(folder_uid=folder_uid) + response = self._send_request(url, headers=self.headers, method="GET") + KEYS_TO_REMOVE = ['created', 'inherited', 'slug', 'teamAvatarUrl', 'teamEmail', 'title', 'uid', 'updated', 'url', + 'userAvatarUrl', 'userEmail', 'userLogin', 'folderId', 'permissionName', 'team', 'isFolder'] + for items in response: + for k in KEYS_TO_REMOVE: + del items[k] + if items['userId'] == 0: + del items['userId'] + if items['teamId'] == 0: + del items['teamId'] + return response + + def create_folder_permission(self, folder_uid, permissions): + url = "/api/folders/{folder_uid}/permissions".format(folder_uid=folder_uid) + items = { + "items": [] + } + items["items"].extend(permissions) + response = self._send_request(url, data=items, headers=self.headers, method="POST") + return response + + def get_user_id_from_mail(self, email): + url = "/api/users/lookup?loginOrEmail={email}".format(email=email) + user = self._send_request(url, headers=self.headers, method="GET") + if user is None: + self._module.fail_json(failed=True, msg="User '%s' does not exists" % email) + return user.get("id") + + def get_team(self, name): + url = "/api/teams/search?name={team}".format(team=quote(name)) + response = self._send_request(url, headers=self.headers, method="GET") + if len(response.get("teams")) == 0: + self._module.fail_json(failed=True, msg="Team '%s' does not exists" % name) + return response.get("teams")[0]["id"] + + def process_permission_input(self, input): + for i in input: + if i["username"] is None: + del i["username"] + if i["team"] is None: + del i["team"] + if i["role"] is None: + del i["role"] + + if "username" in i: + i["userId"] = i.pop("username") + i["userId"] = self.get_user_id_from_mail(i["userId"]) + + if "team" in i: + i["teamId"] = i.pop("team") + i["teamId"] = self.get_team(i["teamId"]) + + if i["permission"] == "view": + i["permission"] = 1 + elif i["permission"] == "edit": + i["permission"] = 2 + elif i["permission"] == "admin": + i["permission"] = 4 + else: + self._module.fail_json(failed=True, msg="Use view, edit or admin as value for permission") + return input + def setup_module_object(): module = AnsibleModule( @@ -257,11 +367,19 @@ def setup_module_object(): return module +permissions_spec = dict( + role=dict(type='str', choices=['Viewer', 'Editor']), + team=dict(type='str'), + username=dict(type='str'), + permission=dict(type='str', choices=['view', 'edit', 'admin'], required=True), +) + argument_spec = base.grafana_argument_spec() argument_spec.update( name=dict(type='str', aliases=['title'], required=True), state=dict(type='str', default='present', choices=['present', 'absent']), skip_version_check=dict(type='bool', default=False), + permissions=dict(type='list', elements='dict', options=permissions_spec), ) @@ -270,6 +388,7 @@ def main(): module = setup_module_object() state = module.params['state'] title = module.params['name'] + permissions = module.params['permissions'] grafana_iface = GrafanaFolderInterface(module) @@ -281,6 +400,13 @@ def main(): folder = grafana_iface.get_folder(title) changed = True folder = grafana_iface.get_folder(title) + if permissions is not None: + current_permissions = grafana_iface.get_folder_permission(folder.get("uid")) + new_permissions = grafana_iface.process_permission_input(permissions) + if current_permissions != new_permissions: + grafana_iface.create_folder_permission(folder.get("uid"), permissions) + changed = True + module.exit_json(changed=changed, folder=folder) elif state == 'absent': folder = grafana_iface.get_folder(title) diff --git a/tests/integration/targets/grafana_folder/tasks/main.yml b/tests/integration/targets/grafana_folder/tasks/main.yml index c6a52056..f73c3ef1 100644 --- a/tests/integration/targets/grafana_folder/tasks/main.yml +++ b/tests/integration/targets/grafana_folder/tasks/main.yml @@ -28,6 +28,93 @@ - "result.changed == false" - "result.folder.title == 'grafana_working_group'" + +- name: Change folder permissions + grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + title: "grafana_working_group" + state: present + permissions: + - role: "Editor" + permission: "admin" + - role: "Viewer" + permission: "view" + register: result + +- assert: + that: + - "result.changed == true" + - "result.folder.title == 'grafana_working_group'" + +- name: Test folder permission idempotency + grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + title: "grafana_working_group" + state: present + permissions: + - role: "Editor" + permission: "admin" + - role: "Viewer" + permission: "view" + register: result + +- assert: + that: + - "result.changed == false" + - "result.folder.title == 'grafana_working_group'" + +- name: Test folder permission user lookup + grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + title: "grafana_working_group" + state: present + permissions: + - role: "Editor" + permission: "admin" + - role: "Viewer" + permission: "view" + - username: "unknown" + permission: "edit" + register: result + ignore_errors: true + +- set_fact: + expected_error: "User 'unknown' does not exists" + +- assert: + that: + - "result.changed == false" + - "result.failed == true" + - "result.msg == expected_error" + +- name: Test folder permission team lookup + grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + title: "grafana_working_group" + state: present + permissions: + - team: "unknown" + permission: "edit" + register: result + ignore_errors: true + +- set_fact: + expected_error: "Team 'unknown' does not exists" + +- assert: + that: + - "result.changed == false" + - "result.failed == true" + - "result.msg == expected_error" + - name: Delete a Folder grafana_folder: url: "{{ grafana_url }}"