def main():
    """
    Module execution

    :return:
    """
    argument_spec = keycloak_argument_spec()

    roles_spec = dict(
        name=dict(type='str'),
        id=dict(type='str'),
    )

    meta_args = dict(
        state=dict(default='present', choices=['present', 'absent']),
        realm=dict(default='master'),
        gid=dict(type='str'),
        group_name=dict(type='str'),
        cid=dict(type='str'),
        client_id=dict(type='str'),
        roles=dict(type='list', elements='dict', options=roles_spec),
    )

    argument_spec.update(meta_args)

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=([[
            'token', 'auth_realm', 'auth_username', 'auth_password'
        ]]),
        required_together=([['auth_realm', 'auth_username', 'auth_password']]))

    result = dict(changed=False,
                  msg='',
                  diff={},
                  proposed={},
                  existing={},
                  end_state={})

    # Obtain access token, initialize API
    try:
        connection_header = get_token(module.params)
    except KeycloakError as e:
        module.fail_json(msg=str(e))

    kc = KeycloakAPI(module, connection_header)

    realm = module.params.get('realm')
    state = module.params.get('state')
    cid = module.params.get('cid')
    client_id = module.params.get('client_id')
    gid = module.params.get('gid')
    group_name = module.params.get('group_name')
    roles = module.params.get('roles')

    # Check the parameters
    if cid is None and client_id is None:
        module.fail_json(
            msg='Either the `client_id` or `cid` has to be specified.')
    if gid is None and group_name is None:
        module.fail_json(
            msg='Either the `group_name` or `gid` has to be specified.')

    # Get the potential missing parameters
    if gid is None:
        group_rep = kc.get_group_by_name(group_name, realm=realm)
        if group_rep is not None:
            gid = group_rep['id']
        else:
            module.fail_json(msg='Could not fetch group %s:' % group_name)
    if cid is None:
        cid = kc.get_client_id(client_id, realm=realm)
        if cid is None:
            module.fail_json(msg='Could not fetch client %s:' % client_id)
    if roles is None:
        module.exit_json(msg="Nothing to do (no roles specified).")
    else:
        for role_index, role in enumerate(roles, start=0):
            if role['name'] is None and role['id'] is None:
                module.fail_json(
                    msg=
                    'Either the `name` or `id` has to be specified on each role.'
                )
            # Fetch missing role_id
            if role['id'] is None:
                role_id = kc.get_client_role_by_name(gid,
                                                     cid,
                                                     role['name'],
                                                     realm=realm)
                if role_id is not None:
                    role['id'] = role_id
                else:
                    module.fail_json(msg='Could not fetch role %s:' %
                                     (role['name']))
            # Fetch missing role_name
            else:
                role['name'] = kc.get_client_rolemapping_by_id(
                    gid, cid, role['id'], realm=realm)['name']
                if role['name'] is None:
                    module.fail_json(msg='Could not fetch role %s' %
                                     (role['id']))

    # Get effective client-level role mappings
    available_roles_before = kc.get_client_available_rolemappings(gid,
                                                                  cid,
                                                                  realm=realm)
    assigned_roles_before = kc.get_client_composite_rolemappings(gid,
                                                                 cid,
                                                                 realm=realm)

    result['existing'] = assigned_roles_before
    result['proposed'] = roles

    update_roles = []
    for role_index, role in enumerate(roles, start=0):
        # Fetch roles to assign if state present
        if state == 'present':
            for available_role in available_roles_before:
                if role['name'] == available_role['name']:
                    update_roles.append({
                        'id': role['id'],
                        'name': role['name'],
                    })
        # Fetch roles to remove if state absent
        else:
            for assigned_role in assigned_roles_before:
                if role['name'] == assigned_role['name']:
                    update_roles.append({
                        'id': role['id'],
                        'name': role['name'],
                    })

    if len(update_roles):
        if state == 'present':
            # Assign roles
            result['changed'] = True
            if module._diff:
                result['diff'] = dict(before=assigned_roles_before,
                                      after=update_roles)
            if module.check_mode:
                module.exit_json(**result)
            kc.add_group_rolemapping(gid, cid, update_roles, realm=realm)
            result['msg'] = 'Roles %s assigned to group %s.' % (update_roles,
                                                                group_name)
            assigned_roles_after = kc.get_client_composite_rolemappings(
                gid, cid, realm=realm)
            result['end_state'] = assigned_roles_after
            module.exit_json(**result)
        else:
            # Remove mapping of role
            result['changed'] = True
            if module._diff:
                result['diff'] = dict(before=assigned_roles_before,
                                      after=update_roles)
            if module.check_mode:
                module.exit_json(**result)
            kc.delete_group_rolemapping(gid, cid, update_roles, realm=realm)
            result['msg'] = 'Roles %s removed from group %s.' % (update_roles,
                                                                 group_name)
            assigned_roles_after = kc.get_client_composite_rolemappings(
                gid, cid, realm=realm)
            result['end_state'] = assigned_roles_after
            module.exit_json(**result)
    # Do nothing
    else:
        result['changed'] = False
        result[
            'msg'] = 'Nothing to do, roles %s are correctly mapped with group %s.' % (
                roles, group_name)
        module.exit_json(**result)
def main():
    """
    Module execution

    :return:
    """
    argument_spec = keycloak_argument_spec()
    meta_args = dict(state=dict(default='present',
                                choices=['present', 'absent']),
                     realm=dict(default='master'),
                     id=dict(type='str'),
                     name=dict(type='str'),
                     attributes=dict(type='dict'))

    argument_spec.update(meta_args)

    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True,
                           required_one_of=([['id', 'name']]))

    result = dict(changed=False, msg='', diff={}, group='')

    # Obtain access token, initialize API
    try:
        connection_header = get_token(
            base_url=module.params.get('auth_keycloak_url'),
            validate_certs=module.params.get('validate_certs'),
            auth_realm=module.params.get('auth_realm'),
            client_id=module.params.get('auth_client_id'),
            auth_username=module.params.get('auth_username'),
            auth_password=module.params.get('auth_password'),
            client_secret=module.params.get('auth_client_secret'),
        )
    except KeycloakError as e:
        module.fail_json(msg=str(e))
    kc = KeycloakAPI(module, connection_header)

    realm = module.params.get('realm')
    state = module.params.get('state')
    gid = module.params.get('id')
    name = module.params.get('name')
    attributes = module.params.get('attributes')

    before_group = None  # current state of the group, for merging.

    # does the group already exist?
    if gid is None:
        before_group = kc.get_group_by_name(name, realm=realm)
    else:
        before_group = kc.get_group_by_groupid(gid, realm=realm)

    before_group = {} if before_group is None else before_group

    # attributes in Keycloak have their values returned as lists
    # via the API. attributes is a dict, so we'll transparently convert
    # the values to lists.
    if attributes is not None:
        for key, val in module.params['attributes'].items():
            module.params['attributes'][key] = [
                val
            ] if not isinstance(val, list) else val

    group_params = [
        x for x in module.params
        if x not in list(keycloak_argument_spec().keys()) +
        ['state', 'realm'] and module.params.get(x) is not None
    ]

    # build a changeset
    changeset = {}
    for param in group_params:
        new_param_value = module.params.get(param)
        old_value = before_group[param] if param in before_group else None
        if new_param_value != old_value:
            changeset[camel(param)] = new_param_value

    # prepare the new group
    updated_group = before_group.copy()
    updated_group.update(changeset)

    # if before_group is none, the group doesn't exist.
    if before_group == {}:
        if state == 'absent':
            # nothing to do.
            if module._diff:
                result['diff'] = dict(before='', after='')
            result['msg'] = 'Group does not exist; doing nothing.'
            result['group'] = dict()
            module.exit_json(**result)

        # for 'present', create a new group.
        result['changed'] = True
        if name is None:
            module.fail_json(
                msg='name must be specified when creating a new group')

        if module._diff:
            result['diff'] = dict(before='', after=updated_group)

        if module.check_mode:
            module.exit_json(**result)

        # do it for real!
        kc.create_group(updated_group, realm=realm)
        after_group = kc.get_group_by_name(name, realm)

        result['group'] = after_group
        result['msg'] = 'Group {name} has been created with ID {id}'.format(
            name=after_group['name'], id=after_group['id'])

    else:
        if state == 'present':
            # no changes
            if updated_group == before_group:
                result['changed'] = False
                result['group'] = updated_group
                result['msg'] = "No changes required to group {name}.".format(
                    name=before_group['name'])
                module.exit_json(**result)

            # update the existing group
            result['changed'] = True

            if module._diff:
                result['diff'] = dict(before=before_group, after=updated_group)

            if module.check_mode:
                module.exit_json(**result)

            # do the update
            kc.update_group(updated_group, realm=realm)

            after_group = kc.get_group_by_groupid(updated_group['id'],
                                                  realm=realm)

            result['group'] = after_group
            result['msg'] = "Group {id} has been updated".format(
                id=after_group['id'])

            module.exit_json(**result)

        elif state == 'absent':
            result['group'] = dict()

            if module._diff:
                result['diff'] = dict(before=before_group, after='')

            if module.check_mode:
                module.exit_json(**result)

            # delete for real
            gid = before_group['id']
            kc.delete_group(groupid=gid, realm=realm)

            result['changed'] = True
            result['msg'] = "Group {name} has been deleted".format(
                name=before_group['name'])

            module.exit_json(**result)

    module.exit_json(**result)
Example #3
0
def main():
    """
    Module execution

    :return:
    """
    argument_spec = keycloak_argument_spec()

    meta_args = dict(
        state=dict(default='present', choices=['present', 'absent']),
        realm=dict(default='master'),
        id=dict(type='str'),
        name=dict(type='str'),
        attributes=dict(type='dict'),
    )

    argument_spec.update(meta_args)

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=([['id', 'name'],
                          [
                              'token', 'auth_realm', 'auth_username',
                              'auth_password'
                          ]]),
        required_together=([['auth_realm', 'auth_username', 'auth_password']]))

    result = dict(changed=False, msg='', diff={}, group='')

    # Obtain access token, initialize API
    try:
        connection_header = get_token(module.params)
    except KeycloakError as e:
        module.fail_json(msg=str(e))

    kc = KeycloakAPI(module, connection_header)

    realm = module.params.get('realm')
    state = module.params.get('state')
    gid = module.params.get('id')
    name = module.params.get('name')
    attributes = module.params.get('attributes')

    # attributes in Keycloak have their values returned as lists
    # via the API. attributes is a dict, so we'll transparently convert
    # the values to lists.
    if attributes is not None:
        for key, val in module.params['attributes'].items():
            module.params['attributes'][key] = [
                val
            ] if not isinstance(val, list) else val

    # Filter and map the parameters names that apply to the group
    group_params = [
        x for x in module.params
        if x not in list(keycloak_argument_spec().keys()) +
        ['state', 'realm'] and module.params.get(x) is not None
    ]

    # See if it already exists in Keycloak
    if gid is None:
        before_group = kc.get_group_by_name(name, realm=realm)
    else:
        before_group = kc.get_group_by_groupid(gid, realm=realm)

    if before_group is None:
        before_group = {}

    # Build a proposed changeset from parameters given to this module
    changeset = {}

    for param in group_params:
        new_param_value = module.params.get(param)
        old_value = before_group[param] if param in before_group else None
        if new_param_value != old_value:
            changeset[camel(param)] = new_param_value

    # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
    desired_group = before_group.copy()
    desired_group.update(changeset)

    # Cater for when it doesn't exist (an empty dict)
    if not before_group:
        if state == 'absent':
            # Do nothing and exit
            if module._diff:
                result['diff'] = dict(before='', after='')
            result['changed'] = False
            result['end_state'] = {}
            result['group'] = result['end_state']
            result['msg'] = 'Group does not exist; doing nothing.'
            module.exit_json(**result)

        # Process a creation
        result['changed'] = True

        if name is None:
            module.fail_json(
                msg='name must be specified when creating a new group')

        if module._diff:
            result['diff'] = dict(before='', after=desired_group)

        if module.check_mode:
            module.exit_json(**result)

        # create it
        kc.create_group(desired_group, realm=realm)
        after_group = kc.get_group_by_name(name, realm)

        result['end_state'] = after_group
        result['group'] = result['end_state']

        result['msg'] = 'Group {name} has been created with ID {id}'.format(
            name=after_group['name'], id=after_group['id'])
        module.exit_json(**result)

    else:
        if state == 'present':
            # Process an update

            # no changes
            if desired_group == before_group:
                result['changed'] = False
                result['end_state'] = desired_group
                result['group'] = result['end_state']
                result['msg'] = "No changes required to group {name}.".format(
                    name=before_group['name'])
                module.exit_json(**result)

            # doing an update
            result['changed'] = True

            if module._diff:
                result['diff'] = dict(before=before_group, after=desired_group)

            if module.check_mode:
                module.exit_json(**result)

            # do the update
            kc.update_group(desired_group, realm=realm)

            after_group = kc.get_group_by_groupid(desired_group['id'],
                                                  realm=realm)

            result['end_state'] = after_group
            result['group'] = result['end_state']

            result['msg'] = "Group {id} has been updated".format(
                id=after_group['id'])
            module.exit_json(**result)

        else:
            # Process a deletion (because state was not 'present')
            result['changed'] = True

            if module._diff:
                result['diff'] = dict(before=before_group, after='')

            if module.check_mode:
                module.exit_json(**result)

            # delete it
            gid = before_group['id']
            kc.delete_group(groupid=gid, realm=realm)

            result['end_state'] = {}
            result['group'] = result['end_state']

            result['msg'] = "Group {name} has been deleted".format(
                name=before_group['name'])

    module.exit_json(**result)