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)
def main(): argument_spec = keycloak_argument_spec() config_spec = dict( vendor=dict(type='list', choices=['ad', 'tivoli', 'edirectory', 'rhds', 'other']), usernameLDAPAttribute=dict(type='list'), editMode=dict(type='list', choices=['READ_ONLY', 'WRITABLE', 'UNSYNCED']), rdnLDAPAttribute=dict(type='list'), uuidLDAPAttribute=dict(type='list'), userObjectClasses=dict(type='list'), connectionUrl=dict(type='list'), usersDn=dict(type='list'), authType=dict(type='list', choices=['simple', 'none']), bindDn=dict(type='list'), bindCredential=dict(type='list'), changedSyncPeriod=dict(type='list'), fullSyncPeriod=dict(type='list'), pagination=dict(type='list', choices=['true', 'false']), connectionPooling=dict(type='list', choices=['true', 'false']), cachePolicy=dict(type='list', choices=[ 'DEFAULT', 'EVICT_DAILY', 'EVICT_WEEKLY', 'MAX_LIFESPAN', 'NO_CACHE' ]), useKerberosForPasswordAuthentication=dict(type='list', choices=['true', 'false']), allowKerberosAuthentication=dict(type='list', choices=[['true'], ['false']]), importEnabled=dict(type='list', choices=['true', 'false']), syncRegistrations=dict(type='list', choices=['true', 'false']), searchScope=dict(type='list', choices=['1', '2']), priority=dict(type='list'), validatePasswordPolicy=dict(type='list', choices=['true', 'false']), batchSizeForSync=dict(type='list')) ldapstoragemapper_spec = { "ldap.attribute": { 'type': 'list' }, "is.mandatory.in.ldap": { 'type': 'list', 'choices': ['true', 'false'] }, "read.only": { 'type': 'list', 'choices': ['true', 'false'] }, "user.model.attribute": { 'type': 'list' }, "always.read.value.from.ldap": { 'type': 'list', 'choices': [['true'], ['false']] }, 'mode': { 'type': 'list', 'choices': ['LDAP_ONLY', 'READ_ONLY', 'IMPORT'] }, "membership.attribute.type": { 'type': 'list', 'choices': ['DN', 'UID'] }, "user.roles.retrieve.strategy": { 'type': 'list', 'choices': [ 'LOAD_GROUPS_BY_MEMBER_ATTRIBUTE', 'GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE', 'LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY' ] }, 'group.name.ldap.attribute': { 'type': 'list' }, 'membership.ldap.attribute': { 'type': 'list' }, 'membership.user.ldap.attribute': { 'type': 'list' }, 'memberof.ldap.attribute': { 'type': 'list' }, 'preserve.group.inheritance': { 'type': 'list', 'choices': ['true', 'false'] }, 'groups.dn': { 'type': 'list' }, 'group.object.classes': { 'type': 'list' }, 'drop.non.existing.groups.during.sync': { 'type': 'list', 'choices': ['true', 'false'] } } subcomponents_config_spec = { "name": { "type": "str" }, "providerId": { "type": "str", "choices": ['user-attribute-ldap-mapper', 'group-ldap-mapper'] }, "config": { "type": "dict", "options": ldapstoragemapper_spec } } subcomponents_spec = { "org.keycloak.storage.ldap.mappers.LDAPStorageMapper": { 'type': 'list', 'options': subcomponents_config_spec } } meta_args = dict( id=dict(type='str'), name=dict(type='str', required=True), realm=dict(type='str', required=True), providerId=dict(choices=[ "ldap", "allowed-client-templates", "trusted-hosts", "allowed-protocol-mappers", "max-clients", "scope", "consent-required", "rsa-generated" ], required=True), providerType=dict(choices=[ "org.keycloak.storage.UserStorageProvider", "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy", "org.keycloak.keys.KeyProvider", "authenticatorConfig", "requiredActions" ], required=True), parentId=dict(type='str'), config=dict(type='dict', options=config_spec), subComponents=dict(type='dict', options=subcomponents_spec), syncUserStorage=dict( choices=["triggerFullSync", "triggerChangedUsersSync"]), syncLdapMappers=dict(choices=["fedToKeycloak", "keycloakToFed"]), state=dict(choices=["absent", "present"], default='present'), force=dict(type='bool', default=False), ) argument_spec.update(meta_args) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) result = dict(changed=False, msg='', diff={}, component='', subComponents='') # 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') force = module.params.get('force') # Create a representation from module parameters newComponent = {} newComponent["id"] = module.params.get('id') newComponent["name"] = module.params.get('name') newComponent["providerId"] = module.params.get('providerId') newComponent["providerType"] = module.params.get('providerType') newComponent["parentId"] = module.params.get( 'parentId') if module.params.get('parentId') is not None else realm newComponent["config"] = module.params.get("config") remove_arguments_with_value_none(newComponent["config"]) newSubComponents = module.params.get("subComponents") remove_arguments_with_value_none(newSubComponents) syncUserStorage = module.params.get( 'syncUserStorage') if module.params.get( 'syncUserStorage') is not None else "no" syncLdapMappers = module.params.get( 'syncLdapMappers') if module.params.get( 'syncLdapMappers') is not None else "no" changed = False component = kc.get_component_by_name_provider_and_parent( name=newComponent["name"], provider_type=newComponent["providerType"], provider_id=newComponent["providerId"], parent_id=newComponent["parentId"], realm=realm) if component == {}: # If component does not exist if (state == 'present'): # If desired stat is present # Create the component and it's sub-components component = kc.create_component(newComponent=newComponent, newSubComponents=newSubComponents, syncLdapMappers=syncLdapMappers, realm=realm) subComponents = kc.get_all_sub_components( parent_id=component["id"], realm=realm) if syncUserStorage != 'no': # If user synchronization is needed kc.sync_user_storage(component_id=component['id'], action=syncUserStorage, realm=realm) result['component'] = component changed = True result['component'] = component result['subComponents'] = subComponents result['changed'] = changed elif state == 'absent': # Id desired state is absent, return absent and do nothing. result['msg'] = newComponent["name"] + ' absent' result['component'] = newComponent result['changed'] = changed else: # If component already exist if (state == 'present'): # if desired state is present if force: # If force option is true # Delete the existing component kc.delete_component(component_id=component["id"], realm=realm) changed = True # Re-create the component. component = kc.create_component( newComponent=newComponent, newSubComponents=newSubComponents, syncLdapMappers=syncLdapMappers, realm=realm) else: # If force option is false # Copy existing id in new component newComponent['id'] = component['id'] newComponent['parentId'] = component['parentId'] excludes = [] # Compare the new component with the existing excludes.append("bindCredential") if not isDictEquals( newComponent, component, excludes): # If the component need to be changed # Update the component component = kc.update_component(newComponent=newComponent, realm=realm) changed = True # Update sub components if kc.update_sub_components(component=newComponent, newSubComponents=newSubComponents, syncLdapMappers=syncLdapMappers, realm=realm): changed = True if syncUserStorage != 'no': # If user synchronization is needed kc.sync_user_storage(component_id=component['id'], action=syncUserStorage, realm=realm) result['component'] = component result['subComponents'] = kc.get_all_sub_components( parent_id=component["id"], realm=realm) result['changed'] = changed elif state == 'absent': # if desired state is absent # Delete the component kc.delete_component(component_id=component['id'], realm=realm) changed = True result['msg'] = newComponent["name"] + ' deleted' result['changed'] = changed module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() protmapper_spec = dict( consentRequired=dict(type='bool'), consentText=dict(type='str'), id=dict(type='str'), name=dict(type='str'), protocol=dict(type='str', choices=['openid-connect', 'saml']), protocolMapper=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( realm=dict(type='str', default='master'), state=dict(default='present', choices=['present', 'absent']), id=dict(type='str'), name=dict(type='str'), description=dict(type='str'), protocol=dict(type='str', choices=['openid-connect', 'saml']), attributes=dict(type='dict'), full_scope_allowed=dict(type='bool'), protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec), ) 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={}, proposed={}, existing={}, end_state={}) # 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') cid = module.params.get('id') # convert module parameters to client representation parameters (if they belong in there) clientt_params = [ x for x in module.params if x not in [ 'state', 'auth_keycloak_url', 'auth_client_id', 'auth_realm', 'auth_client_secret', 'auth_username', 'auth_password', 'validate_certs', 'realm' ] and module.params.get(x) is not None ] # See whether the client template already exists in Keycloak if cid is None: before_clientt = kc.get_client_template_by_name( module.params.get('name'), realm=realm) if before_clientt is not None: cid = before_clientt['id'] else: before_clientt = kc.get_client_template_by_id(cid, realm=realm) if before_clientt is None: before_clientt = dict() result['existing'] = before_clientt # Build a proposed changeset from parameters given to this module changeset = dict() for clientt_param in clientt_params: # lists in the Keycloak API are sorted new_param_value = module.params.get(clientt_param) if isinstance(new_param_value, list): try: new_param_value = sorted(new_param_value) except TypeError: pass changeset[camel(clientt_param)] = new_param_value # Whether creating or updating a client, take the before-state and merge the changeset into it updated_clientt = before_clientt.copy() updated_clientt.update(changeset) result['proposed'] = changeset # If the client template does not exist yet, before_client is still empty if before_clientt == dict(): if state == 'absent': # do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['msg'] = 'Client template does not exist, doing nothing.' module.exit_json(**result) # create new client template result['changed'] = True if 'name' not in updated_clientt: module.fail_json( msg='name needs to be specified when creating a new client') if module._diff: result['diff'] = dict(before='', after=updated_clientt) if module.check_mode: module.exit_json(**result) kc.create_client_template(updated_clientt, realm=realm) after_clientt = kc.get_client_template_by_name(updated_clientt['name'], realm=realm) result['end_state'] = after_clientt result[ 'msg'] = 'Client template %s has been created.' % updated_clientt[ 'name'] module.exit_json(**result) else: if state == 'present': # update existing client template result['changed'] = True if module.check_mode: # We can only compare the current client template with the proposed updates we have if module._diff: result['diff'] = dict(before=before_clientt, after=updated_clientt) module.exit_json(**result) kc.update_client_template(cid, updated_clientt, realm=realm) after_clientt = kc.get_client_template_by_id(cid, realm=realm) if before_clientt == after_clientt: result['changed'] = False if module._diff: result['diff'] = dict(before=before_clientt, after=after_clientt) result['end_state'] = after_clientt result[ 'msg'] = 'Client template %s has been updated.' % updated_clientt[ 'name'] module.exit_json(**result) else: # Delete existing client result['changed'] = True if module._diff: result['diff']['before'] = before_clientt result['diff']['after'] = '' if module.check_mode: module.exit_json(**result) kc.delete_client_template(cid, realm=realm) result['proposed'] = dict() result['end_state'] = dict() result[ 'msg'] = 'Client template %s has been deleted.' % before_clientt[ 'name'] module.exit_json(**result) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() protmapper_spec = dict( consentRequired=dict(type='bool'), consentText=dict(type='str'), id=dict(type='str'), name=dict(type='str'), protocol=dict(type='str', choices=['openid-connect', 'saml']), protocolMapper=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( state=dict(default='present', choices=['present', 'absent']), realm=dict(type='str', default='master'), id=dict(type='str'), client_id=dict(type='str', aliases=['clientId']), name=dict(type='str'), description=dict(type='str'), root_url=dict(type='str', aliases=['rootUrl']), admin_url=dict(type='str', aliases=['adminUrl']), base_url=dict(type='str', aliases=['baseUrl']), surrogate_auth_required=dict(type='bool', aliases=['surrogateAuthRequired']), enabled=dict(type='bool'), client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt'], aliases=['clientAuthenticatorType']), secret=dict(type='str', no_log=True), registration_access_token=dict(type='str', aliases=['registrationAccessToken']), default_roles=dict(type='list', aliases=['defaultRoles']), redirect_uris=dict(type='list', aliases=['redirectUris']), web_origins=dict(type='list', aliases=['webOrigins']), not_before=dict(type='int', aliases=['notBefore']), bearer_only=dict(type='bool', aliases=['bearerOnly']), consent_required=dict(type='bool', aliases=['consentRequired']), standard_flow_enabled=dict(type='bool', aliases=['standardFlowEnabled']), implicit_flow_enabled=dict(type='bool', aliases=['implicitFlowEnabled']), direct_access_grants_enabled=dict( type='bool', aliases=['directAccessGrantsEnabled']), service_accounts_enabled=dict(type='bool', aliases=['serviceAccountsEnabled']), authorization_services_enabled=dict( type='bool', aliases=['authorizationServicesEnabled']), public_client=dict(type='bool', aliases=['publicClient']), frontchannel_logout=dict(type='bool', aliases=['frontchannelLogout']), protocol=dict(type='str', choices=['openid-connect', 'saml']), attributes=dict(type='dict'), full_scope_allowed=dict(type='bool', aliases=['fullScopeAllowed']), node_re_registration_timeout=dict( type='int', aliases=['nodeReRegistrationTimeout']), registered_nodes=dict(type='dict', aliases=['registeredNodes']), client_template=dict(type='str', aliases=['clientTemplate']), use_template_config=dict(type='bool', aliases=['useTemplateConfig']), use_template_scope=dict(type='bool', aliases=['useTemplateScope']), use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']), protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']), authorization_settings=dict(type='dict', aliases=['authorizationSettings']), ) argument_spec.update(meta_args) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, required_one_of=([['client_id', 'id']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # 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') cid = module.params.get('id') state = module.params.get('state') # convert module parameters to client representation parameters (if they belong in there) client_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 ] keycloak_argument_spec().keys() # See whether the client already exists in Keycloak if cid is None: before_client = kc.get_client_by_clientid( module.params.get('client_id'), realm=realm) if before_client is not None: cid = before_client['id'] else: before_client = kc.get_client_by_id(cid, realm=realm) if before_client is None: before_client = dict() # Build a proposed changeset from parameters given to this module changeset = dict() for client_param in client_params: new_param_value = module.params.get(client_param) # some lists in the Keycloak API are sorted, some are not. if isinstance(new_param_value, list): if client_param in ['attributes']: try: new_param_value = sorted(new_param_value) except TypeError: pass # Unfortunately, the ansible argument spec checker introduces variables with null values when # they are not specified if client_param == 'protocol_mappers': new_param_value = [ dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value ] changeset[camel(client_param)] = new_param_value # Whether creating or updating a client, take the before-state and merge the changeset into it updated_client = before_client.copy() updated_client.update(changeset) result['proposed'] = sanitize_cr(changeset) result['existing'] = sanitize_cr(before_client) # If the client does not exist yet, before_client is still empty if before_client == dict(): if state == 'absent': # do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['msg'] = 'Client does not exist, doing nothing.' module.exit_json(**result) # create new client result['changed'] = True if 'clientId' not in updated_client: module.fail_json( msg='client_id needs to be specified when creating a new client' ) if module._diff: result['diff'] = dict(before='', after=sanitize_cr(updated_client)) if module.check_mode: module.exit_json(**result) kc.create_client(updated_client, realm=realm) after_client = kc.get_client_by_clientid(updated_client['clientId'], realm=realm) result['end_state'] = sanitize_cr(after_client) result[ 'msg'] = 'Client %s has been created.' % updated_client['clientId'] module.exit_json(**result) else: if state == 'present': # update existing client result['changed'] = True if module.check_mode: # We can only compare the current client with the proposed updates we have if module._diff: result['diff'] = dict(before=sanitize_cr(before_client), after=sanitize_cr(updated_client)) result['changed'] = (before_client != updated_client) module.exit_json(**result) kc.update_client(cid, updated_client, realm=realm) after_client = kc.get_client_by_id(cid, realm=realm) if before_client == after_client: result['changed'] = False if module._diff: result['diff'] = dict(before=sanitize_cr(before_client), after=sanitize_cr(after_client)) result['end_state'] = sanitize_cr(after_client) result['msg'] = 'Client %s has been updated.' % updated_client[ 'clientId'] module.exit_json(**result) else: # Delete existing client result['changed'] = True if module._diff: result['diff']['before'] = sanitize_cr(before_client) result['diff']['after'] = '' if module.check_mode: module.exit_json(**result) kc.delete_client(cid, realm=realm) result['proposed'] = dict() result['end_state'] = dict() result['msg'] = 'Client %s has been deleted.' % before_client[ 'clientId'] module.exit_json(**result) module.exit_json(**result)