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() 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)