From 58fc1e5e1343830a90399ede1bdc6d923c3e0677 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sun, 1 Nov 2020 23:02:32 +0100 Subject: [PATCH 01/32] main functions defined. stil WIP --- plugins/modules/guacamole_users_group.py | 542 +++++++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 plugins/modules/guacamole_users_group.py diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py new file mode 100644 index 0000000..632d87c --- /dev/null +++ b/plugins/modules/guacamole_users_group.py @@ -0,0 +1,542 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Pablo Escobar +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible_collections.scicore.guacamole.plugins.module_utils.guacamole import GuacamoleError, \ + guacamole_get_token, guacamole_get_connections, guacamole_get_connections_group_id +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: guacamole_connections_group + +short_description: Administer guacamole connections groups using the rest API + +version_added: "2.9" + +description: + - "Add or remove guacamole connections groups." + +options: + base_url: + description: + - Url to access the guacamole API + required: true + aliases: ['url'] + type: str + + auth_username: + description: + - Guacamole admin user to login to the API + required: true + type: str + + auth_password: + description: + - Guacamole admin user password to login to the API + required: true + type: str + + validate_certs: + description: + - Validate ssl certs? + default: true + type: bool + + group_name: + description: + - Group name to create + required: true + type: str + + parent_group: + description: + - Parent group in case this is a sub-group + default: 'ROOT' + aliases: ['parentIdentifier'] + type: str + + group_type: + description: + - Choose the group type + default: 'ORGANIZATIONAL' + type: str + choices: + - "ORGANIZATIONAL" + - "BALANCING" + + max_connections: + description: + - Max connections in this group + type: int + + max_connections_per_user: + description: + - Max connections per user in this group + type: int + + enable_session_affinity: + description: + - Enable session affinity for this group + type: bool + + state: + description: + - Create or delete the connections group? + default: 'present' + type: str + choices: + - present + - absent + + force_deletion: + description: + - Force deletion of the group even if it has child connections + default: 'False' + type: bool + +author: + - Pablo Escobar Lopez (@pescobar) +''' + +EXAMPLES = ''' + +- name: Create a new connections group "group_3" + scicore.guacamole.guacamole_connections_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + group_name: group_3 + +- name: Delete connections group "group_4" + scicore.guacamole.guacamole_connections_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + group_name: group_4 + state: absent + +- name: Force deletion of connections group "group_5 which has child connections" + scicore.guacamole.guacamole_connections_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + group_name: group_4 + state: absent + force_deletion: true +''' + +RETURN = ''' +group_info: + description: Information about the created or updated group + type: dict + returned: always +message: + description: Some extra info about what the module did + type: str + returned: always +''' + +URL_LIST_GROUPS = "{url}/api/session/data/{datasource}/userGroups?token={token}" +URL_ADD_GROUP = URL_LIST_GROUPS +URL_DELETE_GROUP = "{url}/api/session/data/{datasource}/userGroups/{group_name}?token={token}" +URL_GET_GROUP_PERMISSIONS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/permissions?token={token}" +URL_UPDATE_CONNECTIONS_IN_GROUP = URL_GET_GROUP_PERMISSIONS +URL_GET_GROUP_MEMBERS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/memberUsers?token={token}" +URL_UPDATE_USERS_IN_GROUP = URL_GET_GROUP_MEMBERS + + +def guacamole_get_users_groups(base_url, validate_certs, datasource, auth_token): + """ + Returns a dict of dicts. + Each dict provides the name and state (enabled/disabled) for each group of users + """ + + url_list_users_groups = URL_LIST_GROUPS.format( + url=base_url, datasource=datasource, token=auth_token) + + try: + users_groups = json.load(open_url(url_list_users_groups, method='GET', + validate_certs=validate_certs)) + except ValueError as e: + raise GuacamoleError( + 'API returned invalid JSON when trying to obtain users groups from %s: %s' + % (url_list_users_groups, str(e))) + except Exception as e: + raise GuacamoleError('Could not obtain users groups from %s: %s' + % (url_list_users_groups, str(e))) + + return users_groups + + +def guacamole_add_group(base_url, validate_certs, datasource, auth_token, group_name): + """ + Add a group of users + """ + + url_add_group = URL_ADD_GROUP.format( + url=base_url, datasource=datasource, token=auth_token) + + payload = { + "identifier": group_name, + "attributes": { + "disabled": "" + } + } + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_add_group, method='POST', validate_certs=validate_certs, headers=headers, + data=json.dumps(payload)) + except Exception as e: + # if the group exists we get a http code 400 + if e.code == 400: + pass + # raise GuacamoleError('Group %s already exists.' % group_name) + else: + raise GuacamoleError('Could not add a users group. Error msg: %s' % str(e)) + + +def guacamole_delete_group(base_url, validate_certs, datasource, auth_token, group_name): + """ + Delete a group of users + """ + + url_delete_group = URL_DELETE_GROUP.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_delete_group, method='DELETE', validate_certs=validate_certs, headers=headers) + except Exception as e: + raise GuacamoleError('Could not delete a users group. Error msg: %s' % str(e)) + + +def guacamole_get_users_group_permissions(base_url, validate_certs, datasource, auth_token, group_name): + """ + Returns a dict of dicts. + Each dict provides the details for one of the users groups defined in guacamole + """ + + url_get_users_group_permissions = URL_GET_GROUP_PERMISSIONS.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + try: + group_permissions = json.load(open_url(url_get_users_group_permissions, method='GET', + validate_certs=validate_certs)) + except ValueError as e: + raise GuacamoleError( + 'API returned invalid JSON when trying to obtain group permissions from %s: %s' + % (url_get_users_group_permissions, str(e))) + except Exception as e: + raise GuacamoleError('Could not obtain group permissions from %s: %s' + % (url_get_users_group_permissions, str(e))) + + return group_permissions + + +def guacamole_update_users_in_group(base_url, validate_certs, datasource, auth_token, group_name, user, action): + """ + Add or remove a user to a group. + Action must be "add" or "remove" + """ + + if action not in ['add', 'remove']: + raise GuacamoleError("action must be 'add' or 'remove'") + + url_update_users_in_group = URL_UPDATE_USERS_IN_GROUP.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + payload = { + "op": action, + "path": '/', + "value": user, + } + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_update_users_in_group, method='PATCH', validate_certs=validate_certs, headers=headers, + data=json.dumps(payload)) + except Exception as e: + raise GuacamoleError('Could not update users for group %s in url %s. Error msg: %s' + % (group_name, url_update_users_in_group, str(e))) + + +def guacamole_update_connections_in_group(base_url, validate_certs, datasource, auth_token, group_name, connection_id, action): + """ + Add or remove a conection to a group. + Action must be "add" or "remove" + """ + + if action not in ['add', 'remove']: + raise GuacamoleError("action must be 'add' or 'remove'") + + url_update_connections_in_group = URL_UPDATE_CONNECTIONS_IN_GROUP.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + payload = { + "op": action, + "path": '/connectionPermissions/%s' % connection_id, + "value": 'READ', + } + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_update_connections_in_group, method='PATCH', validate_certs=validate_certs, headers=headers, + data=json.dumps(payload)) + except Exception as e: + raise GuacamoleError('Could not update connections for group %s in url %s. Error msg: %s' + % (group_name, url_update_connections_in_group, str(e))) + + +def main(): + + # define the available arguments/parameters that a user can pass to + # the module + module_args = dict( + base_url=dict(type='str', aliases=['url'], required=True), + auth_username=dict(type='str', required=True), + auth_password=dict(type='str', required=True, no_log=True), + validate_certs=dict(type='bool', default=True), + group_name=dict(type='str', required=True), + state=dict(type='str', choices=['absent', 'present'], default='present') + # parent_group=dict(type='str', default='ROOT'), + # group_type=dict(type='str', choices=['ORGANIZATIONAL', 'BALANCING'], default='ORGANIZATIONAL'), + # max_connections=dict(type='int'), + # max_connections_per_user=dict(type='int'), + # enable_session_affinity=dict(type='bool'), + # force_deletion=dict(type='bool', default=False) + ) + + result = dict(changed=False, msg='', users_group_info={}) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=False + ) + + # Obtain access token, initialize API + try: + guacamole_token = guacamole_get_token( + base_url=module.params.get('base_url'), + auth_username=module.params.get('auth_username'), + auth_password=module.params.get('auth_password'), + validate_certs=module.params.get('validate_certs'), + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + # if module.params.get('parent_group') != "ROOT": + # try: + # module.params['parent_group'] = guacamole_get_connections_group_id( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # group=module.params.get('parent_group'), + # auth_token=guacamole_token['authToken'], + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # Get existing guacamole connections groups before doing anything else + try: + guacamole_users_groups_before = guacamole_get_users_groups( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + # try: + # group_permissions = guacamole_get_users_group_permissions( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # group_name=module.params.get('group_name'), + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + try: + guacamole_add_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + try: + guacamole_delete_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name="group_test", + # group_name=module.params.get('group_name'), + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + #module.fail_json(msg=group_permissions) + module.fail_json(msg=guacamole_users_groups_before) + + # # check if the connections group already exists + # # If the connections group exists we get the numeric id + # guacamole_connections_group_exists = False + # for group_id, group_info in guacamole_connections_groups_before.items(): + # if group_info['name'] == module.params.get('group_name'): + # group_numeric_id = group_info['identifier'] + # guacamole_connections_group_exists = True + # break + + # # module arg state=present so we have to create a new connections group + # # or update an existing one + # if module.params.get('state') == 'present': + + # # populate the payload(json) with the group info that we + # # will send to the API + # payload = guacamole_populate_connections_group_payload(module.params) + + # # the group already exists so we update it + # if guacamole_connections_group_exists: + + # try: + # guacamole_update_connections_group( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # group_numeric_id=group_numeric_id, + # payload=payload + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # # if the group doesn't exists we add it + # else: + + # try: + # guacamole_add_connections_group( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # payload=payload + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # result['msg'] = "Connections group '%s' added" % module.params.get('group_name') + + # # module arg state=absent so we have to delete connections group + # if module.params.get('state') == 'absent': + + # # the group exists so we delete it + # if guacamole_connections_group_exists: + + # # if force_deletion=true we delete the group without any extra check + # if module.params.get('force_deletion'): + + # try: + # guacamole_delete_connections_group( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # group_numeric_id=group_numeric_id + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # # if we are here it's because the group exists and force_deletion=false + # else: + + # # Query all the existing guacamole connections in this group + # # to verify if the group we want to delete has any child connection + # try: + # connections_in_group = guacamole_get_connections( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # group=group_numeric_id, + # auth_token=guacamole_token['authToken'], + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # # if the group is empty (no child connections) we delete it + # if not connections_in_group: + + # try: + # guacamole_delete_connections_group( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # group_numeric_id=group_numeric_id + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # # if the group has child connections and force_deletion=false fail and exit + # else: + # module.fail_json( + # msg="Won't delete a group with child connections unless force_deletion=True" + # ) + + # # if the group to delete doesn't exists we just print a message + # else: + + # result['msg'] = "Connections group '%s' doesn't exists. Not doing anything" \ + # % (module.params.get('group_name')) + + # # Get existing guacamole connections groups AFTER to check if something changed + # try: + # guacamole_connections_groups_after = guacamole_get_connections_groups( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) + + # # check if something changed (idempotence) + # if guacamole_connections_groups_before != guacamole_connections_groups_after: + # result['changed'] = True + + # # return connections_group_info{} for the added/updated/deleted connections group + # if module.params.get('state') == 'present': + # for group_id, group_info in guacamole_connections_groups_after.items(): + # if group_info['name'] == module.params.get('group_name'): + # result['connections_group_info'] = group_info + # break + # else: + # for group_id, group_info in guacamole_connections_groups_before.items(): + # if group_info['name'] == module.params.get('group_name'): + # result['connections_group_info'] = group_info + # break + + module.exit_json(**result) + + +if __name__ == '__main__': + main() From d46aa274fce608fb66e9b3d506b152aa4f243d57 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Mon, 2 Nov 2020 22:13:07 +0100 Subject: [PATCH 02/32] first version adding/removing groups --- plugins/modules/guacamole_users_group.py | 148 ++++++++++------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 632d87c..843e312 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -61,81 +61,53 @@ required: true type: str - parent_group: + connections: description: - - Parent group in case this is a sub-group - default: 'ROOT' - aliases: ['parentIdentifier'] - type: str - - group_type: - description: - - Choose the group type - default: 'ORGANIZATIONAL' - type: str - choices: - - "ORGANIZATIONAL" - - "BALANCING" + - List of connections in this group + type: list + elements: str - max_connections: + users: description: - - Max connections in this group - type: int - - max_connections_per_user: - description: - - Max connections per user in this group - type: int - - enable_session_affinity: - description: - - Enable session affinity for this group - type: bool + - List of users in this group + type: list + elements: str state: description: - - Create or delete the connections group? + - Create or delete the users group default: 'present' type: str choices: - present - absent - force_deletion: - description: - - Force deletion of the group even if it has child connections - default: 'False' - type: bool - author: - Pablo Escobar Lopez (@pescobar) ''' EXAMPLES = ''' -- name: Create a new connections group "group_3" - scicore.guacamole.guacamole_connections_group: +- name: Create a new group "lab_3" + scicore.guacamole.guacamole_users_group: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - group_name: group_3 - -- name: Delete connections group "group_4" - scicore.guacamole.guacamole_connections_group: + group_name: lab_3 + connections: + - rdp_lab_1 + - vnc_lab_1 + users: + - john + - laura + +- name: Delete users group "developers" + scicore.guacamole.guacamole_users_group: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - group_name: group_4 + group_name: developers state: absent - -- name: Force deletion of connections group "group_5 which has child connections" - scicore.guacamole.guacamole_connections_group: - base_url: http://localhost/guacamole - auth_username: guacadmin - auth_password: guacadmin - group_name: group_4 - state: absent - force_deletion: true ''' RETURN = ''' @@ -311,6 +283,8 @@ def main(): auth_password=dict(type='str', required=True, no_log=True), validate_certs=dict(type='bool', default=True), group_name=dict(type='str', required=True), + users=dict(type='list'), + connections=dict(type='list'), state=dict(type='str', choices=['absent', 'present'], default='present') # parent_group=dict(type='str', default='ROOT'), # group_type=dict(type='str', choices=['ORGANIZATIONAL', 'BALANCING'], default='ORGANIZATIONAL'), @@ -338,21 +312,8 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - # if module.params.get('parent_group') != "ROOT": - # try: - # module.params['parent_group'] = guacamole_get_connections_group_id( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # group=module.params.get('parent_group'), - # auth_token=guacamole_token['authToken'], - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # Get existing guacamole connections groups before doing anything else try: - guacamole_users_groups_before = guacamole_get_users_groups( + groups_before = guacamole_get_users_groups( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], @@ -361,6 +322,8 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + # module.fail_json(msg=guacamole_users_groups_before) + # try: # group_permissions = guacamole_get_users_group_permissions( # base_url=module.params.get('base_url'), @@ -372,31 +335,44 @@ def main(): # except GuacamoleError as e: # module.fail_json(msg=str(e)) - try: - guacamole_add_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) + # module arg state=present so we have to create a new group + if module.params.get('state') == 'present': - try: - guacamole_delete_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name="group_test", - # group_name=module.params.get('group_name'), - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) + if module.params.get('group_name') not in groups_before: + + try: + guacamole_add_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + result['changed'] = True + + + if module.params.get('state') == 'absent': + + if module.params.get('group_name') in groups_before: + + try: + guacamole_delete_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + result['changed'] = True #module.fail_json(msg=group_permissions) - module.fail_json(msg=guacamole_users_groups_before) + # module.fail_json(msg=guacamole_users_groups_before) # # check if the connections group already exists # # If the connections group exists we get the numeric id From 58010c1fbe2f5b5f1927c59a0a9d24e96aa81909 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Mon, 2 Nov 2020 23:08:17 +0100 Subject: [PATCH 03/32] first version adding connections to groups --- plugins/modules/guacamole_users_group.py | 62 ++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 843e312..535d487 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -231,11 +231,11 @@ def guacamole_update_users_in_group(base_url, validate_certs, datasource, auth_t url_update_users_in_group = URL_UPDATE_USERS_IN_GROUP.format( url=base_url, datasource=datasource, token=auth_token, group_name=group_name) - payload = { + payload = [{ "op": action, "path": '/', "value": user, - } + }] try: headers = {'Content-Type': 'application/json'} @@ -258,11 +258,11 @@ def guacamole_update_connections_in_group(base_url, validate_certs, datasource, url_update_connections_in_group = URL_UPDATE_CONNECTIONS_IN_GROUP.format( url=base_url, datasource=datasource, token=auth_token, group_name=group_name) - payload = { + payload = [{ "op": action, "path": '/connectionPermissions/%s' % connection_id, - "value": 'READ', - } + "value": 'READ' + }] try: headers = {'Content-Type': 'application/json'} @@ -338,6 +338,7 @@ def main(): # module arg state=present so we have to create a new group if module.params.get('state') == 'present': + # if the group doesn't exists we add it if module.params.get('group_name') not in groups_before: try: @@ -353,6 +354,57 @@ def main(): result['changed'] = True + # if the group already exists we only add the connections and users + else: + + # query exiting connections in guacamole + try: + guacamole_existing_connections = guacamole_get_connections( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + group='ROOT', + auth_token=guacamole_token['authToken'], + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + # add the connections to the grouop + if module.params.get('connections'): + for connection in module.params.get('connections'): + for c in guacamole_existing_connections: + if c['name'] == connection: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + connection_id=c['identifier'], + action='add', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + else: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + connection_id=c['identifier'], + action='remove', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + # if the connection doesn't exists we exit with an error + # else: + # module.fail_json(msg="%s '%s'" % (c['name'], connection)) + # # module.fail_json(msg="Cannot find a conection named '%s'" % connection) + if module.params.get('state') == 'absent': From 2a47b0b21804e379ac645ccdd57d2872f43d1c96 Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 11 Apr 2024 16:31:22 -0400 Subject: [PATCH 04/32] looks like it is working --- plugins/modules/guacamole_users_group.py | 29 ++++++++---------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 535d487..f39f090 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -20,7 +20,7 @@ DOCUMENTATION = ''' --- -module: guacamole_connections_group +module: guacamole_users_group short_description: Administer guacamole connections groups using the rest API @@ -61,12 +61,6 @@ required: true type: str - connections: - description: - - List of connections in this group - type: list - elements: str - users: description: - List of users in this group @@ -94,9 +88,6 @@ auth_username: guacadmin auth_password: guacadmin group_name: lab_3 - connections: - - rdp_lab_1 - - vnc_lab_1 users: - john - laura @@ -428,11 +419,11 @@ def main(): # # check if the connections group already exists # # If the connections group exists we get the numeric id - # guacamole_connections_group_exists = False - # for group_id, group_info in guacamole_connections_groups_before.items(): + # guacamole_users_group_exists = False + # for group_id, group_info in guacamole_users_groups_before.items(): # if group_info['name'] == module.params.get('group_name'): # group_numeric_id = group_info['identifier'] - # guacamole_connections_group_exists = True + # guacamole_users_group_exists = True # break # # module arg state=present so we have to create a new connections group @@ -444,7 +435,7 @@ def main(): # payload = guacamole_populate_connections_group_payload(module.params) # # the group already exists so we update it - # if guacamole_connections_group_exists: + # if guacamole_users_group_exists: # try: # guacamole_update_connections_group( @@ -478,7 +469,7 @@ def main(): # if module.params.get('state') == 'absent': # # the group exists so we delete it - # if guacamole_connections_group_exists: + # if guacamole_users_group_exists: # # if force_deletion=true we delete the group without any extra check # if module.params.get('force_deletion'): @@ -538,7 +529,7 @@ def main(): # # Get existing guacamole connections groups AFTER to check if something changed # try: - # guacamole_connections_groups_after = guacamole_get_connections_groups( + # guacamole_users_groups_after = guacamole_get_connections_groups( # base_url=module.params.get('base_url'), # validate_certs=module.params.get('validate_certs'), # datasource=guacamole_token['dataSource'], @@ -548,17 +539,17 @@ def main(): # module.fail_json(msg=str(e)) # # check if something changed (idempotence) - # if guacamole_connections_groups_before != guacamole_connections_groups_after: + # if guacamole_users_groups_before != guacamole_users_groups_after: # result['changed'] = True # # return connections_group_info{} for the added/updated/deleted connections group # if module.params.get('state') == 'present': - # for group_id, group_info in guacamole_connections_groups_after.items(): + # for group_id, group_info in guacamole_users_groups_after.items(): # if group_info['name'] == module.params.get('group_name'): # result['connections_group_info'] = group_info # break # else: - # for group_id, group_info in guacamole_connections_groups_before.items(): + # for group_id, group_info in guacamole_users_groups_before.items(): # if group_info['name'] == module.params.get('group_name'): # result['connections_group_info'] = group_info # break From 657e999e36d1a748dc37a828f7e2dc7ebf21ba43 Mon Sep 17 00:00:00 2001 From: gbischof Date: Wed, 1 May 2024 13:43:16 -0400 Subject: [PATCH 05/32] add read_only parameter when creating connection --- plugins/modules/guacamole_connection.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/modules/guacamole_connection.py b/plugins/modules/guacamole_connection.py index 08cb24b..73ff05a 100644 --- a/plugins/modules/guacamole_connection.py +++ b/plugins/modules/guacamole_connection.py @@ -248,6 +248,11 @@ - Should we enable sftp transfers for this connection? type: bool + read_only: + description: + - True if connection should be read-only. + type: bool + sftp_port: description: - Port to use for sftp @@ -329,6 +334,7 @@ username: vnc_user password: vnc_pass sftp_enable: true + read_only: false sftp_port: 22 sftp_hostname: 192.168.11.11 sftp_server_alive_interval: 10 @@ -410,6 +416,7 @@ def guacamole_populate_connection_payload(module_params): "parameters": { "enable-sftp": module_params['sftp_enable'], "sftp-directory": module_params['sftp_default_upload_directory'], + "read-only": module_params['read_only'] }, "attributes": { "guacd-encryption": module_params['guacd_encryption'], @@ -440,7 +447,8 @@ def guacamole_populate_connection_payload(module_params): "sftp_root_directory", "disable_copy", "disable_paste", - "cursor" + "cursor", + "read_only" ) guacamole_add_parameter(payload, module_params, parameters) @@ -590,6 +598,7 @@ def main(): guacd_hostname=dict(type='str', required=False), guacd_port=dict(type='int', required=False), guacd_encryption=dict(type='str', required=False), + read_only=dict(type='bool', default=False), ) result = dict(changed=False, msg='', diff={}, From 0a4c8c628f2afc9472e270501c6386c3e8510756 Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 21 May 2024 16:59:08 -0400 Subject: [PATCH 06/32] touchups --- plugins/modules/guacamole_users_group.py | 142 +---------------------- 1 file changed, 1 insertion(+), 141 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index f39f090..e249c41 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -360,7 +360,7 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - # add the connections to the grouop + # add the connections to the group if module.params.get('connections'): for connection in module.params.get('connections'): for c in guacamole_existing_connections: @@ -414,146 +414,6 @@ def main(): result['changed'] = True - #module.fail_json(msg=group_permissions) - # module.fail_json(msg=guacamole_users_groups_before) - - # # check if the connections group already exists - # # If the connections group exists we get the numeric id - # guacamole_users_group_exists = False - # for group_id, group_info in guacamole_users_groups_before.items(): - # if group_info['name'] == module.params.get('group_name'): - # group_numeric_id = group_info['identifier'] - # guacamole_users_group_exists = True - # break - - # # module arg state=present so we have to create a new connections group - # # or update an existing one - # if module.params.get('state') == 'present': - - # # populate the payload(json) with the group info that we - # # will send to the API - # payload = guacamole_populate_connections_group_payload(module.params) - - # # the group already exists so we update it - # if guacamole_users_group_exists: - - # try: - # guacamole_update_connections_group( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # group_numeric_id=group_numeric_id, - # payload=payload - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # # if the group doesn't exists we add it - # else: - - # try: - # guacamole_add_connections_group( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # payload=payload - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # result['msg'] = "Connections group '%s' added" % module.params.get('group_name') - - # # module arg state=absent so we have to delete connections group - # if module.params.get('state') == 'absent': - - # # the group exists so we delete it - # if guacamole_users_group_exists: - - # # if force_deletion=true we delete the group without any extra check - # if module.params.get('force_deletion'): - - # try: - # guacamole_delete_connections_group( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # group_numeric_id=group_numeric_id - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # # if we are here it's because the group exists and force_deletion=false - # else: - - # # Query all the existing guacamole connections in this group - # # to verify if the group we want to delete has any child connection - # try: - # connections_in_group = guacamole_get_connections( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # group=group_numeric_id, - # auth_token=guacamole_token['authToken'], - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # # if the group is empty (no child connections) we delete it - # if not connections_in_group: - - # try: - # guacamole_delete_connections_group( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # group_numeric_id=group_numeric_id - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # # if the group has child connections and force_deletion=false fail and exit - # else: - # module.fail_json( - # msg="Won't delete a group with child connections unless force_deletion=True" - # ) - - # # if the group to delete doesn't exists we just print a message - # else: - - # result['msg'] = "Connections group '%s' doesn't exists. Not doing anything" \ - # % (module.params.get('group_name')) - - # # Get existing guacamole connections groups AFTER to check if something changed - # try: - # guacamole_users_groups_after = guacamole_get_connections_groups( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) - - # # check if something changed (idempotence) - # if guacamole_users_groups_before != guacamole_users_groups_after: - # result['changed'] = True - - # # return connections_group_info{} for the added/updated/deleted connections group - # if module.params.get('state') == 'present': - # for group_id, group_info in guacamole_users_groups_after.items(): - # if group_info['name'] == module.params.get('group_name'): - # result['connections_group_info'] = group_info - # break - # else: - # for group_id, group_info in guacamole_users_groups_before.items(): - # if group_info['name'] == module.params.get('group_name'): - # result['connections_group_info'] = group_info - # break - module.exit_json(**result) From d6e21231fdccaee7a2df24df13c7a24cf0f4c308 Mon Sep 17 00:00:00 2001 From: gbischof Date: Fri, 24 May 2024 17:09:05 -0400 Subject: [PATCH 07/32] working on updating guacamole_users_groups to take a permissions dictionary --- plugins/modules/guacamole_users_group.py | 125 ++++++++++------------- 1 file changed, 54 insertions(+), 71 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index e249c41..222503e 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -273,16 +273,8 @@ def main(): auth_username=dict(type='str', required=True), auth_password=dict(type='str', required=True, no_log=True), validate_certs=dict(type='bool', default=True), - group_name=dict(type='str', required=True), - users=dict(type='list'), - connections=dict(type='list'), - state=dict(type='str', choices=['absent', 'present'], default='present') - # parent_group=dict(type='str', default='ROOT'), - # group_type=dict(type='str', choices=['ORGANIZATIONAL', 'BALANCING'], default='ORGANIZATIONAL'), - # max_connections=dict(type='int'), - # max_connections_per_user=dict(type='int'), - # enable_session_affinity=dict(type='bool'), - # force_deletion=dict(type='bool', default=False) + permissions=dict(type='dict', default={}), + state=dict(type='str', choices=['absent', 'present', 'sync'], default='present') ) result = dict(changed=False, msg='', users_group_info={}) @@ -325,30 +317,28 @@ def main(): # ) # except GuacamoleError as e: # module.fail_json(msg=str(e)) - + permissions = module.params.get('permissions') # module arg state=present so we have to create a new group - if module.params.get('state') == 'present': - - # if the group doesn't exists we add it - if module.params.get('group_name') not in groups_before: - - try: - guacamole_add_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) - - result['changed'] = True - - # if the group already exists we only add the connections and users - else: - - # query exiting connections in guacamole + if module.params.get('state') in {'present', 'sync'}: + for group_name, connections in permissions.items(): + + # if the group doesn't exists we add it + if group_name not in groups_before: + + try: + guacamole_add_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name, + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + result['changed'] = True + + # Get a list of the existing connections. try: guacamole_existing_connections = guacamole_get_connections( base_url=module.params.get('base_url'), @@ -360,44 +350,24 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - # add the connections to the group - if module.params.get('connections'): - for connection in module.params.get('connections'): - for c in guacamole_existing_connections: - if c['name'] == connection: - try: - guacamole_update_connections_in_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), - connection_id=c['identifier'], - action='add', - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) - else: - try: - guacamole_update_connections_in_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), - connection_id=c['identifier'], - action='remove', - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) - - # if the connection doesn't exists we exit with an error - # else: - # module.fail_json(msg="%s '%s'" % (c['name'], connection)) - # # module.fail_json(msg="Cannot find a conection named '%s'" % connection) - - - if module.params.get('state') == 'absent': + # Add the connections to the user group permissions. + new_connections = {connection['name'] for connection + in guacamole_existing_connections} & set(connections) + for connection in new_connections: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + connection_id=c['identifier'], + action='add', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + if module.params.get('state') in {'absent', 'sync'}: if module.params.get('group_name') in groups_before: @@ -413,6 +383,19 @@ def main(): module.fail_json(msg=str(e)) result['changed'] = True + #else: + # try: + # guacamole_update_connections_in_group( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # group_name=module.params.get('group_name'), + # connection_id=c['identifier'], + # action='remove', + # ) + # except GuacamoleError as e: + # module.fail_json(msg=str(e)) module.exit_json(**result) From e5f4d1bd231be5af2f70852c0e2556448b4405b4 Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 16:03:37 -0400 Subject: [PATCH 08/32] Pass at making present/sync work --- plugins/modules/guacamole_users_group.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 222503e..e131707 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -239,7 +239,7 @@ def guacamole_update_users_in_group(base_url, validate_certs, datasource, auth_t def guacamole_update_connections_in_group(base_url, validate_certs, datasource, auth_token, group_name, connection_id, action): """ - Add or remove a conection to a group. + Add or remove a connection to a group. Action must be "add" or "remove" """ @@ -351,17 +351,20 @@ def main(): module.fail_json(msg=str(e)) # Add the connections to the user group permissions. - new_connections = {connection['name'] for connection - in guacamole_existing_connections} & set(connections) - for connection in new_connections: + #new_connections = {connection['name'] for connection + # in guacamole_existing_connections} & set(connections) + connection_ids = {connection['identifier'] for connection + in guacamole_existing_connections if + connection['name'] in set(connections)} + for connection_id in connection_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), - connection_id=c['identifier'], + group_name=group_name, + connection_id=connection_id, action='add', ) except GuacamoleError as e: From e88865f5cf04049bb08c4564d2889da4e0b896fb Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 16:43:34 -0400 Subject: [PATCH 09/32] Adding connections groups to users_group logic --- plugins/modules/guacamole_users_group.py | 44 +++++++++++++++--------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index e131707..649bad5 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -9,7 +9,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import open_url from ansible_collections.scicore.guacamole.plugins.module_utils.guacamole import GuacamoleError, \ - guacamole_get_token, guacamole_get_connections, guacamole_get_connections_group_id + guacamole_get_token, guacamole_get_connections, guacamole_get_connections_groups __metaclass__ = type ANSIBLE_METADATA = { @@ -319,12 +319,33 @@ def main(): # module.fail_json(msg=str(e)) permissions = module.params.get('permissions') # module arg state=present so we have to create a new group + + # Get a list of the existing connections. + try: + guacamole_existing_connections = guacamole_get_connections( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + group='ROOT', + auth_token=guacamole_token['authToken'], + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + try: + connections_groups = guacamole_get_connections_groups( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): - # if the group doesn't exists we add it if group_name not in groups_before: - try: guacamole_add_group( base_url=module.params.get('base_url'), @@ -338,25 +359,16 @@ def main(): result['changed'] = True - # Get a list of the existing connections. - try: - guacamole_existing_connections = guacamole_get_connections( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - group='ROOT', - auth_token=guacamole_token['authToken'], - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) - # Add the connections to the user group permissions. #new_connections = {connection['name'] for connection # in guacamole_existing_connections} & set(connections) connection_ids = {connection['identifier'] for connection in guacamole_existing_connections if connection['name'] in set(connections)} - for connection_id in connection_ids: + group_ids = {connection['identifier'] for connection + in connections_groups if + connection['name'] in set(connections)} + for connection_id in connection_ids | group_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), From 86012c022ffa88a01724ff87d2daef34aff18c14 Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 16:47:57 -0400 Subject: [PATCH 10/32] Adding connections groups to users_group logic p2 --- plugins/modules/guacamole_users_group.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 649bad5..55af7a4 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -342,6 +342,8 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + result['groups'] = connections_groups + if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): # if the group doesn't exists we add it @@ -365,10 +367,10 @@ def main(): connection_ids = {connection['identifier'] for connection in guacamole_existing_connections if connection['name'] in set(connections)} - group_ids = {connection['identifier'] for connection - in connections_groups if - connection['name'] in set(connections)} - for connection_id in connection_ids | group_ids: + #group_ids = {connection['identifier'] for connection + # in connections_groups if + # connection['name'] in set(connections)} + for connection_id in connection_ids:# | group_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), From d50ebca4ea607f6124bc3629cffde4c966009fea Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 16:54:13 -0400 Subject: [PATCH 11/32] Adding connections groups to users_group logic p3 --- plugins/modules/guacamole_users_group.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 55af7a4..d5e5b2c 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -367,10 +367,10 @@ def main(): connection_ids = {connection['identifier'] for connection in guacamole_existing_connections if connection['name'] in set(connections)} - #group_ids = {connection['identifier'] for connection - # in connections_groups if - # connection['name'] in set(connections)} - for connection_id in connection_ids:# | group_ids: + group_ids = {connection['identifier'] for connection + in connections_groups.values() if + connection['name'] in set(connections)} + for connection_id in connection_ids: | group_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), From 0c1d323a3101144feca00b920e53ffad34bb08cd Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 16:55:14 -0400 Subject: [PATCH 12/32] Adding connections groups to users_group logic p4 --- plugins/modules/guacamole_users_group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index d5e5b2c..63440c4 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -342,7 +342,7 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - result['groups'] = connections_groups + #result['groups'] = connections_groups if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): @@ -370,7 +370,7 @@ def main(): group_ids = {connection['identifier'] for connection in connections_groups.values() if connection['name'] in set(connections)} - for connection_id in connection_ids: | group_ids: + for connection_id in connection_ids | group_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), From 9dde1b5cdefcbf2647174014fd24bed9f116cbc0 Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 16:59:42 -0400 Subject: [PATCH 13/32] Adding connections groups to users_group logic p5 --- plugins/modules/guacamole_users_group.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 63440c4..31f49f7 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -370,6 +370,8 @@ def main(): group_ids = {connection['identifier'] for connection in connections_groups.values() if connection['name'] in set(connections)} + if group_ids: + result['group'] = group_ids for connection_id in connection_ids | group_ids: try: guacamole_update_connections_in_group( From 808eb7c11358a5bcd3a86d66b14af807dd35e277 Mon Sep 17 00:00:00 2001 From: Robert Schaffer Date: Wed, 29 May 2024 17:06:20 -0400 Subject: [PATCH 14/32] Fixed users group permissions for connections groups (not needed) --- plugins/modules/guacamole_users_group.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 31f49f7..88d4e2c 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -342,8 +342,6 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - #result['groups'] = connections_groups - if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): # if the group doesn't exists we add it @@ -362,17 +360,10 @@ def main(): result['changed'] = True # Add the connections to the user group permissions. - #new_connections = {connection['name'] for connection - # in guacamole_existing_connections} & set(connections) connection_ids = {connection['identifier'] for connection in guacamole_existing_connections if connection['name'] in set(connections)} - group_ids = {connection['identifier'] for connection - in connections_groups.values() if - connection['name'] in set(connections)} - if group_ids: - result['group'] = group_ids - for connection_id in connection_ids | group_ids: + for connection_id in connection_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), From 3883d81a6e2bad0f9570840be98604272c266198 Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 16:14:40 -0400 Subject: [PATCH 15/32] add code for remove user groups and connections --- plugins/modules/guacamole_users_group.py | 56 ++++++++++++++++-------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 88d4e2c..d98bf54 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -360,10 +360,10 @@ def main(): result['changed'] = True # Add the connections to the user group permissions. - connection_ids = {connection['identifier'] for connection - in guacamole_existing_connections if - connection['name'] in set(connections)} - for connection_id in connection_ids: + new_connection_ids = {connection['identifier'] for connection + in guacamole_existing_connections if + connection['name'] in set(connections)} + for connection_id in new_connection_ids: try: guacamole_update_connections_in_group( base_url=module.params.get('base_url'), @@ -379,33 +379,51 @@ def main(): if module.params.get('state') in {'absent', 'sync'}: - if module.params.get('group_name') in groups_before: + # Remove user groups. + if module.params.get('state') == 'absent': + remove_groups = permissions.keys() + else: + remove_groups = set(groups_before) - set(permissions.keys()) + for remove_group in remove_groups: try: guacamole_delete_group( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), + group_name=remove_group, ) except GuacamoleError as e: module.fail_json(msg=str(e)) result['changed'] = True - #else: - # try: - # guacamole_update_connections_in_group( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # group_name=module.params.get('group_name'), - # connection_id=c['identifier'], - # action='remove', - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) + + for group_name, connections in permissions.items() + # Remove connections. + if module.params.get('state') == 'absent': + remove_groups = set(groups_before) - set(permissions.keys()) + else: + remove_connections = guacamole_get_users_group_permissions( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name) + result['remove_connections'] = remove_connections + + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=module.params.get('group_name'), + connection_id=c['identifier'], + action='remove', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) module.exit_json(**result) From cbbdde54cbd90811401de02b75edb243d183d994 Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 16:19:12 -0400 Subject: [PATCH 16/32] missing : --- plugins/modules/guacamole_users_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index d98bf54..b63da94 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -399,7 +399,7 @@ def main(): result['changed'] = True - for group_name, connections in permissions.items() + for group_name, connections in permissions.items(): # Remove connections. if module.params.get('state') == 'absent': remove_groups = set(groups_before) - set(permissions.keys()) From 09fb0bcd31d9599c8c43d0008c185d55a050bd84 Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 16:27:38 -0400 Subject: [PATCH 17/32] debugging --- plugins/modules/guacamole_users_group.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index b63da94..1eef133 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -360,8 +360,20 @@ def main(): result['changed'] = True # Add the connections to the user group permissions. + try: + existing_group_connections = guacamole_get_users_group_permissions( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name + ) + result[f'existing_group_connections_{group_name}'] = existing_group_connections + except GuacamoleError as e: + module.fail_json(msg=str(e)) + new_connection_ids = {connection['identifier'] for connection - in guacamole_existing_connections if + in existing_group_connections if connection['name'] in set(connections)} for connection_id in new_connection_ids: try: From 990c628fab3eeb0e0dcca52d865f05416103893e Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 16:31:51 -0400 Subject: [PATCH 18/32] debugging --- plugins/modules/guacamole_users_group.py | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 1eef133..2c87ba1 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -372,22 +372,22 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - new_connection_ids = {connection['identifier'] for connection - in existing_group_connections if - connection['name'] in set(connections)} - for connection_id in new_connection_ids: - try: - guacamole_update_connections_in_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=group_name, - connection_id=connection_id, - action='add', - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) +# new_connection_ids = {connection['identifier'] for connection +# in existing_group_connections if +# connection['name'] in set(connections)} +# for connection_id in new_connection_ids: +# try: +# guacamole_update_connections_in_group( +# base_url=module.params.get('base_url'), +# validate_certs=module.params.get('validate_certs'), +# datasource=guacamole_token['dataSource'], +# auth_token=guacamole_token['authToken'], +# group_name=group_name, +# connection_id=connection_id, +# action='add', +# ) +# except GuacamoleError as e: +# module.fail_json(msg=str(e)) if module.params.get('state') in {'absent', 'sync'}: From 54655366e88d491d2807b9cabb35fe0aad30ba5c Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 16:48:14 -0400 Subject: [PATCH 19/32] some progress --- plugins/modules/guacamole_users_group.py | 41 ++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 2c87ba1..6a9545a 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -344,7 +344,8 @@ def main(): if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): - # if the group doesn't exists we add it + + # If the group doesn't exists we add it. if group_name not in groups_before: try: guacamole_add_group( @@ -360,34 +361,34 @@ def main(): result['changed'] = True # Add the connections to the user group permissions. + # Check the existing connections for the user group. try: - existing_group_connections = guacamole_get_users_group_permissions( + existing_group_connection_ids = set(guacamole_get_users_group_permissions( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], group_name=group_name - ) - result[f'existing_group_connections_{group_name}'] = existing_group_connections + )['connectionPermissions'].keys()) except GuacamoleError as e: module.fail_json(msg=str(e)) -# new_connection_ids = {connection['identifier'] for connection -# in existing_group_connections if -# connection['name'] in set(connections)} -# for connection_id in new_connection_ids: -# try: -# guacamole_update_connections_in_group( -# base_url=module.params.get('base_url'), -# validate_certs=module.params.get('validate_certs'), -# datasource=guacamole_token['dataSource'], -# auth_token=guacamole_token['authToken'], -# group_name=group_name, -# connection_id=connection_id, -# action='add', -# ) -# except GuacamoleError as e: -# module.fail_json(msg=str(e)) + group_connection_ids = {connection['identifier'] for connection + in guacamole_existing_connections if + connection['name'] in set(connections)} - existing_group_connection_ids + for connection_id in group_connection_ids: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name, + connection_id=connection_id, + action='add', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) if module.params.get('state') in {'absent', 'sync'}: From 2793c50b6104d482fd376cd990ba0327e1fa779e Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 17:04:34 -0400 Subject: [PATCH 20/32] more progress --- plugins/modules/guacamole_users_group.py | 48 ++++++++++++++---------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index 6a9545a..f609afc 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -392,12 +392,14 @@ def main(): if module.params.get('state') in {'absent', 'sync'}: - # Remove user groups. + # Determine which user groups should be deleted. if module.params.get('state') == 'absent': - remove_groups = permissions.keys() + remove_groups = {group for group, connections in permissions.items() + if not connections} else: remove_groups = set(groups_before) - set(permissions.keys()) + # Remove user groups. for remove_group in remove_groups: try: guacamole_delete_group( @@ -413,30 +415,36 @@ def main(): result['changed'] = True for group_name, connections in permissions.items(): - # Remove connections. + + connection_ids = {connection['identifier'] for connection + in guacamole_existing_connections if + connection['name'] in set(connections)} + + # Determine which connections should be remove from group permissions. if module.params.get('state') == 'absent': - remove_groups = set(groups_before) - set(permissions.keys()) + remove_connection_ids = connection_ids else: - remove_connections = guacamole_get_users_group_permissions( + remove_connection_ids = set(guacamole_get_users_group_permissions( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], - group_name=group_name) - result['remove_connections'] = remove_connections - - try: - guacamole_update_connections_in_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=module.params.get('group_name'), - connection_id=c['identifier'], - action='remove', - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) + group_name=group_name + )['connectionPermissions'].keys()) - connection_ids + + for remove_connection_id in remove_connection_ids: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name, + connection_id=remove_connection_id, + action='remove', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) module.exit_json(**result) From db5436a66886712a636fc39500fa6ec452d67533 Mon Sep 17 00:00:00 2001 From: gbischof Date: Thu, 30 May 2024 17:11:10 -0400 Subject: [PATCH 21/32] debug --- plugins/modules/guacamole_users_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_users_group.py index f609afc..5997096 100644 --- a/plugins/modules/guacamole_users_group.py +++ b/plugins/modules/guacamole_users_group.py @@ -184,7 +184,7 @@ def guacamole_delete_group(base_url, validate_certs, datasource, auth_token, gro headers = {'Content-Type': 'application/json'} open_url(url_delete_group, method='DELETE', validate_certs=validate_certs, headers=headers) except Exception as e: - raise GuacamoleError('Could not delete a users group. Error msg: %s' % str(e)) + raise GuacamoleError(f'Could not delete user group {group_name}. Error msg: {e}') def guacamole_get_users_group_permissions(base_url, validate_certs, datasource, auth_token, group_name): From f1eef85c7204b04be2df767b5fd6fc4065b60f08 Mon Sep 17 00:00:00 2001 From: gbischof Date: Fri, 31 May 2024 15:26:37 -0400 Subject: [PATCH 22/32] add guacamole_user_group_users --- plugins/modules/guacamole_user_group_users.py | 404 ++++++++++++++++++ ...sers_group.py => guacamole_user_groups.py} | 0 2 files changed, 404 insertions(+) create mode 100644 plugins/modules/guacamole_user_group_users.py rename plugins/modules/{guacamole_users_group.py => guacamole_user_groups.py} (100%) diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py new file mode 100644 index 0000000..e43523f --- /dev/null +++ b/plugins/modules/guacamole_user_group_users.py @@ -0,0 +1,404 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Pablo Escobar +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible_collections.scicore.guacamole.plugins.module_utils.guacamole import GuacamoleError, \ + guacamole_get_token, guacamole_get_connections, guacamole_get_connections_groups +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: guacamole_users_group + +short_description: Administer guacamole connections groups using the rest API + +version_added: "2.9" + +description: + - "Add or remove guacamole connections groups." + +options: + base_url: + description: + - Url to access the guacamole API + required: true + aliases: ['url'] + type: str + + auth_username: + description: + - Guacamole admin user to login to the API + required: true + type: str + + auth_password: + description: + - Guacamole admin user password to login to the API + required: true + type: str + + validate_certs: + description: + - Validate ssl certs? + default: true + type: bool + + group_name: + description: + - Group name to create + required: true + type: str + + users: + description: + - List of users in this group + type: list + elements: str + + state: + description: + - Create or delete the users group + default: 'present' + type: str + choices: + - present + - absent + +author: + - Pablo Escobar Lopez (@pescobar) +''' + +EXAMPLES = ''' + +- name: Create a new group "lab_3" + scicore.guacamole.guacamole_users_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + group_name: lab_3 + users: + - john + - laura + +- name: Delete users group "developers" + scicore.guacamole.guacamole_users_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + group_name: developers + state: absent +''' + +RETURN = ''' +group_info: + description: Information about the created or updated group + type: dict + returned: always +message: + description: Some extra info about what the module did + type: str + returned: always +''' + +URL_LIST_GROUPS = "{url}/api/session/data/{datasource}/userGroups?token={token}" +URL_ADD_GROUP = URL_LIST_GROUPS +URL_DELETE_GROUP = "{url}/api/session/data/{datasource}/userGroups/{group_name}?token={token}" +URL_GET_GROUP_PERMISSIONS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/permissions?token={token}" +URL_UPDATE_CONNECTIONS_IN_GROUP = URL_GET_GROUP_PERMISSIONS +URL_GET_GROUP_MEMBERS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/memberUsers?token={token}" +URL_UPDATE_USERS_IN_GROUP = URL_GET_GROUP_MEMBERS + + +def guacamole_get_users_groups(base_url, validate_certs, datasource, auth_token): + """ + Returns a dict of dicts. + Each dict provides the name and state (enabled/disabled) for each group of users + """ + + url_list_users_groups = URL_LIST_GROUPS.format( + url=base_url, datasource=datasource, token=auth_token) + + try: + users_groups = json.load(open_url(url_list_users_groups, method='GET', + validate_certs=validate_certs)) + except ValueError as e: + raise GuacamoleError( + 'API returned invalid JSON when trying to obtain users groups from %s: %s' + % (url_list_users_groups, str(e))) + except Exception as e: + raise GuacamoleError('Could not obtain users groups from %s: %s' + % (url_list_users_groups, str(e))) + + return users_groups + + +def guacamole_add_group(base_url, validate_certs, datasource, auth_token, group_name): + """ + Add a group of users + """ + + url_add_group = URL_ADD_GROUP.format( + url=base_url, datasource=datasource, token=auth_token) + + payload = { + "identifier": group_name, + "attributes": { + "disabled": "" + } + } + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_add_group, method='POST', validate_certs=validate_certs, headers=headers, + data=json.dumps(payload)) + except Exception as e: + # if the group exists we get a http code 400 + if e.code == 400: + pass + # raise GuacamoleError('Group %s already exists.' % group_name) + else: + raise GuacamoleError('Could not add a users group. Error msg: %s' % str(e)) + + +def guacamole_delete_group(base_url, validate_certs, datasource, auth_token, group_name): + """ + Delete a group of users + """ + + url_delete_group = URL_DELETE_GROUP.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_delete_group, method='DELETE', validate_certs=validate_certs, headers=headers) + except Exception as e: + raise GuacamoleError(f'Could not delete user group {group_name}. Error msg: {e}') + + +def guacamole_get_users_group_permissions(base_url, validate_certs, datasource, auth_token, group_name): + """ + Returns a dict of dicts. + Each dict provides the details for one of the users groups defined in guacamole + """ + + url_get_users_group_permissions = URL_GET_GROUP_PERMISSIONS.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + try: + group_permissions = json.load(open_url(url_get_users_group_permissions, method='GET', + validate_certs=validate_certs)) + except ValueError as e: + raise GuacamoleError( + 'API returned invalid JSON when trying to obtain group permissions from %s: %s' + % (url_get_users_group_permissions, str(e))) + except Exception as e: + raise GuacamoleError('Could not obtain group permissions from %s: %s' + % (url_get_users_group_permissions, str(e))) + + return group_permissions + + +def guacamole_update_users_in_group(base_url, validate_certs, datasource, auth_token, group_name, user, action): + """ + Add or remove a user to a group. + Action must be "add" or "remove" + """ + + if action not in ['add', 'remove']: + raise GuacamoleError("action must be 'add' or 'remove'") + + url_update_users_in_group = URL_UPDATE_USERS_IN_GROUP.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + payload = [{ + "op": action, + "path": '/', + "value": user, + }] + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_update_users_in_group, method='PATCH', validate_certs=validate_certs, headers=headers, + data=json.dumps(payload)) + except Exception as e: + raise GuacamoleError('Could not update users for group %s in url %s. Error msg: %s' + % (group_name, url_update_users_in_group, str(e))) + + +def guacamole_update_connections_in_group(base_url, validate_certs, datasource, auth_token, group_name, connection_id, action): + """ + Add or remove a connection to a group. + Action must be "add" or "remove" + """ + + if action not in ['add', 'remove']: + raise GuacamoleError("action must be 'add' or 'remove'") + + url_update_connections_in_group = URL_UPDATE_CONNECTIONS_IN_GROUP.format( + url=base_url, datasource=datasource, token=auth_token, group_name=group_name) + + payload = [{ + "op": action, + "path": '/connectionPermissions/%s' % connection_id, + "value": 'READ' + }] + + try: + headers = {'Content-Type': 'application/json'} + open_url(url_update_connections_in_group, method='PATCH', validate_certs=validate_certs, headers=headers, + data=json.dumps(payload)) + except Exception as e: + raise GuacamoleError('Could not update connections for group %s in url %s. Error msg: %s' + % (group_name, url_update_connections_in_group, str(e))) + + +def main(): + + # define the available arguments/parameters that a user can pass to + # the module + module_args = dict( + base_url=dict(type='str', aliases=['url'], required=True), + auth_username=dict(type='str', required=True), + auth_password=dict(type='str', required=True, no_log=True), + validate_certs=dict(type='bool', default=True), + users=dict(type='dict', default={}), + state=dict(type='str', choices=['absent', 'present', 'sync'], default='present') + ) + + result = dict(changed=False, msg='', users_group_info={}) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=False + ) + + # Obtain access token, initialize API + try: + guacamole_token = guacamole_get_token( + base_url=module.params.get('base_url'), + auth_username=module.params.get('auth_username'), + auth_password=module.params.get('auth_password'), + validate_certs=module.params.get('validate_certs'), + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + users = module.params.get('users') + + if module.params.get('state') in {'present', 'sync'}: + for group_name, users in users.items(): + + # Add the users to the user group permissions. + # Check the existing users for the user group. + try: + #existing_group_connection_ids = set(guacamole_get_users_group_permissions( + # base_url=module.params.get('base_url'), + # validate_certs=module.params.get('validate_certs'), + # datasource=guacamole_token['dataSource'], + # auth_token=guacamole_token['authToken'], + # group_name=group_name + #)['connectionPermissions'].keys()) + user_group_permissions = guacamole_get_users_group_permissions( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + result['user_group_permissions'] = user_group_permissions + + module.fail_json(msg=str(e)) + + group_connection_ids = {connection['identifier'] for connection + in guacamole_existing_connections if + connection['name'] in set(connections)} - existing_group_connection_ids + for connection_id in group_connection_ids: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name, + connection_id=connection_id, + action='add', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + if module.params.get('state') in {'absent', 'sync'}: + + # Determine which user groups should be deleted. + if module.params.get('state') == 'absent': + remove_groups = {group for group, connections in permissions.items() + if not connections} + else: + remove_groups = set(groups_before) - set(permissions.keys()) + + # Remove user groups. + for remove_group in remove_groups: + try: + guacamole_delete_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=remove_group, + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + result['changed'] = True + + for group_name, connections in permissions.items(): + + connection_ids = {connection['identifier'] for connection + in guacamole_existing_connections if + connection['name'] in set(connections)} + + # Determine which connections should be remove from group permissions. + if module.params.get('state') == 'absent': + remove_connection_ids = connection_ids + else: + remove_connection_ids = set(guacamole_get_users_group_permissions( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name + )['connectionPermissions'].keys()) - connection_ids + + for remove_connection_id in remove_connection_ids: + try: + guacamole_update_connections_in_group( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name, + connection_id=remove_connection_id, + action='remove', + ) + except GuacamoleError as e: + module.fail_json(msg=str(e)) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/guacamole_users_group.py b/plugins/modules/guacamole_user_groups.py similarity index 100% rename from plugins/modules/guacamole_users_group.py rename to plugins/modules/guacamole_user_groups.py From b17ca55acbcf52f66e63846f9ea9c8ca90f20043 Mon Sep 17 00:00:00 2001 From: gbischof Date: Fri, 31 May 2024 15:47:24 -0400 Subject: [PATCH 23/32] progress --- plugins/modules/guacamole_user_group_users.py | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py index e43523f..0e25fc1 100644 --- a/plugins/modules/guacamole_user_group_users.py +++ b/plugins/modules/guacamole_user_group_users.py @@ -187,13 +187,13 @@ def guacamole_delete_group(base_url, validate_certs, datasource, auth_token, gro raise GuacamoleError(f'Could not delete user group {group_name}. Error msg: {e}') -def guacamole_get_users_group_permissions(base_url, validate_certs, datasource, auth_token, group_name): +def guacamole_get_user_group_users(base_url, validate_certs, datasource, auth_token, group_name): """ Returns a dict of dicts. Each dict provides the details for one of the users groups defined in guacamole """ - url_get_users_group_permissions = URL_GET_GROUP_PERMISSIONS.format( + url_get_users_group_permissions = URL_GET_GROUP_MEMBERS.format( url=base_url, datasource=datasource, token=auth_token, group_name=group_name) try: @@ -303,43 +303,33 @@ def main(): # Add the users to the user group permissions. # Check the existing users for the user group. try: - #existing_group_connection_ids = set(guacamole_get_users_group_permissions( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # group_name=group_name - #)['connectionPermissions'].keys()) - user_group_permissions = guacamole_get_users_group_permissions( + existing_users = set(guacamole_get_user_group_users( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], group_name=group_name - ) + )) except GuacamoleError as e: module.fail_json(msg=str(e)) - result['user_group_permissions'] = user_group_permissions - - module.fail_json(msg=str(e)) + new_users = users - existing_users - group_connection_ids = {connection['identifier'] for connection - in guacamole_existing_connections if - connection['name'] in set(connections)} - existing_group_connection_ids - for connection_id in group_connection_ids: + for new_user in new_users: try: - guacamole_update_connections_in_group( + guacamole_update_users_in_group( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], group_name=group_name, - connection_id=connection_id, + user=new_user, action='add', ) except GuacamoleError as e: - module.fail_json(msg=str(e)) + module.fail_json(msg=str(e)) + + module.exit_json(**result) if module.params.get('state') in {'absent', 'sync'}: From 9a5cae74d868bd321f4e0acdeefe0df921924ed4 Mon Sep 17 00:00:00 2001 From: gbischof Date: Fri, 31 May 2024 15:48:46 -0400 Subject: [PATCH 24/32] progress --- plugins/modules/guacamole_user_group_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py index 0e25fc1..b6ab7ef 100644 --- a/plugins/modules/guacamole_user_group_users.py +++ b/plugins/modules/guacamole_user_group_users.py @@ -313,7 +313,7 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - new_users = users - existing_users + new_users = set(users) - existing_users for new_user in new_users: try: From f1493f1653a3071c661e135fc6054ff118dbda8d Mon Sep 17 00:00:00 2001 From: gbischof Date: Fri, 31 May 2024 15:58:01 -0400 Subject: [PATCH 25/32] progress --- plugins/modules/guacamole_user_group_users.py | 57 ++++++------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py index b6ab7ef..3a8f48e 100644 --- a/plugins/modules/guacamole_user_group_users.py +++ b/plugins/modules/guacamole_user_group_users.py @@ -329,64 +329,39 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - module.exit_json(**result) + result['changed'] = True if module.params.get('state') in {'absent', 'sync'}: - # Determine which user groups should be deleted. - if module.params.get('state') == 'absent': - remove_groups = {group for group, connections in permissions.items() - if not connections} - else: - remove_groups = set(groups_before) - set(permissions.keys()) - - # Remove user groups. - for remove_group in remove_groups: - try: - guacamole_delete_group( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=remove_group, - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) - - result['changed'] = True - - for group_name, connections in permissions.items(): - - connection_ids = {connection['identifier'] for connection - in guacamole_existing_connections if - connection['name'] in set(connections)} - - # Determine which connections should be remove from group permissions. + # Remove users from user group. + for group_name, users in users.items(): + existing_users = set(guacamole_get_user_group_users( + base_url=module.params.get('base_url'), + validate_certs=module.params.get('validate_certs'), + datasource=guacamole_token['dataSource'], + auth_token=guacamole_token['authToken'], + group_name=group_name + )) if module.params.get('state') == 'absent': - remove_connection_ids = connection_ids + remove_users = set(users) & existing_users else: - remove_connection_ids = set(guacamole_get_users_group_permissions( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - group_name=group_name - )['connectionPermissions'].keys()) - connection_ids + remove_users = existing_users - users - for remove_connection_id in remove_connection_ids: + for remove_user in remove_users: try: - guacamole_update_connections_in_group( + guacamole_update_users_in_group( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), datasource=guacamole_token['dataSource'], auth_token=guacamole_token['authToken'], group_name=group_name, - connection_id=remove_connection_id, + user=remove_user, action='remove', ) except GuacamoleError as e: module.fail_json(msg=str(e)) + result['changed'] = True module.exit_json(**result) From b626f48c9300860ee1c63d54dea1fcc5e41e55fe Mon Sep 17 00:00:00 2001 From: gbischof Date: Fri, 31 May 2024 16:02:21 -0400 Subject: [PATCH 26/32] progress --- plugins/modules/guacamole_user_group_users.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py index 3a8f48e..20c9265 100644 --- a/plugins/modules/guacamole_user_group_users.py +++ b/plugins/modules/guacamole_user_group_users.py @@ -298,7 +298,7 @@ def main(): users = module.params.get('users') if module.params.get('state') in {'present', 'sync'}: - for group_name, users in users.items(): + for group_name, usernames in users.items(): # Add the users to the user group permissions. # Check the existing users for the user group. @@ -313,7 +313,7 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - new_users = set(users) - existing_users + new_users = set(usernames) - existing_users for new_user in new_users: try: @@ -334,7 +334,7 @@ def main(): if module.params.get('state') in {'absent', 'sync'}: # Remove users from user group. - for group_name, users in users.items(): + for group_name, usernames in users.items(): existing_users = set(guacamole_get_user_group_users( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), @@ -343,9 +343,9 @@ def main(): group_name=group_name )) if module.params.get('state') == 'absent': - remove_users = set(users) & existing_users + remove_users = set(usernames) & existing_users else: - remove_users = existing_users - users + remove_users = existing_users - set(usernames) for remove_user in remove_users: try: From d6e944f09773325a2dfa2805592ce464b7f4b34d Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 4 Jun 2024 13:30:36 -0400 Subject: [PATCH 27/32] flake8 fixes --- plugins/modules/guacamole_connections_group.py | 7 +++---- plugins/modules/guacamole_user_group_users.py | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/modules/guacamole_connections_group.py b/plugins/modules/guacamole_connections_group.py index 85ee750..211e069 100644 --- a/plugins/modules/guacamole_connections_group.py +++ b/plugins/modules/guacamole_connections_group.py @@ -154,7 +154,6 @@ URL_DELETE_CONNECTIONS_GROUP = URL_UPDATE_CONNECTIONS_GROUP - def guacamole_populate_connections_group_payload(module_params): """ Populate the json that we send to the guaccamole API to create new connections group @@ -174,7 +173,7 @@ def guacamole_populate_connections_group_payload(module_params): return payload -def guacamole_add_connections_group(base_url, validate_certs, datasource, auth_token, payload): +def guacamole_add_connections_group(base_url, validate_certs, datasource, auth_token, payload): """ Add a new connections group to the guacamole server. """ @@ -386,7 +385,7 @@ def main(): # if the group has child connections and force_deletion=false fail and exit else: module.fail_json( - msg="Won't delete a group with child connections unless force_deletion=True" + msg="Won't delete a group with child connections unless force_deletion=True" ) # if the group to delete doesn't exists we just print a message @@ -408,7 +407,7 @@ def main(): # check if something changed (idempotence) if guacamole_connections_groups_before != guacamole_connections_groups_after: - result['changed'] = True + result['changed'] = True # return connections_group_info{} for the added/updated/deleted connections group if module.params.get('state') == 'present': diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py index 20c9265..bcf18e5 100644 --- a/plugins/modules/guacamole_user_group_users.py +++ b/plugins/modules/guacamole_user_group_users.py @@ -9,7 +9,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import open_url from ansible_collections.scicore.guacamole.plugins.module_utils.guacamole import GuacamoleError, \ - guacamole_get_token, guacamole_get_connections, guacamole_get_connections_groups + guacamole_get_token __metaclass__ = type ANSIBLE_METADATA = { @@ -132,7 +132,7 @@ def guacamole_get_users_groups(base_url, validate_certs, datasource, auth_token) try: users_groups = json.load(open_url(url_list_users_groups, method='GET', - validate_certs=validate_certs)) + validate_certs=validate_certs)) except ValueError as e: raise GuacamoleError( 'API returned invalid JSON when trying to obtain users groups from %s: %s' @@ -198,7 +198,7 @@ def guacamole_get_user_group_users(base_url, validate_certs, datasource, auth_to try: group_permissions = json.load(open_url(url_get_users_group_permissions, method='GET', - validate_certs=validate_certs)) + validate_certs=validate_certs)) except ValueError as e: raise GuacamoleError( 'API returned invalid JSON when trying to obtain group permissions from %s: %s' @@ -327,7 +327,7 @@ def main(): action='add', ) except GuacamoleError as e: - module.fail_json(msg=str(e)) + module.fail_json(msg=str(e)) result['changed'] = True From 8195f6e8f986bfc18fa00c3a1bb3620065973503 Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 4 Jun 2024 13:46:59 -0400 Subject: [PATCH 28/32] clean up --- plugins/modules/guacamole_user_groups.py | 59 ++++++------------------ 1 file changed, 13 insertions(+), 46 deletions(-) diff --git a/plugins/modules/guacamole_user_groups.py b/plugins/modules/guacamole_user_groups.py index 5997096..c357a6a 100644 --- a/plugins/modules/guacamole_user_groups.py +++ b/plugins/modules/guacamole_user_groups.py @@ -210,33 +210,6 @@ def guacamole_get_users_group_permissions(base_url, validate_certs, datasource, return group_permissions -def guacamole_update_users_in_group(base_url, validate_certs, datasource, auth_token, group_name, user, action): - """ - Add or remove a user to a group. - Action must be "add" or "remove" - """ - - if action not in ['add', 'remove']: - raise GuacamoleError("action must be 'add' or 'remove'") - - url_update_users_in_group = URL_UPDATE_USERS_IN_GROUP.format( - url=base_url, datasource=datasource, token=auth_token, group_name=group_name) - - payload = [{ - "op": action, - "path": '/', - "value": user, - }] - - try: - headers = {'Content-Type': 'application/json'} - open_url(url_update_users_in_group, method='PATCH', validate_certs=validate_certs, headers=headers, - data=json.dumps(payload)) - except Exception as e: - raise GuacamoleError('Could not update users for group %s in url %s. Error msg: %s' - % (group_name, url_update_users_in_group, str(e))) - - def guacamole_update_connections_in_group(base_url, validate_certs, datasource, auth_token, group_name, connection_id, action): """ Add or remove a connection to a group. @@ -295,6 +268,7 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + # Get the list of existing user-groups. try: groups_before = guacamole_get_users_groups( base_url=module.params.get('base_url'), @@ -305,22 +279,9 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - # module.fail_json(msg=guacamole_users_groups_before) - - # try: - # group_permissions = guacamole_get_users_group_permissions( - # base_url=module.params.get('base_url'), - # validate_certs=module.params.get('validate_certs'), - # datasource=guacamole_token['dataSource'], - # auth_token=guacamole_token['authToken'], - # group_name=module.params.get('group_name'), - # ) - # except GuacamoleError as e: - # module.fail_json(msg=str(e)) permissions = module.params.get('permissions') - # module arg state=present so we have to create a new group - # Get a list of the existing connections. + # Get the list of the existing connections. try: guacamole_existing_connections = guacamole_get_connections( base_url=module.params.get('base_url'), @@ -332,6 +293,7 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + # Get the list of the existing connection-groups. try: connections_groups = guacamole_get_connections_groups( base_url=module.params.get('base_url'), @@ -342,10 +304,11 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + # Add user-groups and assign permisions for connections to the user-groups. if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): - # If the group doesn't exists we add it. + # Add the user-group if it does not exist. if group_name not in groups_before: try: guacamole_add_group( @@ -360,8 +323,7 @@ def main(): result['changed'] = True - # Add the connections to the user group permissions. - # Check the existing connections for the user group. + # Get the list of connections for the user-group. try: existing_group_connection_ids = set(guacamole_get_users_group_permissions( base_url=module.params.get('base_url'), @@ -376,6 +338,8 @@ def main(): group_connection_ids = {connection['identifier'] for connection in guacamole_existing_connections if connection['name'] in set(connections)} - existing_group_connection_ids + + # Add connection permissions to the user-group. for connection_id in group_connection_ids: try: guacamole_update_connections_in_group( @@ -390,16 +354,17 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + # Remove user-groups and connection permisions. if module.params.get('state') in {'absent', 'sync'}: - # Determine which user groups should be deleted. + # Determine which user-groups should be deleted. if module.params.get('state') == 'absent': remove_groups = {group for group, connections in permissions.items() if not connections} else: remove_groups = set(groups_before) - set(permissions.keys()) - # Remove user groups. + # Remove user-groups. for remove_group in remove_groups: try: guacamole_delete_group( @@ -414,6 +379,7 @@ def main(): result['changed'] = True + # Remove connection permissions from user-groups. for group_name, connections in permissions.items(): connection_ids = {connection['identifier'] for connection @@ -432,6 +398,7 @@ def main(): group_name=group_name )['connectionPermissions'].keys()) - connection_ids + # Remove connection permissions. for remove_connection_id in remove_connection_ids: try: guacamole_update_connections_in_group( From 6fcd38b7c336633287b1c650f6c20d1a8324ed14 Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 4 Jun 2024 14:08:46 -0400 Subject: [PATCH 29/32] doc updates --- plugins/modules/guacamole_user_groups.py | 45 +++++++++++++----------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/plugins/modules/guacamole_user_groups.py b/plugins/modules/guacamole_user_groups.py index c357a6a..fdcde8b 100644 --- a/plugins/modules/guacamole_user_groups.py +++ b/plugins/modules/guacamole_user_groups.py @@ -20,14 +20,14 @@ DOCUMENTATION = ''' --- -module: guacamole_users_group +module: guacamole_user_group -short_description: Administer guacamole connections groups using the rest API +short_description: Administer guacamole user-groups using the rest API. version_added: "2.9" description: - - "Add or remove guacamole connections groups." + - "Manage guacamole user-groups and assign connection permissions." options: base_url: @@ -55,50 +55,55 @@ default: true type: bool - group_name: + permissions: description: - - Group name to create - required: true - type: str - - users: - description: - - List of users in this group - type: list + - A dictionary that maps user-group names to connections. + type: dict elements: str state: description: - - Create or delete the users group + - Create, delete or sync the user-group. + - `sync` will make guacamole match the permissions dict, so it will add and remove. default: 'present' type: str choices: - present - absent + - sync author: - Pablo Escobar Lopez (@pescobar) + - Garrett Bischof (@gwbischof) + - Robert Schaffer (@RobertSchaffer1) ''' EXAMPLES = ''' -- name: Create a new group "lab_3" +- name: Create a new user-group "users1" with permissions for connections: 'c1' and "c2" scicore.guacamole.guacamole_users_group: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - group_name: lab_3 - users: - - john - - laura + permissions: "{{ {'users1' : ['c1, c2']} }}" + state: present -- name: Delete users group "developers" +- name: Remove user-group "users1". scicore.guacamole.guacamole_users_group: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - group_name: developers + permissions: "{{ {'users1' : []} }}" state: absent + +- name: Sync user-groups and permissions. This will create groups and permissions defined + in the permissions dict, and delete anything not defined in the permissions. + scicore.guacamole.guacamole_users_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + permissions: "{{ {'users1' : ['c1, c2']} }}" + state: sync ''' RETURN = ''' From 17116610166f93eb4f23df22a81ace9d3b22fc7e Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 4 Jun 2024 14:13:36 -0400 Subject: [PATCH 30/32] flake8 fixes --- plugins/modules/guacamole_user_groups.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/plugins/modules/guacamole_user_groups.py b/plugins/modules/guacamole_user_groups.py index fdcde8b..dfb49e6 100644 --- a/plugins/modules/guacamole_user_groups.py +++ b/plugins/modules/guacamole_user_groups.py @@ -9,7 +9,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import open_url from ansible_collections.scicore.guacamole.plugins.module_utils.guacamole import GuacamoleError, \ - guacamole_get_token, guacamole_get_connections, guacamole_get_connections_groups + guacamole_get_token, guacamole_get_connections __metaclass__ = type ANSIBLE_METADATA = { @@ -137,7 +137,7 @@ def guacamole_get_users_groups(base_url, validate_certs, datasource, auth_token) try: users_groups = json.load(open_url(url_list_users_groups, method='GET', - validate_certs=validate_certs)) + validate_certs=validate_certs)) except ValueError as e: raise GuacamoleError( 'API returned invalid JSON when trying to obtain users groups from %s: %s' @@ -203,7 +203,7 @@ def guacamole_get_users_group_permissions(base_url, validate_certs, datasource, try: group_permissions = json.load(open_url(url_get_users_group_permissions, method='GET', - validate_certs=validate_certs)) + validate_certs=validate_certs)) except ValueError as e: raise GuacamoleError( 'API returned invalid JSON when trying to obtain group permissions from %s: %s' @@ -298,17 +298,6 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) - # Get the list of the existing connection-groups. - try: - connections_groups = guacamole_get_connections_groups( - base_url=module.params.get('base_url'), - validate_certs=module.params.get('validate_certs'), - datasource=guacamole_token['dataSource'], - auth_token=guacamole_token['authToken'], - ) - except GuacamoleError as e: - module.fail_json(msg=str(e)) - # Add user-groups and assign permisions for connections to the user-groups. if module.params.get('state') in {'present', 'sync'}: for group_name, connections in permissions.items(): @@ -341,8 +330,8 @@ def main(): module.fail_json(msg=str(e)) group_connection_ids = {connection['identifier'] for connection - in guacamole_existing_connections if - connection['name'] in set(connections)} - existing_group_connection_ids + in guacamole_existing_connections if connection['name'] + in set(connections)} - existing_group_connection_ids # Add connection permissions to the user-group. for connection_id in group_connection_ids: From 4ed18d5ea4033a260332907acb0754f23465ed4f Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 4 Jun 2024 15:26:59 -0400 Subject: [PATCH 31/32] clean up guacamole user group users module --- plugins/modules/guacamole_user_group_users.py | 159 ++++-------------- plugins/modules/guacamole_user_groups.py | 20 ++- 2 files changed, 47 insertions(+), 132 deletions(-) diff --git a/plugins/modules/guacamole_user_group_users.py b/plugins/modules/guacamole_user_group_users.py index bcf18e5..3cf08d4 100644 --- a/plugins/modules/guacamole_user_group_users.py +++ b/plugins/modules/guacamole_user_group_users.py @@ -20,9 +20,9 @@ DOCUMENTATION = ''' --- -module: guacamole_users_group +module: guacamole_user_group_users -short_description: Administer guacamole connections groups using the rest API +short_description: Administer guacamole user-group users using the rest API version_added: "2.9" @@ -55,138 +55,71 @@ default: true type: bool - group_name: - description: - - Group name to create - required: true - type: str - users: description: - - List of users in this group - type: list + - Dictionary that maps user-groups to a list of users. + type: dict elements: str state: description: - - Create or delete the users group + - Create, delete or sync the user-group members. default: 'present' type: str choices: - present - absent + - sync author: - Pablo Escobar Lopez (@pescobar) + - Garrett Bischof (@gwbischof) + - Robert Schaffer (@RobertSchaffer1) ''' EXAMPLES = ''' -- name: Create a new group "lab_3" - scicore.guacamole.guacamole_users_group: +- name: Assign user 'u1' to user group 'users1' + scicore.guacamole.guacamole_user_group_users: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - group_name: lab_3 users: - - john - - laura + "{{ { 'users1': ['u1'] } }}" + state: present -- name: Delete users group "developers" - scicore.guacamole.guacamole_users_group: +- name: Remove user 'u1' from user group 'users1' + scicore.guacamole.guacamole_user_group_users: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - group_name: developers + users: + "{{ { 'users1': ['u1'] } }}" state: absent + + +- name: Assign user 'u1' to user group 'users1', and remove any other users from 'users1'. + scicore.guacamole.guacamole_user_group_users: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + users: + "{{ { 'users1': ['u1'] } }}" + state: sync + ''' RETURN = ''' -group_info: - description: Information about the created or updated group - type: dict - returned: always message: description: Some extra info about what the module did type: str returned: always ''' -URL_LIST_GROUPS = "{url}/api/session/data/{datasource}/userGroups?token={token}" -URL_ADD_GROUP = URL_LIST_GROUPS -URL_DELETE_GROUP = "{url}/api/session/data/{datasource}/userGroups/{group_name}?token={token}" -URL_GET_GROUP_PERMISSIONS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/permissions?token={token}" -URL_UPDATE_CONNECTIONS_IN_GROUP = URL_GET_GROUP_PERMISSIONS URL_GET_GROUP_MEMBERS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/memberUsers?token={token}" URL_UPDATE_USERS_IN_GROUP = URL_GET_GROUP_MEMBERS -def guacamole_get_users_groups(base_url, validate_certs, datasource, auth_token): - """ - Returns a dict of dicts. - Each dict provides the name and state (enabled/disabled) for each group of users - """ - - url_list_users_groups = URL_LIST_GROUPS.format( - url=base_url, datasource=datasource, token=auth_token) - - try: - users_groups = json.load(open_url(url_list_users_groups, method='GET', - validate_certs=validate_certs)) - except ValueError as e: - raise GuacamoleError( - 'API returned invalid JSON when trying to obtain users groups from %s: %s' - % (url_list_users_groups, str(e))) - except Exception as e: - raise GuacamoleError('Could not obtain users groups from %s: %s' - % (url_list_users_groups, str(e))) - - return users_groups - - -def guacamole_add_group(base_url, validate_certs, datasource, auth_token, group_name): - """ - Add a group of users - """ - - url_add_group = URL_ADD_GROUP.format( - url=base_url, datasource=datasource, token=auth_token) - - payload = { - "identifier": group_name, - "attributes": { - "disabled": "" - } - } - - try: - headers = {'Content-Type': 'application/json'} - open_url(url_add_group, method='POST', validate_certs=validate_certs, headers=headers, - data=json.dumps(payload)) - except Exception as e: - # if the group exists we get a http code 400 - if e.code == 400: - pass - # raise GuacamoleError('Group %s already exists.' % group_name) - else: - raise GuacamoleError('Could not add a users group. Error msg: %s' % str(e)) - - -def guacamole_delete_group(base_url, validate_certs, datasource, auth_token, group_name): - """ - Delete a group of users - """ - - url_delete_group = URL_DELETE_GROUP.format( - url=base_url, datasource=datasource, token=auth_token, group_name=group_name) - - try: - headers = {'Content-Type': 'application/json'} - open_url(url_delete_group, method='DELETE', validate_certs=validate_certs, headers=headers) - except Exception as e: - raise GuacamoleError(f'Could not delete user group {group_name}. Error msg: {e}') - - def guacamole_get_user_group_users(base_url, validate_certs, datasource, auth_token, group_name): """ Returns a dict of dicts. @@ -237,33 +170,6 @@ def guacamole_update_users_in_group(base_url, validate_certs, datasource, auth_t % (group_name, url_update_users_in_group, str(e))) -def guacamole_update_connections_in_group(base_url, validate_certs, datasource, auth_token, group_name, connection_id, action): - """ - Add or remove a connection to a group. - Action must be "add" or "remove" - """ - - if action not in ['add', 'remove']: - raise GuacamoleError("action must be 'add' or 'remove'") - - url_update_connections_in_group = URL_UPDATE_CONNECTIONS_IN_GROUP.format( - url=base_url, datasource=datasource, token=auth_token, group_name=group_name) - - payload = [{ - "op": action, - "path": '/connectionPermissions/%s' % connection_id, - "value": 'READ' - }] - - try: - headers = {'Content-Type': 'application/json'} - open_url(url_update_connections_in_group, method='PATCH', validate_certs=validate_certs, headers=headers, - data=json.dumps(payload)) - except Exception as e: - raise GuacamoleError('Could not update connections for group %s in url %s. Error msg: %s' - % (group_name, url_update_connections_in_group, str(e))) - - def main(): # define the available arguments/parameters that a user can pass to @@ -277,7 +183,7 @@ def main(): state=dict(type='str', choices=['absent', 'present', 'sync'], default='present') ) - result = dict(changed=False, msg='', users_group_info={}) + result = dict(changed=False, msg='') module = AnsibleModule( argument_spec=module_args, @@ -297,10 +203,10 @@ def main(): users = module.params.get('users') + # Add users to user-group. if module.params.get('state') in {'present', 'sync'}: for group_name, usernames in users.items(): - # Add the users to the user group permissions. # Check the existing users for the user group. try: existing_users = set(guacamole_get_user_group_users( @@ -313,8 +219,10 @@ def main(): except GuacamoleError as e: module.fail_json(msg=str(e)) + # Find which users need to be added. new_users = set(usernames) - existing_users + # Add new users to user-group. for new_user in new_users: try: guacamole_update_users_in_group( @@ -331,10 +239,12 @@ def main(): result['changed'] = True + # Remove users from user group. if module.params.get('state') in {'absent', 'sync'}: - # Remove users from user group. for group_name, usernames in users.items(): + + # Find which users need to be removed. existing_users = set(guacamole_get_user_group_users( base_url=module.params.get('base_url'), validate_certs=module.params.get('validate_certs'), @@ -347,6 +257,7 @@ def main(): else: remove_users = existing_users - set(usernames) + # Remove users. for remove_user in remove_users: try: guacamole_update_users_in_group( diff --git a/plugins/modules/guacamole_user_groups.py b/plugins/modules/guacamole_user_groups.py index dfb49e6..a5f9ddc 100644 --- a/plugins/modules/guacamole_user_groups.py +++ b/plugins/modules/guacamole_user_groups.py @@ -80,12 +80,12 @@ EXAMPLES = ''' -- name: Create a new user-group "users1" with permissions for connections: 'c1' and "c2" +- name: Create a new user-group "users1" with permissions for connections: "c1' and "c2" scicore.guacamole.guacamole_users_group: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - permissions: "{{ {'users1' : ['c1, c2']} }}" + permissions: "{{ {'users1' : ['c1', 'c2']} }}" state: present - name: Remove user-group "users1". @@ -96,21 +96,25 @@ permissions: "{{ {'users1' : []} }}" state: absent +- name: Remove connection "c1" from user-group "users1". + scicore.guacamole.guacamole_users_group: + base_url: http://localhost/guacamole + auth_username: guacadmin + auth_password: guacadmin + permissions: "{{ {'users1' : ['c1']} }}" + state: absent + - name: Sync user-groups and permissions. This will create groups and permissions defined in the permissions dict, and delete anything not defined in the permissions. scicore.guacamole.guacamole_users_group: base_url: http://localhost/guacamole auth_username: guacadmin auth_password: guacadmin - permissions: "{{ {'users1' : ['c1, c2']} }}" + permissions: "{{ {'users1' : ['c1', 'c2']} }}" state: sync ''' RETURN = ''' -group_info: - description: Information about the created or updated group - type: dict - returned: always message: description: Some extra info about what the module did type: str @@ -380,7 +384,7 @@ def main(): in guacamole_existing_connections if connection['name'] in set(connections)} - # Determine which connections should be remove from group permissions. + # Determine which connections should be removed from group permissions. if module.params.get('state') == 'absent': remove_connection_ids = connection_ids else: From 988f0a30dfc3b704dbd18db74af819e7ebecde67 Mon Sep 17 00:00:00 2001 From: gbischof Date: Tue, 4 Jun 2024 15:28:01 -0400 Subject: [PATCH 32/32] touch ups --- plugins/modules/guacamole_user_groups.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/modules/guacamole_user_groups.py b/plugins/modules/guacamole_user_groups.py index a5f9ddc..f3975fb 100644 --- a/plugins/modules/guacamole_user_groups.py +++ b/plugins/modules/guacamole_user_groups.py @@ -126,8 +126,6 @@ URL_DELETE_GROUP = "{url}/api/session/data/{datasource}/userGroups/{group_name}?token={token}" URL_GET_GROUP_PERMISSIONS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/permissions?token={token}" URL_UPDATE_CONNECTIONS_IN_GROUP = URL_GET_GROUP_PERMISSIONS -URL_GET_GROUP_MEMBERS = "{url}/api/session/data/{datasource}/userGroups/{group_name}/memberUsers?token={token}" -URL_UPDATE_USERS_IN_GROUP = URL_GET_GROUP_MEMBERS def guacamole_get_users_groups(base_url, validate_certs, datasource, auth_token):