def main(): """ Module execution :return: """ argument_spec = dict( auth_keycloak_url=dict(type='str', aliases=['url'], required=True, no_log=False), validate_certs=dict(type='bool', default=True), realm=dict(default='master'), ) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) result = dict(changed=False, msg='', realm_info='') kc = KeycloakAPI(module, {}) realm = module.params.get('realm') realm_info = kc.get_realm_info_by_id(realm=realm) result['realm_info'] = realm_info result['msg'] = 'Get realm public info successful for ID {realm}'.format( realm=realm) 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'], no_log=True), 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']), 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']), authentication_flow_binding_overrides=dict( type='dict', aliases=['authenticationFlowBindingOverrides']), 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'], [ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') cid = module.params.get('id') state = module.params.get('state') # Filter and map the parameters names that apply to the client 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 ] # See if it 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 = {} # Build a proposed changeset from parameters given to this module changeset = {} 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 # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_client = before_client.copy() desired_client.update(changeset) result['proposed'] = sanitize_cr(changeset) result['existing'] = sanitize_cr(before_client) # Cater for when it doesn't exist (an empty dict) if not before_client: if state == 'absent': # Do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = {} result['msg'] = 'Client does not exist; doing nothing.' module.exit_json(**result) # Process a creation result['changed'] = True if 'clientId' not in desired_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(desired_client)) if module.check_mode: module.exit_json(**result) # create it kc.create_client(desired_client, realm=realm) after_client = kc.get_client_by_clientid(desired_client['clientId'], realm=realm) result['end_state'] = sanitize_cr(after_client) result[ 'msg'] = 'Client %s has been created.' % desired_client['clientId'] module.exit_json(**result) else: if state == 'present': # Process an update result['changed'] = True if module.check_mode: # We can only compare the current client with the proposed updates we have before_norm = normalise_cr(before_client, remove_ids=True) desired_norm = normalise_cr(desired_client, remove_ids=True) if module._diff: result['diff'] = dict(before=sanitize_cr(before_norm), after=sanitize_cr(desired_norm)) result['changed'] = (before_norm != desired_norm) module.exit_json(**result) # do the update kc.update_client(cid, desired_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.' % desired_client[ 'clientId'] module.exit_json(**result) else: # Process a deletion (because state was not 'present') result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize_cr(before_client), after='') if module.check_mode: module.exit_json(**result) # delete it kc.delete_client(cid, realm=realm) result['proposed'] = {} result['end_state'] = {} result['msg'] = 'Client %s has been deleted.' % before_client[ 'clientId'] module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() meta_args = dict( state=dict(default='present', choices=['present', 'absent']), id=dict(type='str'), realm=dict(type='str'), access_code_lifespan=dict(type='int', aliases=['accessCodeLifespan']), access_code_lifespan_login=dict(type='int', aliases=['accessCodeLifespanLogin']), access_code_lifespan_user_action=dict( type='int', aliases=['accessCodeLifespanUserAction']), access_token_lifespan=dict(type='int', aliases=['accessTokenLifespan'], no_log=False), access_token_lifespan_for_implicit_flow=dict( type='int', aliases=['accessTokenLifespanForImplicitFlow'], no_log=False), account_theme=dict(type='str', aliases=['accountTheme']), action_token_generated_by_admin_lifespan=dict( type='int', aliases=['actionTokenGeneratedByAdminLifespan'], no_log=False), action_token_generated_by_user_lifespan=dict( type='int', aliases=['actionTokenGeneratedByUserLifespan'], no_log=False), admin_events_details_enabled=dict( type='bool', aliases=['adminEventsDetailsEnabled']), admin_events_enabled=dict(type='bool', aliases=['adminEventsEnabled']), admin_theme=dict(type='str', aliases=['adminTheme']), attributes=dict(type='dict'), browser_flow=dict(type='str', aliases=['browserFlow']), browser_security_headers=dict(type='dict', aliases=['browserSecurityHeaders']), brute_force_protected=dict(type='bool', aliases=['bruteForceProtected']), client_authentication_flow=dict(type='str', aliases=['clientAuthenticationFlow']), client_scope_mappings=dict(type='dict', aliases=['clientScopeMappings']), default_default_client_scopes=dict( type='list', elements='dict', aliases=['defaultDefaultClientScopes']), default_groups=dict(type='list', elements='dict', aliases=['defaultGroups']), default_locale=dict(type='str', aliases=['defaultLocale']), default_optional_client_scopes=dict( type='list', elements='dict', aliases=['defaultOptionalClientScopes']), default_roles=dict(type='list', elements='dict', aliases=['defaultRoles']), default_signature_algorithm=dict(type='str', aliases=['defaultSignatureAlgorithm' ]), direct_grant_flow=dict(type='str', aliases=['directGrantFlow']), display_name=dict(type='str', aliases=['displayName']), display_name_html=dict(type='str', aliases=['displayNameHtml']), docker_authentication_flow=dict(type='str', aliases=['dockerAuthenticationFlow']), duplicate_emails_allowed=dict(type='bool', aliases=['duplicateEmailsAllowed']), edit_username_allowed=dict(type='bool', aliases=['editUsernameAllowed']), email_theme=dict(type='str', aliases=['emailTheme']), enabled=dict(type='bool'), enabled_event_types=dict(type='list', elements='str', aliases=['enabledEventTypes']), events_expiration=dict(type='int', aliases=['eventsExpiration']), events_listeners=dict(type='list', elements='dict', aliases=['eventsListeners']), failure_factor=dict(type='int', aliases=['failureFactor']), internationalization_enabled=dict( type='bool', aliases=['internationalizationEnabled']), login_theme=dict(type='str', aliases=['loginTheme']), login_with_email_allowed=dict(type='bool', aliases=['loginWithEmailAllowed']), max_delta_time_seconds=dict(type='int', aliases=['maxDeltaTimeSeconds']), max_failure_wait_seconds=dict(type='int', aliases=['maxFailureWaitSeconds']), minimum_quick_login_wait_seconds=dict( type='int', aliases=['minimumQuickLoginWaitSeconds']), not_before=dict(type='int', aliases=['notBefore']), offline_session_idle_timeout=dict( type='int', aliases=['offlineSessionIdleTimeout']), offline_session_max_lifespan=dict( type='int', aliases=['offlineSessionMaxLifespan']), offline_session_max_lifespan_enabled=dict( type='bool', aliases=['offlineSessionMaxLifespanEnabled']), otp_policy_algorithm=dict(type='str', aliases=['otpPolicyAlgorithm']), otp_policy_digits=dict(type='int', aliases=['otpPolicyDigits']), otp_policy_initial_counter=dict(type='int', aliases=['otpPolicyInitialCounter']), otp_policy_look_ahead_window=dict(type='int', aliases=['otpPolicyLookAheadWindow' ]), otp_policy_period=dict(type='int', aliases=['otpPolicyPeriod']), otp_policy_type=dict(type='str', aliases=['otpPolicyType']), otp_supported_applications=dict(type='list', elements='str', aliases=['otpSupportedApplications']), password_policy=dict(type='str', aliases=['passwordPolicy'], no_log=False), permanent_lockout=dict(type='bool', aliases=['permanentLockout']), quick_login_check_milli_seconds=dict( type='int', aliases=['quickLoginCheckMilliSeconds']), refresh_token_max_reuse=dict(type='int', aliases=['refreshTokenMaxReuse'], no_log=False), registration_allowed=dict(type='bool', aliases=['registrationAllowed']), registration_email_as_username=dict( type='bool', aliases=['registrationEmailAsUsername']), registration_flow=dict(type='str', aliases=['registrationFlow']), remember_me=dict(type='bool', aliases=['rememberMe']), reset_credentials_flow=dict(type='str', aliases=['resetCredentialsFlow']), reset_password_allowed=dict(type='bool', aliases=['resetPasswordAllowed']), revoke_refresh_token=dict(type='bool', aliases=['revokeRefreshToken']), smtp_server=dict(type='dict', aliases=['smtpServer']), ssl_required=dict(type='bool', aliases=['sslRequired']), sso_session_idle_timeout=dict(type='int', aliases=['ssoSessionIdleTimeout']), sso_session_idle_timeout_remember_me=dict( type='int', aliases=['ssoSessionIdleTimeoutRememberMe']), sso_session_max_lifespan=dict(type='int', aliases=['ssoSessionMaxLifespan']), sso_session_max_lifespan_remember_me=dict( type='int', aliases=['ssoSessionMaxLifespanRememberMe']), supported_locales=dict(type='list', elements='str', aliases=['supportedLocales']), user_managed_access_allowed=dict(type='bool', aliases=['userManagedAccessAllowed']), verify_email=dict(type='bool', aliases=['verifyEmail']), wait_increment_seconds=dict(type='int', aliases=['waitIncrementSeconds']), ) argument_spec.update(meta_args) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=([['id', 'realm', 'enabled'], [ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') # convert module parameters to realm representation parameters (if they belong in there) params_to_ignore = list(keycloak_argument_spec().keys()) + ['state'] realm_params = [ x for x in module.params if x not in params_to_ignore and module.params.get(x) is not None ] # See whether the realm already exists in Keycloak before_realm = kc.get_realm_by_id(realm=realm) or {} # Build a proposed changeset from parameters given to this module changeset = dict() for realm_param in realm_params: new_param_value = module.params.get(realm_param) changeset[camel(realm_param)] = new_param_value # Whether creating or updating a realm, take the before-state and merge the changeset into it updated_realm = before_realm.copy() updated_realm.update(changeset) result['proposed'] = sanitize_cr(changeset) before_realm_sanitized = sanitize_cr(before_realm) result['existing'] = before_realm_sanitized # If the realm does not exist yet, before_realm is still empty if not before_realm: if state == 'absent': # do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['msg'] = 'Realm does not exist, doing nothing.' module.exit_json(**result) # create new realm result['changed'] = True if 'id' not in updated_realm: module.fail_json( msg='id needs to be specified when creating a new realm') if module._diff: result['diff'] = dict(before='', after=sanitize_cr(updated_realm)) if module.check_mode: module.exit_json(**result) kc.create_realm(updated_realm) after_realm = kc.get_realm_by_id(updated_realm['id']) result['end_state'] = sanitize_cr(after_realm) result['msg'] = 'Realm %s has been created.' % updated_realm['id'] module.exit_json(**result) else: if state == 'present': # update existing realm result['changed'] = True if module.check_mode: # We can only compare the current realm with the proposed updates we have if module._diff: result['diff'] = dict(before=before_realm_sanitized, after=sanitize_cr(updated_realm)) result['changed'] = (before_realm != updated_realm) module.exit_json(**result) kc.update_realm(updated_realm, realm=realm) after_realm = kc.get_realm_by_id(realm=realm) if before_realm == after_realm: result['changed'] = False if module._diff: result['diff'] = dict(before=before_realm_sanitized, after=sanitize_cr(after_realm)) result['end_state'] = sanitize_cr(after_realm) result['msg'] = 'Realm %s has been updated.' % updated_realm['id'] module.exit_json(**result) else: # Delete existing realm result['changed'] = True if module._diff: result['diff']['before'] = before_realm_sanitized result['diff']['after'] = '' if module.check_mode: module.exit_json(**result) kc.delete_realm(realm=realm) result['proposed'] = dict() result['end_state'] = dict() result['msg'] = 'Realm %s has been deleted.' % before_realm['id'] module.exit_json(**result) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() meta_args = dict( realm=dict(type='str', required=True), alias=dict(type='str', required=True), providerId=dict(type='str'), description=dict(type='str'), copyFrom=dict(type='str'), authenticationExecutions=dict( type='list', elements='dict', options=dict( providerId=dict(type='str'), displayName=dict(type='str'), requirement=dict(choices=[ "REQUIRED", "ALTERNATIVE", "DISABLED", "CONDITIONAL" ], type='str'), flowAlias=dict(type='str'), authenticationConfig=dict(type='dict'), index=dict(type='int'), )), 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, required_one_of=([[ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', flow={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') force = module.params.get('force') new_auth_repr = { "alias": module.params.get("alias"), "copyFrom": module.params.get("copyFrom"), "providerId": module.params.get("providerId"), "authenticationExecutions": module.params.get("authenticationExecutions"), "description": module.params.get("description"), "builtIn": module.params.get("builtIn"), "subflow": module.params.get("subflow"), } auth_repr = kc.get_authentication_flow_by_alias( alias=new_auth_repr["alias"], realm=realm) if auth_repr == {}: # Authentication flow does not exist if state == 'present': # If desired state is present result['changed'] = True if module._diff: result['diff'] = dict(before='', after=new_auth_repr) if module.check_mode: module.exit_json(**result) # If copyFrom is defined, create authentication flow from a copy if "copyFrom" in new_auth_repr and new_auth_repr[ "copyFrom"] is not None: auth_repr = kc.copy_auth_flow(config=new_auth_repr, realm=realm) else: # Create an empty authentication flow auth_repr = kc.create_empty_auth_flow(config=new_auth_repr, realm=realm) # If the authentication still not exist on the server, raise an exception. if auth_repr is None: result[ 'msg'] = "Authentication just created not found: " + str( new_auth_repr) module.fail_json(**result) # Configure the executions for the flow create_or_update_executions(kc=kc, config=new_auth_repr, realm=realm) # Get executions created exec_repr = kc.get_executions_representation(config=new_auth_repr, realm=realm) if exec_repr is not None: auth_repr["authenticationExecutions"] = exec_repr result['flow'] = auth_repr elif state == 'absent': # If desired state is absent. if module._diff: result['diff'] = dict(before='', after='') result['msg'] = new_auth_repr["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 result['changed'] = True if module._diff: result['diff'] = dict(before=auth_repr, after=new_auth_repr) if module.check_mode: module.exit_json(**result) kc.delete_authentication_flow_by_id(id=auth_repr["id"], realm=realm) # If copyFrom is defined, create authentication flow from a copy if "copyFrom" in new_auth_repr and new_auth_repr[ "copyFrom"] is not None: auth_repr = kc.copy_auth_flow(config=new_auth_repr, realm=realm) else: # Create an empty authentication flow auth_repr = kc.create_empty_auth_flow(config=new_auth_repr, realm=realm) # If the authentication still not exist on the server, raise an exception. if auth_repr is None: result[ 'msg'] = "Authentication just created not found: " + str( new_auth_repr) module.fail_json(**result) # Configure the executions for the flow if module.check_mode: module.exit_json(**result) if create_or_update_executions(kc=kc, config=new_auth_repr, realm=realm): result['changed'] = True # Get executions created exec_repr = kc.get_executions_representation(config=new_auth_repr, realm=realm) if exec_repr is not None: auth_repr["authenticationExecutions"] = exec_repr result['flow'] = auth_repr elif state == 'absent': # If desired state is absent result['changed'] = True # Delete the authentication flow alias. if module._diff: result['diff'] = dict(before=auth_repr, after='') if module.check_mode: module.exit_json(**result) kc.delete_authentication_flow_by_id(id=auth_repr["id"], realm=realm) result[ 'msg'] = 'Authentication flow: {alias} id: {id} is deleted'.format( alias=new_auth_repr['alias'], id=auth_repr["id"]) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() meta_args = dict( state=dict(type='str', default='present', choices=['present', 'absent']), name=dict(type='str', required=True), description=dict(type='str'), realm=dict(type='str', default='master'), client_id=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=([[ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') clientid = module.params.get('client_id') name = module.params.get('name') state = module.params.get('state') # 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 module.params.get('attributes') is not None: for key, val in module.params['attributes'].items(): module.params['attributes'][key] = [ val ] if not isinstance(val, list) else val # convert module parameters to client representation parameters (if they belong in there) role_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'client_id', 'composites'] and module.params.get(x) is not None ] # does the role already exist? if clientid is None: before_role = kc.get_realm_role(name, realm) else: before_role = kc.get_client_role(name, clientid, realm) if before_role is None: before_role = dict() # build a changeset changeset = dict() for param in role_params: new_param_value = module.params.get(param) old_value = before_role[param] if param in before_role else None if new_param_value != old_value: changeset[camel(param)] = new_param_value # prepare the new role updated_role = before_role.copy() updated_role.update(changeset) result['proposed'] = changeset result['existing'] = before_role # if before_role is none, the role doesn't exist. if before_role == dict(): if state == 'absent': # nothing to do. if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = dict() result['msg'] = 'Role does not exist; doing nothing.' module.exit_json(**result) # for 'present', create a new role. result['changed'] = True if name is None: module.fail_json( msg='name must be specified when creating a new role') if module._diff: result['diff'] = dict(before='', after=updated_role) if module.check_mode: module.exit_json(**result) # do it for real! if clientid is None: kc.create_realm_role(updated_role, realm) after_role = kc.get_realm_role(name, realm) else: kc.create_client_role(updated_role, clientid, realm) after_role = kc.get_client_role(name, clientid, realm) result['end_state'] = after_role result['msg'] = 'Role {name} has been created'.format(name=name) module.exit_json(**result) else: if state == 'present': # no changes if updated_role == before_role: result['changed'] = False result['end_state'] = updated_role result['msg'] = "No changes required to role {name}.".format( name=name) module.exit_json(**result) # update the existing role result['changed'] = True if module._diff: result['diff'] = dict(before=before_role, after=updated_role) if module.check_mode: module.exit_json(**result) # do the update if clientid is None: kc.update_realm_role(updated_role, realm) after_role = kc.get_realm_role(name, realm) else: kc.update_client_role(updated_role, clientid, realm) after_role = kc.get_client_role(name, clientid, realm) result['end_state'] = after_role result['msg'] = "Role {name} has been updated".format(name=name) module.exit_json(**result) elif state == 'absent': result['changed'] = True if module._diff: result['diff'] = dict(before=before_role, after='') if module.check_mode: module.exit_json(**result) # delete for real if clientid is None: kc.delete_realm_role(name, realm) else: kc.delete_client_role(name, clientid, realm) result['end_state'] = dict() result['msg'] = "Role {name} has been deleted".format(name=name) module.exit_json(**result) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() mapper_spec = dict( id=dict(type='str'), name=dict(type='str'), identityProviderAlias=dict(type='str'), identityProviderMapper=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( state=dict(type='str', default='present', choices=['present', 'absent']), realm=dict(type='str', default='master'), alias=dict(type='str', required=True), add_read_token_role_on_create=dict( type='bool', aliases=['addReadTokenRoleOnCreate']), authenticate_by_default=dict(type='bool', aliases=['authenticateByDefault']), config=dict(type='dict'), display_name=dict(type='str', aliases=['displayName']), enabled=dict(type='bool'), first_broker_login_flow_alias=dict( type='str', aliases=['firstBrokerLoginFlowAlias']), link_only=dict(type='bool', aliases=['linkOnly']), post_broker_login_flow_alias=dict(type='str', aliases=['postBrokerLoginFlowAlias' ]), provider_id=dict(type='str', aliases=['providerId']), store_token=dict(type='bool', aliases=['storeToken']), trust_email=dict(type='bool', aliases=['trustEmail']), mappers=dict(type='list', elements='dict', options=mapper_spec), ) argument_spec.update(meta_args) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=([[ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') alias = module.params.get('alias') state = module.params.get('state') # convert module parameters to client representation parameters (if they belong in there) idp_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and module.params.get(x) is not None ] # does the identity provider already exist? before_idp = get_identity_provider_with_mappers(kc, alias, realm) # build a changeset changeset = dict() for param in idp_params: new_param_value = module.params.get(param) old_value = before_idp[camel(param)] if camel( param) in before_idp else None if new_param_value != old_value: changeset[camel(param)] = new_param_value # special handling of mappers list to allow change detection changeset['mappers'] = before_idp.get('mappers', list()) if module.params.get('mappers') is not None: for new_mapper in module.params.get('mappers'): old_mapper = next((x for x in changeset['mappers'] if x['name'] == new_mapper['name']), None) new_mapper = dict((k, v) for k, v in new_mapper.items() if new_mapper[k] is not None) if old_mapper is not None: old_mapper.update(new_mapper) else: changeset['mappers'].append(new_mapper) # remove mappers if not present in module params changeset['mappers'] = [ x for x in changeset['mappers'] if [ y for y in module.params.get('mappers', []) if y['name'] == x['name'] ] != [] ] # prepare the new representation updated_idp = before_idp.copy() updated_idp.update(changeset) result['proposed'] = sanitize(changeset) result['existing'] = sanitize(before_idp) # if before_idp is none, the identity provider doesn't exist. if before_idp == dict(): if state == 'absent': # nothing to do. if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = dict() result['msg'] = 'Identity provider does not exist; doing nothing.' module.exit_json(**result) # for 'present', create a new identity provider. result['changed'] = True if module._diff: result['diff'] = dict(before='', after=sanitize(updated_idp)) if module.check_mode: module.exit_json(**result) # do it for real! updated_idp = updated_idp.copy() mappers = updated_idp.pop('mappers', []) kc.create_identity_provider(updated_idp, realm) for mapper in mappers: kc.create_identity_provider_mapper(mapper, alias, realm) after_idp = get_identity_provider_with_mappers(kc, alias, realm) result['end_state'] = sanitize(after_idp) result['msg'] = 'Identity provider {alias} has been created'.format( alias=alias) module.exit_json(**result) else: if state == 'present': # no changes if updated_idp == before_idp: result['changed'] = False result['end_state'] = sanitize(updated_idp) result[ 'msg'] = "No changes required to identity provider {alias}.".format( alias=alias) module.exit_json(**result) # update the existing role result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_idp), after=sanitize(updated_idp)) if module.check_mode: module.exit_json(**result) # do the update updated_idp = updated_idp.copy() updated_mappers = updated_idp.pop('mappers', []) kc.update_identity_provider(updated_idp, realm) for mapper in updated_mappers: if mapper.get('id') is not None: kc.update_identity_provider_mapper(mapper, alias, realm) else: kc.create_identity_provider_mapper(mapper, alias, realm) for mapper in [ x for x in before_idp['mappers'] if [y for y in updated_mappers if y["name"] == x['name']] == [] ]: kc.delete_identity_provider_mapper(mapper['id'], alias, realm) after_idp = get_identity_provider_with_mappers(kc, alias, realm) result['end_state'] = sanitize(after_idp) result[ 'msg'] = "Identity provider {alias} has been updated".format( alias=alias) module.exit_json(**result) elif state == 'absent': result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_idp), after='') if module.check_mode: module.exit_json(**result) # delete for real kc.delete_identity_provider(alias, realm) result['end_state'] = dict() result[ 'msg'] = "Identity provider {alias} has been deleted".format( alias=alias) module.exit_json(**result) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() meta_args = dict(state=dict(default='present', choices=['present', 'absent']), realm=dict(default='master'), id=dict(type='str'), name=dict(type='str'), attributes=dict(type='dict')) argument_spec.update(meta_args) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, required_one_of=([['id', 'name']])) result = dict(changed=False, msg='', diff={}, group='') # Obtain access token, initialize API try: connection_header = get_token( base_url=module.params.get('auth_keycloak_url'), validate_certs=module.params.get('validate_certs'), auth_realm=module.params.get('auth_realm'), client_id=module.params.get('auth_client_id'), auth_username=module.params.get('auth_username'), auth_password=module.params.get('auth_password'), client_secret=module.params.get('auth_client_secret'), ) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') gid = module.params.get('id') name = module.params.get('name') attributes = module.params.get('attributes') before_group = None # current state of the group, for merging. # does the group already exist? if gid is None: before_group = kc.get_group_by_name(name, realm=realm) else: before_group = kc.get_group_by_groupid(gid, realm=realm) before_group = {} if before_group is None else before_group # attributes in Keycloak have their values returned as lists # via the API. attributes is a dict, so we'll transparently convert # the values to lists. if attributes is not None: for key, val in module.params['attributes'].items(): module.params['attributes'][key] = [ val ] if not isinstance(val, list) else val group_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm'] and module.params.get(x) is not None ] # build a changeset changeset = {} for param in group_params: new_param_value = module.params.get(param) old_value = before_group[param] if param in before_group else None if new_param_value != old_value: changeset[camel(param)] = new_param_value # prepare the new group updated_group = before_group.copy() updated_group.update(changeset) # if before_group is none, the group doesn't exist. if before_group == {}: if state == 'absent': # nothing to do. if module._diff: result['diff'] = dict(before='', after='') result['msg'] = 'Group does not exist; doing nothing.' result['group'] = dict() module.exit_json(**result) # for 'present', create a new group. result['changed'] = True if name is None: module.fail_json( msg='name must be specified when creating a new group') if module._diff: result['diff'] = dict(before='', after=updated_group) if module.check_mode: module.exit_json(**result) # do it for real! kc.create_group(updated_group, realm=realm) after_group = kc.get_group_by_name(name, realm) result['group'] = after_group result['msg'] = 'Group {name} has been created with ID {id}'.format( name=after_group['name'], id=after_group['id']) else: if state == 'present': # no changes if updated_group == before_group: result['changed'] = False result['group'] = updated_group result['msg'] = "No changes required to group {name}.".format( name=before_group['name']) module.exit_json(**result) # update the existing group result['changed'] = True if module._diff: result['diff'] = dict(before=before_group, after=updated_group) if module.check_mode: module.exit_json(**result) # do the update kc.update_group(updated_group, realm=realm) after_group = kc.get_group_by_groupid(updated_group['id'], realm=realm) result['group'] = after_group result['msg'] = "Group {id} has been updated".format( id=after_group['id']) module.exit_json(**result) elif state == 'absent': result['group'] = dict() if module._diff: result['diff'] = dict(before=before_group, after='') if module.check_mode: module.exit_json(**result) # delete for real gid = before_group['id'] kc.delete_group(groupid=gid, realm=realm) result['changed'] = True result['msg'] = "Group {name} has been deleted".format( name=before_group['name']) module.exit_json(**result) module.exit_json(**result)
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() config_spec = dict( allowKerberosAuthentication=dict(type='bool', default=False), allowPasswordAuthentication=dict(type='bool'), authType=dict(type='str', choices=['none', 'simple'], default='none'), batchSizeForSync=dict(type='int', default=1000), bindCredential=dict(type='str', no_log=True), bindDn=dict(type='str'), cachePolicy=dict(type='str', choices=[ 'DEFAULT', 'EVICT_DAILY', 'EVICT_WEEKLY', 'MAX_LIFESPAN', 'NO_CACHE' ], default='DEFAULT'), changedSyncPeriod=dict(type='int', default=-1), connectionPooling=dict(type='bool', default=True), connectionPoolingAuthentication=dict( type='str', choices=['none', 'simple', 'DIGEST-MD5']), connectionPoolingDebug=dict(type='str'), connectionPoolingInitSize=dict(type='int'), connectionPoolingMaxSize=dict(type='int'), connectionPoolingPrefSize=dict(type='int'), connectionPoolingProtocol=dict(type='str'), connectionPoolingTimeout=dict(type='int'), connectionTimeout=dict(type='int'), connectionUrl=dict(type='str'), customUserSearchFilter=dict(type='str'), debug=dict(type='bool'), editMode=dict(type='str', choices=['READ_ONLY', 'WRITABLE', 'UNSYNCED']), enabled=dict(type='bool', default=True), evictionDay=dict(type='str'), evictionHour=dict(type='str'), evictionMinute=dict(type='str'), fullSyncPeriod=dict(type='int', default=-1), importEnabled=dict(type='bool', default=True), kerberosRealm=dict(type='str'), keyTab=dict(type='str', no_log=False), maxLifespan=dict(type='int'), pagination=dict(type='bool', default=True), priority=dict(type='int', default=0), rdnLDAPAttribute=dict(type='str'), readTimeout=dict(type='int'), searchScope=dict(type='str', choices=['1', '2'], default='1'), serverPrincipal=dict(type='str'), startTls=dict(type='bool', default=False), syncRegistrations=dict(type='bool', default=False), trustEmail=dict(type='bool', default=False), updateProfileFirstLogin=dict(type='bool'), useKerberosForPasswordAuthentication=dict(type='bool', default=False), usePasswordModifyExtendedOp=dict(type='bool', default=False, no_log=False), useTruststoreSpi=dict(type='str', choices=['always', 'ldapsOnly', 'never'], default='ldapsOnly'), userObjectClasses=dict(type='str'), usernameLDAPAttribute=dict(type='str'), usersDn=dict(type='str'), uuidLDAPAttribute=dict(type='str'), validatePasswordPolicy=dict(type='bool', default=False), vendor=dict(type='str'), ) mapper_spec = dict( id=dict(type='str'), name=dict(type='str'), parentId=dict(type='str'), providerId=dict(type='str'), providerType=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( config=dict(type='dict', options=config_spec), state=dict(type='str', default='present', choices=['present', 'absent']), realm=dict(type='str', default='master'), id=dict(type='str'), name=dict(type='str'), provider_id=dict(type='str', aliases=['providerId'], choices=['ldap', 'kerberos', 'sssd']), provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'), parent_id=dict(type='str', aliases=['parentId']), mappers=dict(type='list', elements='dict', options=mapper_spec), ) argument_spec.update(meta_args) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=([['id', 'name'], [ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') config = module.params.get('config') mappers = module.params.get('mappers') cid = module.params.get('id') name = module.params.get('name') # Keycloak API expects config parameters to be arrays containing a single string element if config is not None: module.params['config'] = dict( (k, [str(v).lower() if not isinstance(v, str) else v]) for k, v in config.items() if config[k] is not None) if mappers is not None: for mapper in mappers: if mapper.get('config') is not None: mapper['config'] = dict( (k, [str(v).lower() if not isinstance(v, str) else v]) for k, v in mapper['config'].items() if mapper['config'][k] is not None) # Filter and map the parameters names that apply comp_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and module.params.get(x) is not None ] # See if it already exists in Keycloak if cid is None: found = kc.get_components( urlencode( dict(type='org.keycloak.storage.UserStorageProvider', parent=realm, name=name)), realm) if len(found) > 1: module.fail_json( msg= 'No ID given and found multiple user federations with name `{name}`. Cannot continue.' .format(name=name)) before_comp = next(iter(found), None) if before_comp is not None: cid = before_comp['id'] else: before_comp = kc.get_component(cid, realm) if before_comp is None: before_comp = {} # if user federation exists, get associated mappers if cid is not None and before_comp: before_comp['mappers'] = sorted(kc.get_components( urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name')) # Build a proposed changeset from parameters given to this module changeset = {} for param in comp_params: new_param_value = module.params.get(param) old_value = before_comp[camel(param)] if camel( param) in before_comp else None if param == '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 new_param_value != old_value: changeset[camel(param)] = new_param_value # special handling of mappers list to allow change detection if module.params.get('mappers') is not None: if module.params['provider_id'] in ['kerberos', 'sssd']: module.fail_json( msg='Cannot configure mappers for {type} provider.'.format( type=module.params['provider_id'])) for change in module.params['mappers']: change = dict( (k, v) for k, v in change.items() if change[k] is not None) if change.get('id') is None and change.get('name') is None: module.fail_json( msg= 'Either `name` or `id` has to be specified on each mapper.' ) if cid is None: old_mapper = {} elif change.get('id') is not None: old_mapper = kc.get_component(change['id'], realm) if old_mapper is None: old_mapper = {} else: found = kc.get_components( urlencode(dict(parent=cid, name=change['name'])), realm) if len(found) > 1: module.fail_json( msg= 'Found multiple mappers with name `{name}`. Cannot continue.' .format(name=change['name'])) if len(found) == 1: old_mapper = found[0] else: old_mapper = {} new_mapper = old_mapper.copy() new_mapper.update(change) if new_mapper != old_mapper: if changeset.get('mappers') is None: changeset['mappers'] = list() changeset['mappers'].append(new_mapper) # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_comp = before_comp.copy() desired_comp.update(changeset) result['proposed'] = sanitize(changeset) result['existing'] = sanitize(before_comp) # Cater for when it doesn't exist (an empty dict) if not before_comp: if state == 'absent': # Do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = {} result['msg'] = 'User federation does not exist; doing nothing.' module.exit_json(**result) # Process a creation result['changed'] = True if module._diff: result['diff'] = dict(before='', after=sanitize(desired_comp)) if module.check_mode: module.exit_json(**result) # create it desired_comp = desired_comp.copy() updated_mappers = desired_comp.pop('mappers', []) after_comp = kc.create_component(desired_comp, realm) for mapper in updated_mappers: found = kc.get_components( urlencode(dict(parent=cid, name=mapper['name'])), realm) if len(found) > 1: module.fail_json( msg= 'Found multiple mappers with name `{name}`. Cannot continue.' .format(name=mapper['name'])) if len(found) == 1: old_mapper = found[0] else: old_mapper = {} new_mapper = old_mapper.copy() new_mapper.update(mapper) if new_mapper.get('id') is not None: kc.update_component(new_mapper, realm) else: if new_mapper.get('parentId') is None: new_mapper['parentId'] = after_comp['id'] mapper = kc.create_component(new_mapper, realm) after_comp['mappers'] = updated_mappers result['end_state'] = sanitize(after_comp) result['msg'] = "User federation {id} has been created".format( id=after_comp['id']) module.exit_json(**result) else: if state == 'present': # Process an update # no changes if desired_comp == before_comp: result['changed'] = False result['end_state'] = sanitize(desired_comp) result[ 'msg'] = "No changes required to user federation {id}.".format( id=cid) module.exit_json(**result) # doing an update result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_comp), after=sanitize(desired_comp)) if module.check_mode: module.exit_json(**result) # do the update desired_comp = desired_comp.copy() updated_mappers = desired_comp.pop('mappers', []) kc.update_component(desired_comp, realm) after_comp = kc.get_component(cid, realm) for mapper in updated_mappers: if mapper.get('id') is not None: kc.update_component(mapper, realm) else: if mapper.get('parentId') is None: mapper['parentId'] = desired_comp['id'] mapper = kc.create_component(mapper, realm) after_comp['mappers'] = updated_mappers result['end_state'] = sanitize(after_comp) result['msg'] = "User federation {id} has been updated".format( id=cid) module.exit_json(**result) elif state == 'absent': # Process a deletion result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_comp), after='') if module.check_mode: module.exit_json(**result) # delete it kc.delete_component(cid, realm) result['end_state'] = {} result['msg'] = "User federation {id} has been deleted".format( id=cid) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() mapper_spec = dict( id=dict(type='str'), name=dict(type='str'), identityProviderAlias=dict(type='str'), identityProviderMapper=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( state=dict(type='str', default='present', choices=['present', 'absent']), realm=dict(type='str', default='master'), alias=dict(type='str', required=True), add_read_token_role_on_create=dict( type='bool', aliases=['addReadTokenRoleOnCreate']), authenticate_by_default=dict(type='bool', aliases=['authenticateByDefault']), config=dict(type='dict'), display_name=dict(type='str', aliases=['displayName']), enabled=dict(type='bool'), first_broker_login_flow_alias=dict( type='str', aliases=['firstBrokerLoginFlowAlias']), link_only=dict(type='bool', aliases=['linkOnly']), post_broker_login_flow_alias=dict(type='str', aliases=['postBrokerLoginFlowAlias' ]), provider_id=dict(type='str', aliases=['providerId']), store_token=dict(type='bool', aliases=['storeToken']), trust_email=dict(type='bool', aliases=['trustEmail']), mappers=dict(type='list', elements='dict', options=mapper_spec), ) argument_spec.update(meta_args) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=([[ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') alias = module.params.get('alias') state = module.params.get('state') # Filter and map the parameters names that apply to the identity provider. idp_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and module.params.get(x) is not None ] # See if it already exists in Keycloak before_idp = get_identity_provider_with_mappers(kc, alias, realm) # Build a proposed changeset from parameters given to this module changeset = {} for param in idp_params: new_param_value = module.params.get(param) old_value = before_idp[camel(param)] if camel( param) in before_idp else None if new_param_value != old_value: changeset[camel(param)] = new_param_value # special handling of mappers list to allow change detection if module.params.get('mappers') is not None: for change in module.params['mappers']: change = dict( (k, v) for k, v in change.items() if change[k] is not None) if change.get('id') is None and change.get('name') is None: module.fail_json( msg= 'Either `name` or `id` has to be specified on each mapper.' ) if before_idp == dict(): old_mapper = dict() elif change.get('id') is not None: old_mapper = kc.get_identity_provider_mapper( change['id'], alias, realm) if old_mapper is None: old_mapper = dict() else: found = [ x for x in kc.get_identity_provider_mappers(alias, realm) if x['name'] == change['name'] ] if len(found) == 1: old_mapper = found[0] else: old_mapper = dict() new_mapper = old_mapper.copy() new_mapper.update(change) if new_mapper != old_mapper: if changeset.get('mappers') is None: changeset['mappers'] = list() changeset['mappers'].append(new_mapper) # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_idp = before_idp.copy() desired_idp.update(changeset) result['proposed'] = sanitize(changeset) result['existing'] = sanitize(before_idp) # Cater for when it doesn't exist (an empty dict) if not before_idp: if state == 'absent': # Do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = {} result['msg'] = 'Identity provider does not exist; doing nothing.' module.exit_json(**result) # Process a creation result['changed'] = True if module._diff: result['diff'] = dict(before='', after=sanitize(desired_idp)) if module.check_mode: module.exit_json(**result) # create it desired_idp = desired_idp.copy() mappers = desired_idp.pop('mappers', []) kc.create_identity_provider(desired_idp, realm) for mapper in mappers: if mapper.get('identityProviderAlias') is None: mapper['identityProviderAlias'] = alias kc.create_identity_provider_mapper(mapper, alias, realm) after_idp = get_identity_provider_with_mappers(kc, alias, realm) result['end_state'] = sanitize(after_idp) result['msg'] = 'Identity provider {alias} has been created'.format( alias=alias) module.exit_json(**result) else: if state == 'present': # Process an update # no changes if desired_idp == before_idp: result['changed'] = False result['end_state'] = sanitize(desired_idp) result[ 'msg'] = "No changes required to identity provider {alias}.".format( alias=alias) module.exit_json(**result) # doing an update result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_idp), after=sanitize(desired_idp)) if module.check_mode: module.exit_json(**result) # do the update desired_idp = desired_idp.copy() updated_mappers = desired_idp.pop('mappers', []) kc.update_identity_provider(desired_idp, realm) for mapper in updated_mappers: if mapper.get('id') is not None: kc.update_identity_provider_mapper(mapper, alias, realm) else: if mapper.get('identityProviderAlias') is None: mapper['identityProviderAlias'] = alias kc.create_identity_provider_mapper(mapper, alias, realm) for mapper in [ x for x in before_idp['mappers'] if [y for y in updated_mappers if y["name"] == x['name']] == [] ]: kc.delete_identity_provider_mapper(mapper['id'], alias, realm) after_idp = get_identity_provider_with_mappers(kc, alias, realm) result['end_state'] = sanitize(after_idp) result[ 'msg'] = "Identity provider {alias} has been updated".format( alias=alias) module.exit_json(**result) elif state == 'absent': # Process a deletion result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_idp), after='') if module.check_mode: module.exit_json(**result) # delete it kc.delete_identity_provider(alias, realm) result['end_state'] = {} result[ 'msg'] = "Identity provider {alias} has been deleted".format( alias=alias) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() meta_args = dict( state=dict(type='str', default='present', choices=['present', 'absent']), name=dict(type='str', required=True), description=dict(type='str'), realm=dict(type='str', default='master'), client_id=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=([[ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') clientid = module.params.get('client_id') name = module.params.get('name') state = module.params.get('state') # 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 module.params.get('attributes') is not None: for key, val in module.params['attributes'].items(): module.params['attributes'][key] = [ val ] if not isinstance(val, list) else val # Filter and map the parameters names that apply to the role role_params = [ x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'client_id', 'composites'] and module.params.get(x) is not None ] # See if it already exists in Keycloak if clientid is None: before_role = kc.get_realm_role(name, realm) else: before_role = kc.get_client_role(name, clientid, realm) if before_role is None: before_role = {} # Build a proposed changeset from parameters given to this module changeset = {} for param in role_params: new_param_value = module.params.get(param) old_value = before_role[param] if param in before_role else None if new_param_value != old_value: changeset[camel(param)] = new_param_value # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_role = before_role.copy() desired_role.update(changeset) result['proposed'] = changeset result['existing'] = before_role # Cater for when it doesn't exist (an empty dict) if not before_role: if state == 'absent': # Do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = {} result['msg'] = 'Role does not exist, doing nothing.' module.exit_json(**result) # Process a creation result['changed'] = True if name is None: module.fail_json( msg='name must be specified when creating a new role') if module._diff: result['diff'] = dict(before='', after=desired_role) if module.check_mode: module.exit_json(**result) # create it if clientid is None: kc.create_realm_role(desired_role, realm) after_role = kc.get_realm_role(name, realm) else: kc.create_client_role(desired_role, clientid, realm) after_role = kc.get_client_role(name, clientid, realm) result['end_state'] = after_role result['msg'] = 'Role {name} has been created'.format(name=name) module.exit_json(**result) else: if state == 'present': # Process an update # no changes if desired_role == before_role: result['changed'] = False result['end_state'] = desired_role result['msg'] = "No changes required to role {name}.".format( name=name) module.exit_json(**result) # doing an update result['changed'] = True if module._diff: result['diff'] = dict(before=before_role, after=desired_role) if module.check_mode: module.exit_json(**result) # do the update if clientid is None: kc.update_realm_role(desired_role, realm) after_role = kc.get_realm_role(name, realm) else: kc.update_client_role(desired_role, clientid, realm) after_role = kc.get_client_role(name, clientid, realm) result['end_state'] = after_role result['msg'] = "Role {name} has been updated".format(name=name) module.exit_json(**result) else: # Process a deletion (because state was not 'present') result['changed'] = True if module._diff: result['diff'] = dict(before=before_role, after='') if module.check_mode: module.exit_json(**result) # delete it if clientid is None: kc.delete_realm_role(name, realm) else: kc.delete_client_role(name, clientid, realm) result['end_state'] = {} result['msg'] = "Role {name} has been deleted".format(name=name) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() roles_spec = dict( name=dict(type='str'), id=dict(type='str'), ) meta_args = dict( state=dict(default='present', choices=['present', 'absent']), realm=dict(default='master'), gid=dict(type='str'), group_name=dict(type='str'), cid=dict(type='str'), client_id=dict(type='str'), roles=dict(type='list', elements='dict', options=roles_spec), ) argument_spec.update(meta_args) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=([[ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') cid = module.params.get('cid') client_id = module.params.get('client_id') gid = module.params.get('gid') group_name = module.params.get('group_name') roles = module.params.get('roles') # Check the parameters if cid is None and client_id is None: module.fail_json( msg='Either the `client_id` or `cid` has to be specified.') if gid is None and group_name is None: module.fail_json( msg='Either the `group_name` or `gid` has to be specified.') # Get the potential missing parameters if gid is None: group_rep = kc.get_group_by_name(group_name, realm=realm) if group_rep is not None: gid = group_rep['id'] else: module.fail_json(msg='Could not fetch group %s:' % group_name) if cid is None: cid = kc.get_client_id(client_id, realm=realm) if cid is None: module.fail_json(msg='Could not fetch client %s:' % client_id) if roles is None: module.exit_json(msg="Nothing to do (no roles specified).") else: for role_index, role in enumerate(roles, start=0): if role['name'] is None and role['id'] is None: module.fail_json( msg= 'Either the `name` or `id` has to be specified on each role.' ) # Fetch missing role_id if role['id'] is None: role_id = kc.get_client_role_by_name(gid, cid, role['name'], realm=realm) if role_id is not None: role['id'] = role_id else: module.fail_json(msg='Could not fetch role %s:' % (role['name'])) # Fetch missing role_name else: role['name'] = kc.get_client_rolemapping_by_id( gid, cid, role['id'], realm=realm)['name'] if role['name'] is None: module.fail_json(msg='Could not fetch role %s' % (role['id'])) # Get effective client-level role mappings available_roles_before = kc.get_client_available_rolemappings(gid, cid, realm=realm) assigned_roles_before = kc.get_client_composite_rolemappings(gid, cid, realm=realm) result['existing'] = assigned_roles_before result['proposed'] = roles update_roles = [] for role_index, role in enumerate(roles, start=0): # Fetch roles to assign if state present if state == 'present': for available_role in available_roles_before: if role['name'] == available_role['name']: update_roles.append({ 'id': role['id'], 'name': role['name'], }) # Fetch roles to remove if state absent else: for assigned_role in assigned_roles_before: if role['name'] == assigned_role['name']: update_roles.append({ 'id': role['id'], 'name': role['name'], }) if len(update_roles): if state == 'present': # Assign roles result['changed'] = True if module._diff: result['diff'] = dict(before=assigned_roles_before, after=update_roles) if module.check_mode: module.exit_json(**result) kc.add_group_rolemapping(gid, cid, update_roles, realm=realm) result['msg'] = 'Roles %s assigned to group %s.' % (update_roles, group_name) assigned_roles_after = kc.get_client_composite_rolemappings( gid, cid, realm=realm) result['end_state'] = assigned_roles_after module.exit_json(**result) else: # Remove mapping of role result['changed'] = True if module._diff: result['diff'] = dict(before=assigned_roles_before, after=update_roles) if module.check_mode: module.exit_json(**result) kc.delete_group_rolemapping(gid, cid, update_roles, realm=realm) result['msg'] = 'Roles %s removed from group %s.' % (update_roles, group_name) assigned_roles_after = kc.get_client_composite_rolemappings( gid, cid, realm=realm) result['end_state'] = assigned_roles_after module.exit_json(**result) # Do nothing else: result['changed'] = False result[ 'msg'] = 'Nothing to do, roles %s are correctly mapped with group %s.' % ( roles, group_name) module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() 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'], [ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') cid = module.params.get('id') # Filter and map the parameters names that apply to the client template 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 if it 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 = {} result['existing'] = before_clientt # Build a proposed changeset from parameters given to this module changeset = {} 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 # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_clientt = before_clientt.copy() desired_clientt.update(changeset) result['proposed'] = changeset # Cater for when it doesn't exist (an empty dict) if not before_clientt: if state == 'absent': # Do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = {} result['msg'] = 'Client template does not exist, doing nothing.' module.exit_json(**result) # Process a creation result['changed'] = True if 'name' not in desired_clientt: module.fail_json( msg='name needs to be specified when creating a new client') if module._diff: result['diff'] = dict(before='', after=desired_clientt) if module.check_mode: module.exit_json(**result) # create it kc.create_client_template(desired_clientt, realm=realm) after_clientt = kc.get_client_template_by_name(desired_clientt['name'], realm=realm) result['end_state'] = after_clientt result[ 'msg'] = 'Client template %s has been created.' % desired_clientt[ 'name'] module.exit_json(**result) else: if state == 'present': # Process an update 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=desired_clientt) module.exit_json(**result) # do the update kc.update_client_template(cid, desired_clientt, realm=realm) after_clientt = kc.get_client_template_by_id(cid, realm=realm) if before_clientt == after_clientt: result['changed'] = False result['end_state'] = after_clientt if module._diff: result['diff'] = dict(before=before_clientt, after=after_clientt) result[ 'msg'] = 'Client template %s has been updated.' % desired_clientt[ 'name'] module.exit_json(**result) else: # Process a deletion (because state was not 'present') result['changed'] = True if module._diff: result['diff'] = dict(before=before_clientt, after='') if module.check_mode: module.exit_json(**result) # delete it kc.delete_client_template(cid, realm=realm) result['proposed'] = {} result['end_state'] = {} result[ 'msg'] = 'Client template %s has been deleted.' % before_clientt[ 'name'] module.exit_json(**result)
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() protmapper_spec = dict( id=dict(type='str'), name=dict(type='str'), protocol=dict(type='str', choices=['openid-connect', 'saml', 'wsfed']), protocolMapper=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( state=dict(default='present', choices=['present', 'absent']), realm=dict(default='master'), id=dict(type='str'), name=dict(type='str'), description=dict(type='str'), protocol=dict(type='str', choices=['openid-connect', 'saml', 'wsfed']), attributes=dict(type='dict'), protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']), ) argument_spec.update(meta_args) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=([['id', 'name'], [ 'token', 'auth_realm', 'auth_username', 'auth_password' ]]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') cid = module.params.get('id') name = module.params.get('name') protocol_mappers = module.params.get('protocol_mappers') before_clientscope = None # current state of the clientscope, for merging. # does the clientscope already exist? if cid is None: before_clientscope = kc.get_clientscope_by_name(name, realm=realm) else: before_clientscope = kc.get_clientscope_by_clientscopeid(cid, realm=realm) before_clientscope = {} if before_clientscope is None else before_clientscope clientscope_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 proposed changeset from parameters given to this module changeset = dict() for clientscope_param in clientscope_params: new_param_value = module.params.get(clientscope_param) # some lists in the Keycloak API are sorted, some are not. if isinstance(new_param_value, list): if clientscope_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 clientscope_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(clientscope_param)] = new_param_value # prepare the new clientscope updated_clientscope = before_clientscope.copy() updated_clientscope.update(changeset) # if before_clientscope is none, the clientscope doesn't exist. if before_clientscope == {}: if state == 'absent': # nothing to do. if module._diff: result['diff'] = dict(before='', after='') result['msg'] = 'Clientscope does not exist; doing nothing.' result['end_state'] = dict() module.exit_json(**result) # for 'present', create a new clientscope. result['changed'] = True if name is None: module.fail_json( msg='name must be specified when creating a new clientscope') if module._diff: result['diff'] = dict(before='', after=sanitize_cr(updated_clientscope)) if module.check_mode: module.exit_json(**result) # do it for real! kc.create_clientscope(updated_clientscope, realm=realm) after_clientscope = kc.get_clientscope_by_name(name, realm) result['end_state'] = sanitize_cr(after_clientscope) result[ 'msg'] = 'Clientscope {name} has been created with ID {id}'.format( name=after_clientscope['name'], id=after_clientscope['id']) else: if state == 'present': # no changes if updated_clientscope == before_clientscope: result['changed'] = False result['end_state'] = sanitize_cr(updated_clientscope) result[ 'msg'] = "No changes required to clientscope {name}.".format( name=before_clientscope['name']) module.exit_json(**result) # update the existing clientscope result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize_cr(before_clientscope), after=sanitize_cr(updated_clientscope)) if module.check_mode: module.exit_json(**result) # do the clientscope update kc.update_clientscope(updated_clientscope, realm=realm) # do the protocolmappers update if protocol_mappers is not None: for protocol_mapper in protocol_mappers: # update if protocolmapper exist current_protocolmapper = kc.get_clientscope_protocolmapper_by_name( updated_clientscope['id'], protocol_mapper['name'], realm=realm) if current_protocolmapper is not None: protocol_mapper['id'] = current_protocolmapper['id'] kc.update_clientscope_protocolmappers( updated_clientscope['id'], protocol_mapper, realm=realm) # create otherwise else: kc.create_clientscope_protocolmapper( updated_clientscope['id'], protocol_mapper, realm=realm) after_clientscope = kc.get_clientscope_by_clientscopeid( updated_clientscope['id'], realm=realm) result['end_state'] = after_clientscope result['msg'] = "Clientscope {id} has been updated".format( id=after_clientscope['id']) module.exit_json(**result) elif state == 'absent': result['end_state'] = dict() if module._diff: result['diff'] = dict(before=sanitize_cr(before_clientscope), after='') if module.check_mode: module.exit_json(**result) # delete for real cid = before_clientscope['id'] kc.delete_clientscope(cid=cid, realm=realm) result['changed'] = True result['msg'] = "Clientscope {name} has been deleted".format( name=before_clientscope['name']) module.exit_json(**result) module.exit_json(**result)