def main(): argument_spec = keycloak_argument_spec() composites_spec = dict(name=dict(type='str', required=True), clientId=dict(type='str')) meta_args = dict( realm=dict(type='str', default='master'), name=dict(type='str', required=True), description=dict(type='str', default=None), composite=dict(type='bool', default=False), clientRole=dict(type='bool', default=False), containerId=dict(type='str', required=False), composites=dict(type='list', elements='dict', default=[], options=composites_spec), 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='', role={}, composites=None) # 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') newComposites = None # Create representation for the new role newRoleRepresentation = {} newRoleRepresentation["name"] = module.params.get('name') if module.params.get('description') is not None: newRoleRepresentation["description"] = module.params.get('description') newRoleRepresentation["composite"] = module.params.get('composite') newRoleRepresentation["clientRole"] = module.params.get('clientRole') newRoleRepresentation["containerId"] = module.params.get( 'containerId') if module.params.get( 'containerId') is not None else realm if module.params.get('composites') is not None: newComposites = module.params.get('composites') changed = False # Search the role on Keycloak server. roleRepresentation = kc.search_realm_role_by_name( name=newRoleRepresentation["name"], realm=realm) if roleRepresentation == {}: # If role does not exists if (state == 'present'): # If desired state is present # Create Role kc.create_realm_role(newRoleRepresentation=newRoleRepresentation, realm=realm) # Create composites kc.create_or_update_realm_role_composites( newComposites=newComposites, newRoleRepresentation=newRoleRepresentation, realm=realm) # Get created role roleRepresentation = kc.get_realm_role( name=newRoleRepresentation["name"], realm=realm) # Get created composites composites = kc.get_realm_role_composites_with_client_id( name=newRoleRepresentation["name"], realm=realm) changed = True result['role'] = roleRepresentation result['composites'] = composites elif state == 'absent': # If desired state is absent result["msg"] = "Realm role %s is absent in realm %s" % ( newRoleRepresentation["name"], realm) else: # If role already exists if (state == 'present'): # If desired state is present if force: # If force option is true # Delete the existing role kc.delete_realm_role(name=roleRepresentation["name"], realm=realm) # Create role again kc.create_realm_role( newRoleRepresentation=newRoleRepresentation, realm=realm) changed = True else: # If force option is false # Compare roles if not (isDictEquals( newRoleRepresentation, roleRepresentation)): # If new role introduce changes # Update the role kc.update_realm_role( newRoleRepresentation=newRoleRepresentation, realm=realm) changed = True # Manage composites if kc.create_or_update_realm_role_composites( newComposites=newComposites, newRoleRepresentation=newRoleRepresentation, realm=realm): changed = True # Get created role roleRepresentation = kc.get_realm_role( name=newRoleRepresentation["name"], realm=realm) # Get composites composites = kc.get_realm_role_composites_with_client_id( name=newRoleRepresentation["name"], realm=realm) result["role"] = roleRepresentation result["composites"] = composites elif state == 'absent': # If desired state is absent # Delete role kc.delete_realm_role(name=newRoleRepresentation["name"], realm=realm) changed = True result["msg"] = "Realm role %s is deleted in realm %s" % ( newRoleRepresentation["name"], realm) result['changed'] = changed module.exit_json(**result)
def main(): argument_spec = keycloak_argument_spec() config_spec = dict( vendor=dict(type='list', elements='str', choices=['ad', 'tivoli', 'edirectory', 'rhds', 'other']), usernameLDAPAttribute=dict(type='list', elements='str'), editMode=dict(type='list', elements='str', choices=['READ_ONLY', 'WRITABLE', 'UNSYNCED']), rdnLDAPAttribute=dict(type='list', elements='str'), uuidLDAPAttribute=dict(type='list', elements='str'), userObjectClasses=dict(type='list', elements='str'), connectionUrl=dict(type='list', elements='str'), usersDn=dict(type='list', elements='str'), authType=dict(type='list', elements='str', choices=['simple', 'none']), bindDn=dict(type='list', elements='str'), bindCredential=dict(type='list', elements='str'), changedSyncPeriod=dict(type='list', elements='str'), fullSyncPeriod=dict(type='list', elements='str'), pagination=dict(type='list', elements='str', choices=['true', 'false']), connectionPooling=dict(type='list', elements='str', choices=['true', 'false']), cachePolicy=dict(type='list', elements='str', choices=['DEFAULT', 'EVICT_DAILY', 'EVICT_WEEKLY', 'MAX_LIFESPAN', 'NO_CACHE']), useKerberosForPasswordAuthentication=dict(type='list', elements='str', choices=['true', 'false']), allowKerberosAuthentication=dict(type='list', elements='str', choices=['true', 'false']), importEnabled=dict(type='list', elements='str', choices=['true', 'false']), syncRegistrations=dict(type='list', elements='str', choices=['true', 'false']), searchScope=dict(type='list', elements='str', choices=['1', '2']), priority=dict(type='list', elements='int'), validatePasswordPolicy=dict(type='list', elements='str', choices=['true', 'false']), batchSizeForSync=dict(type='list', elements='str') ) ldapstoragemapper_spec = { "ldap.attribute": {'type': 'list', 'elements': 'str'}, "is.mandatory.in.ldap": {'type': 'list', 'elements': 'str', 'choices': ['true', 'false']}, "read.only": {'type': 'list', 'elements': 'str', 'choices': ['true', 'false']}, "user.model.attribute": {'type': 'list', 'elements': 'str'}, "always.read.value.from.ldap": {'type': 'list', 'elements': 'str', 'choices': ['true', 'false']}, 'mode': {'type': 'list', 'elements': 'str', 'choices': ['LDAP_ONLY', 'READ_ONLY', 'IMPORT']}, "membership.attribute.type": {'type': 'list', 'elements': 'str', 'choices': ['DN', 'UID']}, "user.roles.retrieve.strategy": {'type': 'list', 'elements': 'str', 'choices': [ 'LOAD_GROUPS_BY_MEMBER_ATTRIBUTE', 'GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE', 'LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY' ]}, 'group.name.ldap.attribute': {'type': 'list', 'elements': 'str'}, 'membership.ldap.attribute': {'type': 'list', 'elements': 'str'}, 'membership.user.ldap.attribute': {'type': 'list', 'elements': 'str'}, 'memberof.ldap.attribute': {'type': 'list', 'elements': 'str'}, 'preserve.group.inheritance': {'type': 'list', 'elements': 'str', 'choices': ['true', 'false']}, 'groups.dn': {'type': 'list', 'elements': 'str'}, 'group.object.classes': {'type': 'list', 'elements': 'str'}, 'drop.non.existing.groups.during.sync': {'type': 'list', 'elements': 'str', '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', 'elements': 'dict', '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"] = remove_arguments_with_value_none(module.params.get("config").copy()) newSubComponents = remove_arguments_with_value_none(module.params.get("subComponents").copy()) 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(): argument_spec = keycloak_argument_spec() idpconfig_spec = { "openIdConfigurationUrl": { "type": "str" }, "clientId": { "type": "str" }, "clientSecret": { "type": "str" }, "clientAuthMethod": { "type": "str", "choices": [ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt" ], "default": "client_secret_post" }, "disableUserInfo": { "type": "str", "default": "false", "choices": ["true", "false"] }, "defaultScope": { "type": "str" }, "guiOrder": { "type": "int" }, "backchannelSupported": { "type": "str", "default": "true", "choices": ["true", "false"] } } mapperconfig_args = { "claim": { "type": "str" }, "user.attribute": { "type": "str" }, "claim.value": { "type": "str" }, "role": { "type": "str" } } mapper_spec = { "name": { "type": "str" }, "identityProviderMapper": { "type": "str", "choices": ["oidc-user-attribute-idp-mapper", "oidc-role-idp-mapper"] }, "config": { "type": "dict", "options": mapperconfig_args } } meta_args = dict( realm=dict(type='str', default='master'), alias=dict(type='str', required=True), displayName=dict(type='str'), providerId=dict(type='str', default='oidc'), enabled=dict(type='bool', default=True), updateProfileFirstLoginMode=dict(type='str'), trustEmail=dict(type='bool'), storeToken=dict(type='bool', default=True), addReadTokenRoleOnCreate=dict(type='bool'), authenticateByDefault=dict(type='bool'), firstBrokerLoginFlowAlias=dict(type='str'), postBrokerLoginFlowAlias=dict(type='str'), linkOnly=dict(type='bool', default=False), config=dict(type='dict', options=idpconfig_spec), mappers=dict(type='list', elements='dict', options=mapper_spec), 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='', idp={}, mappers=[]) # 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') changed = False # Créer un représentation du realm recu en paramètres newIdPRepresentation = {} newIdPRepresentation["alias"] = module.params.get('alias') if module.params.get('displayName') is not None: newIdPRepresentation["displayName"] = module.params.get('displayName') newIdPRepresentation["providerId"] = module.params.get('providerId') newIdPRepresentation["enabled"] = module.params.get('enabled') if module.params.get('updateProfileFirstLoginMode') is not None: newIdPRepresentation[ "updateProfileFirstLoginMode"] = module.params.get( 'updateProfileFirstLoginMode') if module.params.get('trustEmail') is not None: newIdPRepresentation["trustEmail"] = module.params.get('trustEmail') if module.params.get('storeToken') is not None: newIdPRepresentation["storeToken"] = module.params.get('storeToken') if module.params.get('addReadTokenRoleOnCreate') is not None: newIdPRepresentation["addReadTokenRoleOnCreate"] = module.params.get( 'addReadTokenRoleOnCreate') if module.params.get("authenticateByDefault") is not None: newIdPRepresentation["authenticateByDefault"] = module.params.get( 'authenticateByDefault') if module.params.get('firstBrokerLoginFlowAlias') is not None: newIdPRepresentation["firstBrokerLoginFlowAlias"] = module.params.get( 'firstBrokerLoginFlowAlias') if module.params.get('postBrokerLoginFlowAlias') is not None: newIdPRepresentation["postBrokerLoginFlowAlias"] = module.params.get( 'postBrokerLoginFlowAlias') newIdPConfig = None if module.params.get('config') is not None: newIdPConfig = remove_arguments_with_value_none( module.params.get('config')) if 'openIdConfigurationUrl' in newIdPConfig: del (newIdPConfig['openIdConfigurationUrl']) if 'providerId' in newIdPRepresentation and newIdPRepresentation[ "providerId"] == 'google' and 'userIp' in module.params.get( "config"): newIdPConfig["userIp"] = module.params.get("config")["userIp"] newIdPMappers = remove_arguments_with_value_none( module.params.get('mappers')) if newIdPConfig is not None: if (module.params.get('config') is not None and 'openIdConfigurationUrl' in module.params.get('config') and module.params.get("config")['openIdConfigurationUrl'] is not None): kc.add_idp_endpoints( newIdPConfig, module.params.get("config")['openIdConfigurationUrl']) newIdPRepresentation["config"] = newIdPConfig # Search the Idp on Keycloak server. # By its alias idPRepresentation = kc.search_idp_by_alias( alias=newIdPRepresentation["alias"], realm=realm) if idPRepresentation == {}: # IdP does not exist on Keycloak server if (state == 'present'): # If desired state is present # Create IdP changed = True idPRepresentation = kc.create_idp( newIdPRepresentation=newIdPRepresentation, realm=realm) result["idp"] = idPRepresentation if newIdPMappers is not None and len(newIdPMappers) > 0: kc.create_or_update_idp_mappers( alias=newIdPRepresentation["alias"], idPMappers=newIdPMappers, realm=realm) mappersRepresentation = kc.get_idp_mappers( alias=newIdPRepresentation["alias"], realm=realm) result["mappers"] = mappersRepresentation else: # Sinon, le status est absent result["msg"] = newIdPRepresentation["alias"] + ' absent' else: # if IdP already exists alias = idPRepresentation['alias'] if (state == 'present'): # if desired state is present if force: # If force option is true # Delete all idp's mappers kc.delete_all_idp_mappers(alias=alias, realm=realm) # Delete the existing IdP kc.delete_idp(alias=alias, realm=realm) # Re-create the IdP idPRepresentation = kc.create_idp( newIdPRepresentation=newIdPRepresentation, realm=realm) changed = True else: # if force option is false # Compare the new Idp with the existing IdP if (not isDictEquals( newIdPRepresentation, idPRepresentation, ["clientSecret", "openIdConfigurationUrl", "mappers"]) or ("config" in newIdPRepresentation and "clientSecret" in newIdPRepresentation["config"] and newIdPRepresentation["config"]["clientSecret"] is not None)): # If they are different # Create an updated representation of the IdP updatedIdP = copy.deepcopy(idPRepresentation) updatedIdP.update(newIdPRepresentation) if "config" in newIdPRepresentation and newIdPRepresentation[ "config"] is not None: updatedConfig = idPRepresentation["config"] updatedConfig.update(newIdPRepresentation["config"]) updatedIdP["config"] = updatedConfig # Update the IdP idPRepresentation = kc.update_idp( newIdPRepresentation=updatedIdP, realm=realm) changed = True if newIdPMappers is not None and len(newIdPMappers) > 0: if kc.create_or_update_idp_mappers( alias=newIdPRepresentation["alias"], idPMappers=newIdPMappers, realm=realm): changed = True mappersRepresentation = kc.get_idp_mappers( alias=newIdPRepresentation["alias"], realm=realm) result["idp"] = idPRepresentation result["mappers"] = mappersRepresentation else: # If desired state is absent # Delete all idp's mappers kc.delete_all_idp_mappers(alias=alias, realm=realm) # Delete the existing IdP kc.delete_idp(alias=alias, realm=realm) changed = True result["msg"] = 'IdP %s is deleted' % (alias) result["changed"] = changed module.exit_json(**result)
def main(): argument_spec = keycloak_argument_spec() client_role_spec = dict( clientId=dict(type='str', required=True), roles=dict(type='list', elements='str', required=True), ) meta_args = dict( realm=dict(type='str', default='master'), self=dict(type='str'), id=dict(type='str'), username=dict(type='str', required=True), firstName=dict(type='str'), lastName=dict(type='str'), email=dict(type='str'), enabled=dict(type='bool', default=True), emailVerified=dict(type='bool', default=False), federationLink=dict(type='str'), serviceAccountClientId=dict(type='str'), attributes=dict(type='dict'), access=dict(type='dict'), clientRoles=dict(type='list', elements='dict', default=[], options=client_role_spec), realmRoles=dict(type='list', elements='str', default=[]), groups=dict(type='list', elements='str', default=[]), disableableCredentialTypes=dict(type='list', elements='str', default=[]), requiredActions=dict(type='list', elements='str', default=[]), credentials=dict(type='list', elements='dict', default=[]), federatedIdentities=dict(type='list', elements='str', default=[]), clientConsents=dict(type='list', elements='dict', default=[]), origin=dict(type='str'), 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='', user={}) # 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 of the user received in parameters newUserRepresentation = {} newUserClientRolesRepresentation = {} newUserRepresentation["username"] = module.params.get('username') if module.params.get('self') is not None: newUserRepresentation["self"] = module.params.get('self') if module.params.get('id') is not None: newUserRepresentation["id"] = module.params.get('id') newUserRepresentation["enabled"] = module.params.get('enabled') newUserRepresentation["emailVerified"] = module.params.get('emailVerified') if module.params.get('firstName') is not None: newUserRepresentation["firstName"] = module.params.get('firstName') if module.params.get('lastName') is not None: newUserRepresentation["lastName"] = module.params.get('lastName') if module.params.get('email') is not None: newUserRepresentation["email"] = module.params.get('email') if module.params.get('federationLink') is not None: newUserRepresentation["federationLink"] = module.params.get( 'federationLink') if module.params.get('serviceAccountClientId') is not None: newUserRepresentation["serviceAccountClientId"] = module.params.get( 'serviceAccountClientId') if module.params.get('origin') is not None: newUserRepresentation["origin"] = module.params.get('origin') if module.params.get('credentials') is not None: newUserRepresentation["credentials"] = module.params.get('credentials') if module.params.get('disableableCredentialTypes') is not None: newUserRepresentation[ "disableableCredentialTypes"] = module.params.get( 'disableableCredentialTypes') if module.params.get('federatedIdentities') is not None: newUserRepresentation["federatedIdentities"] = module.params.get( 'federatedIdentities') if module.params.get('requiredActions') is not None: newUserRepresentation["requiredActions"] = module.params.get( 'requiredActions') if module.params.get('clientConsents') is not None: newUserRepresentation["clientConsents"] = module.params.get( 'clientConsents') if module.params.get('attributes') is not None: newUserRepresentation["attributes"] = module.params.get('attributes') if module.params.get('access') is not None: newUserRepresentation["access"] = module.params.get('access') if module.params.get('clientRoles') is not None: newUserClientRolesRepresentation["clientRoles"] = module.params.get( 'clientRoles') if module.params.get('realmRoles') is not None: newUserRepresentation["realmRoles"] = module.params.get('realmRoles') if module.params.get('groups') is not None: newUserRepresentation["groups"] = module.params.get('groups') changed = False userRepresentation = kc.search_user_by_username( username=newUserRepresentation["username"], realm=realm) if userRepresentation == {}: # The user does not exist # Create the user if (state == 'present'): # If state is present # Create the user userRepresentation = kc.create_user( newUserRepresentation=newUserRepresentation, realm=realm) # Add user ID to new representation newUserRepresentation['id'] = userRepresentation["id"] # Assign roles to user kc.assing_roles_to_user( user_id=newUserRepresentation["id"], userRealmRoles=newUserRepresentation['realmRoles'], userClientRoles=newUserClientRolesRepresentation[ 'clientRoles'], realm=realm) # set user groups kc.update_user_groups_membership( newUserRepresentation=newUserRepresentation, realm=realm) # Get the updated user realm roles userRepresentation["realmRoles"] = kc.get_user_realm_roles( user_id=userRepresentation["id"], realm=realm) # Get the user clientRoles userRepresentation["clientRoles"] = kc.get_user_client_roles( user_id=userRepresentation["id"], realm=realm) # Get the user groups userRepresentation["groups"] = kc.get_user_groups( user_id=userRepresentation["id"], realm=realm) changed = True result["user"] = userRepresentation elif state == 'absent': # Otherwise, the status is absent result["msg"] = 'User %s is absent' % ( newUserRepresentation["username"]) else: # the user already exists if (state == 'present'): # if desired state is present if force: # If the force option is set to true # Delete the existing user kc.delete_user(user_id=userRepresentation["id"], realm=realm) changed = True # Recreate the user userRepresentation = kc.create_user( newUserRepresentation=newUserRepresentation, realm=realm) # Add user ID to new representation newUserRepresentation['id'] = userRepresentation["id"] else: # If the force option is false excludes = [ "access", "notBefore", "createdTimestamp", "totp", "credentials", "disableableCredentialTypes", "realmRoles", "clientRoles", "groups", "clientConsents", "federatedIdentities", "requiredActions" ] # Add user ID to new representation newUserRepresentation['id'] = userRepresentation["id"] # Compare users if not ( isDictEquals(newUserRepresentation, userRepresentation, excludes) ): # If the new user does not introduce a change to the existing user # Update the user userRepresentation = kc.update_user( newUserRepresentation=newUserRepresentation, realm=realm) changed = True # Assign roles to user if kc.assing_roles_to_user( user_id=newUserRepresentation["id"], userRealmRoles=newUserRepresentation['realmRoles'], userClientRoles=newUserClientRolesRepresentation[ 'clientRoles'], realm=realm): changed = True # set user groups if kc.update_user_groups_membership( newUserRepresentation=newUserRepresentation, realm=realm): changed = True # Get the updated user realm roles userRepresentation["realmRoles"] = kc.get_user_realm_roles( user_id=userRepresentation["id"], realm=realm) # Get the user clientRoles userRepresentation["clientRoles"] = kc.get_user_client_roles( user_id=userRepresentation["id"], realm=realm) # Get the user groups userRepresentation["groups"] = kc.get_user_groups( user_id=userRepresentation["id"], realm=realm) result["user"] = userRepresentation elif state == 'absent': # Status is absent # Delete user kc.delete_user(user_id=userRepresentation['id'], realm=realm) result["msg"] = 'User %s deleted' % (userRepresentation['id']) changed = True result['changed'] = changed 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'), path=dict(type='str'), attributes_list=dict(type='list', elements='dict'), realmRoles=dict(type='list', elements='str'), clientRoles=dict(type='list', elements='dict'), syncLdapMappers=dict(type='bool', default=False), force=dict(type='bool', default=False), ) 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') # Add attribute received as a list to the attributes dict kc.add_attributes_list_to_attributes_dict( module.params.get('attributes_list'), attributes) syncLdapMappers = module.params.get('syncLdapMappers') groupRealmRoles = module.params.get('realmRoles') groupClientRoles = module.params.get('clientRoles') force = module.params.get('force') before_group = None # current state of the group, for merging. # Synchronize LDAP group to Keycloak if syncLdapMappers is true if syncLdapMappers: kc.sync_ldap_groups("fedToKeycloak", realm=realm) # 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 excludes = [ 'state', 'realm', 'force', 'attributes_list', 'realmRoles', 'clientRoles', 'syncLdapMappers' ] group_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + excludes 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) # Assing roles to group kc.assing_roles_to_group(groupRepresentation=updated_group, groupRealmRoles=groupRealmRoles, groupClientRoles=groupClientRoles, realm=realm) # Sync Keycloak groups to User Storages if syncLdapMappers is true if syncLdapMappers: kc.sync_ldap_groups("keycloakToFed", 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 and not force: 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) if force: # delete for real gid = before_group['id'] kc.delete_group(groupid=gid, realm=realm) # remove id del (updated_group['id']) if "realmRoles" in updated_group: del (updated_group['realmRoles']) if "clientRoles" in updated_group: del (updated_group['clientRoles']) # create it again kc.create_group(updated_group, realm=realm) else: # do the update kc.update_group(updated_group, realm=realm) # Assing roles to group kc.assing_roles_to_group(groupRepresentation=updated_group, groupRealmRoles=groupRealmRoles, groupClientRoles=groupClientRoles, realm=realm) # Sync Keycloak groups to User Storages if syncLdapMappers is true if syncLdapMappers: kc.sync_ldap_groups("keycloakToFed", realm=realm) if force: after_group = kc.get_group_by_name(name, realm) else: 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) # Sync Keycloak groups to User Storages if syncLdapMappers is true if syncLdapMappers: kc.sync_ldap_groups("keycloakToFed", 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() smtp_spec = { "replyToDisplayName": { "type": "str" }, "starttls": { "type": "str", "choices": ["true", "false"], "default": "false" }, "auth": { "type": "str", "choices": ["true", "false"], "default": "false" }, "port": { "type": "str", "default": "25" }, "host": { "type": "str" }, "replyTo": { "type": "str" }, "from": { "type": "str" }, "fromDisplayName": { "type": "str" }, "envelopeFrom": { "type": "str" }, "ssl": { "type": "str", "choices": ["true", "false"], "default": "false" }, "user": { "type": "str" }, "password": { "type": "str", "no_log": True } } eventconfig_spec = { "eventsEnabled": { "type": "bool" }, "eventsListeners": { "type": "list", "elements": "str" }, "adminEventsEnabled": { "type": "bool" }, "adminEventsDetailsEnabled": { "type": "bool" }, "eventsExpiration": { "type": "int" }, "enabledEventTypes": { "type": "list", "elements": "str", "choices": [ "SEND_RESET_PASSWORD", "UPDATE_TOTP", "REMOVE_TOTP", "REVOKE_GRANT", "LOGIN_ERROR", "CLIENT_LOGIN", "RESET_PASSWORD_ERROR", "IMPERSONATE_ERROR", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "UPDATE_PROFILE_ERROR", "IMPERSONATE", "LOGIN", "UPDATE_PASSWORD_ERROR", "REGISTER", "LOGOUT", "CLIENT_REGISTER", "UPDATE_PASSWORD", "FEDERATED_IDENTITY_LINK_ERROR", "CLIENT_DELETE", "IDENTITY_PROVIDER_FIRST_LOGIN", "VERIFY_EMAIL", "CLIENT_DELETE_ERROR", "CLIENT_LOGIN_ERROR", "REMOVE_FEDERATED_IDENTITY_ERROR", "EXECUTE_ACTIONS", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_VERIFY_EMAIL", "EXECUTE_ACTIONS_ERROR", "REMOVE_FEDERATED_IDENTITY", "IDENTITY_PROVIDER_POST_LOGIN", "UPDATE_EMAIL", "REGISTER_ERROR", "REVOKE_GRANT_ERROR", "LOGOUT_ERROR", "UPDATE_EMAIL_ERROR", "CLIENT_UPDATE_ERROR", "UPDATE_PROFILE", "FEDERATED_IDENTITY_LINK", "CLIENT_REGISTER_ERROR", "SEND_VERIFY_EMAIL_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "RESET_PASSWORD", "REMOVE_TOTP_ERROR", "VERIFY_EMAIL_ERROR", "SEND_RESET_PASSWORD_ERROR", "CLIENT_UPDATE", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "CUSTOM_REQUIRED_ACTION_ERROR", "UPDATE_TOTP_ERROR", "CODE_TO_TOKEN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR" ] } } meta_args = dict( accessCodeLifespan=dict(type='int', default=60), accessCodeLifespanLogin=dict(type='int', default=1800), accessCodeLifespanUserAction=dict(type='int', default=300), accountTheme=dict(type="str"), accessTokenLifespan=dict(type='int', default=300), accessTokenLifespanForImplicitFlow=dict(type='int', default=900), actionTokenGeneratedByAdminLifespan=dict(type='int', default=43200), actionTokenGeneratedByUserLifespan=dict(type='int', default=300), adminTheme=dict(type="str"), attributes=dict(type='dict', default=None), browserSecurityHeaders=dict(type='dict', default=None), browserFlow=dict(type='str', default="browser"), bruteForceProtected=dict(type='bool', default=False), clientAuthenticationFlow=dict(type='str', default="clients"), defaultLocale=dict(type="str"), defaultRoles=dict(type='list', elements='str', default=["offline_access", "uma_authorization"]), directGrantFlow=dict(type='str', default="direct grant"), displayName=dict(type='str', required=True, aliases=['name']), displayNameHtml=dict(type='str', required=True, aliases=['namehtml']), duplicateEmailsAllowed=dict(type='bool', default=False), editUsernameAllowed=dict(type='bool', default=False), emailTheme=dict(type="str"), enabled=dict(type='bool', default=True), eventsConfig=dict(type='dict', options=eventconfig_spec), failureFactor=dict(type='int', default=30), force=dict(type='bool', default=False), internationalizationEnabled=dict(type="bool"), loginTheme=dict(type="str"), loginWithEmailAllowed=dict(type='bool', default=True), maxDeltaTimeSeconds=dict(type='int', default=43200), maxFailureWaitSeconds=dict(type='int', default=900), minimumQuickLoginWaitSeconds=dict(type='int', default=60), notBefore=dict(type='int', default=0), offlineSessionIdleTimeout=dict(type='int', default=2592000), otpPolicyAlgorithm=dict(type='str', default="HmacSHA1"), otpPolicyDigits=dict(type='int', default=6), otpPolicyInitialCounter=dict(type='int', default=0), otpPolicyLookAheadWindow=dict(type='int', default=1), otpPolicyPeriod=dict(type='int', default=30), otpPolicyType=dict(type='str', default="totp"), passwordPolicy=dict(type='str', default="hashIterations(20000)"), permanentLockout=dict(type='bool', default=False), quickLoginCheckMilliSeconds=dict(type='int', default=1000), realm=dict(type='str', default='master'), registrationAllowed=dict(type='bool', default=False), registrationEmailAsUsername=dict(type='bool', default=False), registrationFlow=dict(type='str', default="registration"), rememberMe=dict(type='bool', default=False), requiredCredentials=dict(type='list', elements='str', default=["password"]), resetCredentialsFlow=dict(type='str', default="reset credentials"), resetPasswordAllowed=dict(type='bool', default=False), revokeRefreshToken=dict(type='bool', default=False), smtpServer=dict(type='dict', default={}, options=smtp_spec), sslRequired=dict(type='str', default="external"), ssoSessionIdleTimeout=dict(type='int', default=1800), ssoSessionMaxLifespan=dict(type='int', default=36000), state=dict(choices=["absent", "present"], default='present'), supportedLocales=dict(type="list", elements="str"), verifyEmail=dict(type='bool', default=False), waitIncrementSeconds=dict(type='int', default=60), ) argument_spec.update(meta_args) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) result = dict(changed=False, msg='', realm={}, eventsConfig={}) # 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) # defaultAttributes = dict( # _browser_header=dict( # contentSecurityPolicy=dict(type='unicode', default="frame-src 'self'"), # xContentTypeOptions=dict(type='unicode', default="nosniff"), # xFrameOptions=dict(type='unicode', default="SAMEORIGIN"), # xRobotsTag=dict(type='unicode', default="none"), # xXSSProtection=dict(type='unicode', default="1; mode=block") # ), # actionTokenGeneratedByAdminLifespan=dict(type='int', default=43200), # actionTokenGeneratedByUserLifespan=dict(type='int', default=300), # displayName=dict(type='unicode', default=module.params.get('name')), # displayNameHtml=dict(type='unicode', default=module.params.get('namehtml')), # ) # defaultBrowserSecurityHeaders = dict( # contentSecurityPolicy=dict(type='unicode', default="frame-src 'self'"), # xContentTypeOptions=dict(type='unicode', default="nosniff"), # xFrameOptions=dict(type='unicode', default="SAMEORIGIN"), # xRobotsTag=dict(type='unicode', default="none"), # xXSSProtection=dict(type='unicode', default="1; mode=block") # ) state = module.params.get('state') force = module.params.get('force') # Create a realm representation form module parameters newRealmRepresentation = {} newRealmRepresentation["id"] = module.params.get('realm') newRealmRepresentation["realm"] = module.params.get('realm') newRealmRepresentation["displayName"] = module.params.get('displayName') newRealmRepresentation["displayNameHtml"] = module.params.get( 'displayNameHtml') if module.params.get("loginTheme") is not None: newRealmRepresentation["loginTheme"] = module.params.get("loginTheme") if module.params.get("adminTheme") is not None: newRealmRepresentation["adminTheme"] = module.params.get("adminTheme") if module.params.get("emailTheme") is not None: newRealmRepresentation["emailTheme"] = module.params.get("emailTheme") if module.params.get("accountTheme") is not None: newRealmRepresentation["accountTheme"] = module.params.get( "accountTheme") newRealmRepresentation["accessCodeLifespan"] = module.params.get( 'accessCodeLifespan') newRealmRepresentation["accessCodeLifespanLogin"] = module.params.get( 'accessCodeLifespanLogin') newRealmRepresentation["accessCodeLifespanUserAction"] = module.params.get( 'accessCodeLifespanUserAction') newRealmRepresentation[ "actionTokenGeneratedByAdminLifespan"] = module.params.get( 'actionTokenGeneratedByAdminLifespan') newRealmRepresentation[ "actionTokenGeneratedByUserLifespan"] = module.params.get( 'actionTokenGeneratedByUserLifespan') newRealmRepresentation["notBefore"] = module.params.get('notBefore') newRealmRepresentation["revokeRefreshToken"] = module.params.get( 'revokeRefreshToken') newRealmRepresentation["accessTokenLifespan"] = module.params.get( 'accessTokenLifespan') newRealmRepresentation[ "accessTokenLifespanForImplicitFlow"] = module.params.get( 'accessTokenLifespanForImplicitFlow') newRealmRepresentation["ssoSessionIdleTimeout"] = module.params.get( 'ssoSessionIdleTimeout') newRealmRepresentation["ssoSessionMaxLifespan"] = module.params.get( 'ssoSessionMaxLifespan') newRealmRepresentation["offlineSessionIdleTimeout"] = module.params.get( 'offlineSessionIdleTimeout') newRealmRepresentation["enabled"] = module.params.get('enabled') newRealmRepresentation["sslRequired"] = module.params.get('sslRequired') newRealmRepresentation["registrationAllowed"] = module.params.get( 'registrationAllowed') newRealmRepresentation["registrationEmailAsUsername"] = module.params.get( 'registrationEmailAsUsername') newRealmRepresentation["rememberMe"] = module.params.get('rememberMe') newRealmRepresentation["verifyEmail"] = module.params.get('verifyEmail') newRealmRepresentation["loginWithEmailAllowed"] = module.params.get( 'loginWithEmailAllowed') newRealmRepresentation["duplicateEmailsAllowed"] = module.params.get( 'duplicateEmailsAllowed') newRealmRepresentation["resetPasswordAllowed"] = module.params.get( 'resetPasswordAllowed') newRealmRepresentation["editUsernameAllowed"] = module.params.get( 'editUsernameAllowed') newRealmRepresentation["bruteForceProtected"] = module.params.get( 'bruteForceProtected') newRealmRepresentation["permanentLockout"] = module.params.get( 'permanentLockout') newRealmRepresentation["maxFailureWaitSeconds"] = module.params.get( 'maxFailureWaitSeconds') newRealmRepresentation["minimumQuickLoginWaitSeconds"] = module.params.get( 'minimumQuickLoginWaitSeconds') newRealmRepresentation["waitIncrementSeconds"] = module.params.get( 'waitIncrementSeconds') newRealmRepresentation["quickLoginCheckMilliSeconds"] = module.params.get( 'quickLoginCheckMilliSeconds') newRealmRepresentation["maxDeltaTimeSeconds"] = module.params.get( 'maxDeltaTimeSeconds') newRealmRepresentation["failureFactor"] = module.params.get( 'failureFactor') newRealmRepresentation["defaultRoles"] = module.params.get('defaultRoles') newRealmRepresentation["requiredCredentials"] = module.params.get( 'requiredCredentials') newRealmRepresentation["passwordPolicy"] = module.params.get( 'passwordPolicy') newRealmRepresentation["otpPolicyType"] = module.params.get( 'otpPolicyType') newRealmRepresentation["otpPolicyAlgorithm"] = module.params.get( 'otpPolicyAlgorithm') newRealmRepresentation["otpPolicyInitialCounter"] = module.params.get( 'otpPolicyInitialCounter') newRealmRepresentation["otpPolicyDigits"] = module.params.get( 'otpPolicyDigits') newRealmRepresentation["otpPolicyLookAheadWindow"] = module.params.get( 'otpPolicyLookAheadWindow') newRealmRepresentation["otpPolicyPeriod"] = module.params.get( 'otpPolicyPeriod') newRealmRepresentation["smtpServer"] = remove_arguments_with_value_none( module.params.get('smtpServer')) if module.params.get("supportedLocales") is not None: if module.params.get("internationalizationEnabled") is not None: newRealmRepresentation[ "internationalizationEnabled"] = module.params.get( "internationalizationEnabled") else: newRealmRepresentation["internationalizationEnabled"] = True newRealmRepresentation["supportedLocales"] = module.params.get( "supportedLocales") if module.params.get("defaultLocale") is not None: newRealmRepresentation["defaultLocale"] = module.params.get( "defaultLocale") else: newRealmRepresentation["internationalizationEnabled"] = False newRealmRepresentation["browserFlow"] = module.params.get('browserFlow') newRealmRepresentation["registrationFlow"] = module.params.get( 'registrationFlow') newRealmRepresentation["directGrantFlow"] = module.params.get( 'directGrantFlow') newRealmRepresentation["resetCredentialsFlow"] = module.params.get( 'resetCredentialsFlow') newRealmRepresentation["clientAuthenticationFlow"] = module.params.get( 'clientAuthenticationFlow') if module.params.get("eventsExpiration") is not None: newRealmRepresentation["eventsExpiration"] = module.params.get( 'eventsExpiration') # Read Events configuration for the Realm newEventsConfig = remove_arguments_with_value_none( module.params.get("eventsConfig")) if module.params.get("browserSecurityHeaders") is not None: newRealmRepresentation["browserSecurityHeaders"] = module.params.get( "browserSecurityHeaders") changed = False # Find realm on Keycloak server realmRepresentation = kc.search_realm( realm=newRealmRepresentation["realm"]) if realmRepresentation == {}: # Realm does not exist if (state == 'present'): # If desired state is present # Create the realm result["realm"] = kc.create_realm( newRealmRepresentation=newRealmRepresentation) if newEventsConfig is not None: eventsConfig = kc.update_realm_events_config( realm=newRealmRepresentation["realm"], newEventsConfig=newEventsConfig) result["eventsConfig"] = eventsConfig changed = True else: # if desired state is absent result['msg'] = 'Realm %s is absent' % ( newRealmRepresentation["realm"]) else: # Realm already exists if (state == 'present'): # If desired state is present if force: # If force option is true # Delete the existing realm kc.delete_realm(newRealmRepresentation["realm"]) # Create realm realmRepresentation = kc.create_realm( newRealmRepresentation=newRealmRepresentation) changed = True else: # If force option is false # Compare realms if not isDictEquals(newRealmRepresentation, realmRepresentation ): # If new realm introduces changes # Update REALM realmRepresentation = kc.update_realm( newRealmRepresentation=newRealmRepresentation) changed = True else: realmRepresentation = kc.get_realm( realm=newRealmRepresentation["realm"]) if newEventsConfig is not None: # If there is event configuration # Get the existing events config eventsConfig = kc.get_realm_events_config( realm=newRealmRepresentation["realm"]) if not isDictEquals(newEventsConfig, eventsConfig): # If realm needs changed # Update event config eventsConfig = kc.update_realm_events_config( realm=newRealmRepresentation["realm"], newEventsConfig=newEventsConfig) result["eventsConfig"] = eventsConfig result["realm"] = realmRepresentation else: # If desired state is absent # Delete Realm kc.delete_realm(newRealmRepresentation["realm"]) changed = True result["msg"] = 'Realm %s deleted' % ( newRealmRepresentation["realm"]) 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'), state=dict(type='str', choices=['absent', 'present'], default='present'), ) clientrolecomposites_spec = dict( name=dict(type='str'), id=dict(type='str'), ) clientroles_spec = dict( name=dict(type='str'), description=dict(type='str'), composite=dict(type='bool'), composites=dict(type='list', elements='dict', options=clientrolecomposites_spec), state=dict(type='str', choices=['absent', 'present'], default='present'), ) realmscopemappings_spec = dict(name=dict(type='str'), state=dict(type='str', choices=['absent', 'present'], default='present')) clientrolescopemappings_spec = dict(name=dict(type='str'), state=dict( type='str', choices=['absent', 'present'], default='present')) clientsscopemappings_spec = dict(id=dict(type='str'), roles=dict( type='list', elements='dict', options=clientrolescopemappings_spec)) scopemappings_spec = dict(realm=dict(type='list', elements='dict', options=realmscopemappings_spec), clients=dict(type='list', elements='dict', options=clientsscopemappings_spec)) 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', elements='str', aliases=['defaultRoles']), redirect_uris=dict(type='list', elements='str', aliases=['redirectUris']), web_origins=dict(type='list', elements='str', 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'], default='openid-connect'), 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']), client_roles=dict(type='list', elements='dict', options=clientroles_spec, aliases=['clientRoles', 'roles']), scope_mappings=dict(type='dict', aliases=['scopeMappings'], options=scopemappings_spec), force=dict(type='bool', default=False), ) 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={}, clientSecret='') connection_header = {} # 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', 'url', 'force'] 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 ] if client_param == 'roles': client_param = 'client_roles' changeset[camel(client_param)] = new_param_value newClientScopeMappings = {} newClientScopeRealm = {} newClientScopeClients = {} if module.params.get('scope_mappings') is not None: newClientScopeMappings["scope_mappings"] = module.params.get( 'scope_mappings') if newClientScopeMappings["scope_mappings"]["realm"] is not None: newClientScopeRealm["realmRoles"] = newClientScopeMappings[ "scope_mappings"]["realm"] if newClientScopeMappings["scope_mappings"]["clients"] is not None: newClientScopeClients["clientRoles"] = newClientScopeMappings[ "scope_mappings"]["clients"] # 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) client_secret = kc.get_client_secret_by_id(after_client['id'], realm=realm) if client_secret is not None: result['clientSecret'] = client_secret if module.params.get('scope_mappings') is not None: kc.assing_scope_roles_to_client( client_id=after_client['id'], clientScopeRealmRoles=newClientScopeRealm["realmRoles"], clientScopeClientRoles=newClientScopeClients["clientRoles"], realm=realm) result['changed'] = True 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) client_secret = kc.get_client_secret_by_id(cid, realm=realm) if client_secret is not None: result['clientSecret'] = client_secret if before_client == after_client: result['changed'] = False if module._diff: result['diff'] = dict(before=sanitize_cr(before_client), after=sanitize_cr(after_client)) if module.params.get('scope_mappings') is not None: result['changed'] = kc.assing_scope_roles_to_client( client_id=after_client['id'], clientScopeRealmRoles=newClientScopeRealm["realmRoles"], clientScopeClientRoles=newClientScopeClients[ "clientRoles"], realm=realm) 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)
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 :returm: """ argument_spec = keycloak_argument_spec() meta_args = dict( realm=dict(type='str', required=True), alias=dict(type='str', required=True), providerId=dict(type='str'), copyFrom=dict(type='str'), authenticationExecutions=dict(type='list', elements='dict'), 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='', flow={}) # 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') newAuthenticationRepresentation = {} newAuthenticationRepresentation["alias"] = module.params.get("alias") newAuthenticationRepresentation["copyFrom"] = module.params.get("copyFrom") newAuthenticationRepresentation["providerId"] = module.params.get( "providerId") newAuthenticationRepresentation[ "authenticationExecutions"] = module.params.get( "authenticationExecutions") changed = False authenticationRepresentation = kc.get_authentication_flow_by_alias( alias=newAuthenticationRepresentation["alias"], realm=realm) if authenticationRepresentation == {}: # Authentication flow does not exist if (state == 'present'): # If desired state is prenset # If copyFrom is defined, create authentication flow from a copy if "copyFrom" in newAuthenticationRepresentation and newAuthenticationRepresentation[ "copyFrom"] is not None: authenticationRepresentation = kc.copy_auth_flow( config=newAuthenticationRepresentation, realm=realm) else: # Create an empty authentication flow authenticationRepresentation = kc.create_empty_auth_flow( config=newAuthenticationRepresentation, realm=realm) # If the authentication still not exist on the server, raise an exception. if authenticationRepresentation is None: result[ 'msg'] = "Authentication just created not found: " + str( newAuthenticationRepresentation) module.fail_json(**result) # Configure the executions for the flow kc.create_or_update_executions( config=newAuthenticationRepresentation, realm=realm) changed = True # Get executions created executionsRepresentation = kc.get_executions_representation( config=newAuthenticationRepresentation, realm=realm) if executionsRepresentation is not None: authenticationRepresentation[ "authenticationExecutions"] = executionsRepresentation result['changed'] = changed result['flow'] = authenticationRepresentation elif state == 'absent': # If desired state is absent. result[ 'msg'] = newAuthenticationRepresentation["alias"] + ' absent' else: # The authentication flow already exist if (state == 'present'): # if desired state is present if force: # If force option is true # Delete the actual authentication flow kc.delete_authentication_flow_by_id( id=authenticationRepresentation["id"], realm=realm) changed = True # If copyFrom is defined, create authentication flow from a copy if "copyFrom" in newAuthenticationRepresentation and newAuthenticationRepresentation[ "copyFrom"] is not None: authenticationRepresentation = kc.copy_auth_flow( config=newAuthenticationRepresentation, realm=realm) else: # Create an empty authentication flow authenticationRepresentation = kc.create_empty_auth_flow( config=newAuthenticationRepresentation, realm=realm) # If the authentication still not exist on the server, raise an exception. if authenticationRepresentation is None: result[ 'msg'] = "Authentication just created not found: " + str( newAuthenticationRepresentation) result['changed'] = changed module.fail_json(**result) # Configure the executions for the flow if kc.create_or_update_executions( config=newAuthenticationRepresentation, realm=realm): changed = True # Get executions created executionsRepresentation = kc.get_executions_representation( config=newAuthenticationRepresentation, realm=realm) if executionsRepresentation is not None: authenticationRepresentation[ "authenticationExecutions"] = executionsRepresentation result['flow'] = authenticationRepresentation result['changed'] = changed elif state == 'absent': # If desired state is absent # Delete the authentication flow alias. kc.delete_authentication_flow_by_id( id=authenticationRepresentation["id"], realm=realm) changed = True result[ 'msg'] = 'Authentication flow: ' + newAuthenticationRepresentation[ 'alias'] + ' id: ' + authenticationRepresentation[ "id"] + ' is deleted' result['changed'] = changed module.exit_json(**result)