Exemplo n.º 1
0
def test_error_returned(mock_401_returned):
    with pytest.raises(KeycloakError) as raised_error:
        get_token(module_params_creds)
    assert str(raised_error.value) == (
        'Could not obtain access token from http://keycloak.url'
        '/auth/realms/master/protocol/openid-connect/token: '
        'HTTP Error 401: Unauthorized')
Exemplo n.º 2
0
def test_bad_json_returned(mock_bad_json_returned):
    with pytest.raises(KeycloakError) as raised_error:
        get_token(module_params_creds)
    # cannot check all the message, different errors message for the value
    # error in python 2.6, 2.7 and 3.*.
    assert (
        'API returned invalid JSON when trying to obtain access token from '
        'http://keycloak.url/auth/realms/master/protocol/openid-connect/token: '
    ) in str(raised_error.value)
Exemplo n.º 3
0
def test_json_without_token_returned(mock_json_without_token_returned):
    with pytest.raises(KeycloakError) as raised_error:
        get_token(base_url='http://keycloak.url/auth',
                  validate_certs=True,
                  auth_realm='master',
                  client_id='admin-cli',
                  auth_username='******',
                  auth_password='******',
                  client_secret=None)
    assert str(raised_error.value) == (
        'Could not obtain access token from http://keycloak.url'
        '/auth/realms/master/protocol/openid-connect/token')
Exemplo n.º 4
0
def test_bad_json_returned(mock_bad_json_returned):
    with pytest.raises(KeycloakError) as raised_error:
        get_token(base_url='http://keycloak.url/auth',
                  validate_certs=True,
                  auth_realm='master',
                  client_id='admin-cli',
                  auth_username='******',
                  auth_password='******',
                  client_secret=None)
    # cannot check all the message, different errors message for the value
    # error in python 2.6, 2.7 and 3.*.
    assert (
        'API returned invalid JSON when trying to obtain access token from '
        'http://keycloak.url/auth/realms/master/protocol/openid-connect/token: '
    ) in str(raised_error.value)
Exemplo n.º 5
0
def main():

    argument_spec = keycloak_argument_spec()
    meta_args = dict(
        client_id=dict(type='str', aliases=['clientId'], required=True),
        realm=dict(type='str', default='master'),
        roles=dict(type='list', elements='dict'),
        serviceAccountRoles=dict(type='dict')
    )

    argument_spec.update(meta_args)

    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)

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

    kcx = KeycloakAPIX(module, connection_header)

    realm = module.params.get('realm')
    client_id = module.params.get('client_id')
    cid = None

    before_client = kcx.get_client_by_clientid(client_id, realm=realm)
    if before_client is not None:
        cid = before_client['id']
    else:
        module.fail_json(msg=str('Client %s not found' % module.params.get('client_id')))

    desiredRoles = module.params.get('roles')
    if desiredRoles:
        existingRoles = map(lambda exRole: exRole['name'], kcx.get_client_roles(cid, realm))
        absentRoles = [item for item in desiredRoles if item['name'] not in existingRoles]
        for role in absentRoles:
            kcx.create_client_role(cid, role, realm)

    saRoles = module.params.get('serviceAccountRoles')
    if saRoles and saRoles['clientRoles']:
        kcx.add_client_sa_roles(cid, client_id, saRoles['clientRoles'], realm)

    result = dict(changed=True, msg='', diff={}, proposed={}, existing={}, end_state={})
    module.exit_json(**result)
Exemplo n.º 6
0
def test_connect_to_keycloak(mock_good_connection):
    keycloak_header = get_token(base_url='http://keycloak.url/auth',
                                validate_certs=True,
                                auth_realm='master',
                                client_id='admin-cli',
                                auth_username='******',
                                auth_password='******',
                                client_secret=None)
    assert keycloak_header == {
        'Authorization': 'Bearer alongtoken',
        'Content-Type': 'application/json'
    }
Exemplo n.º 7
0
def test_connect_to_keycloak_with_token(mock_good_connection):
    module_params_token = {
        'auth_keycloak_url': 'http://keycloak.url/auth',
        'validate_certs': True,
        'client_id': 'admin-cli',
        'token': "alongtoken"
    }
    keycloak_header = get_token(module_params_token)
    assert keycloak_header == {
        'Authorization': 'Bearer alongtoken',
        'Content-Type': 'application/json'
    }
Exemplo n.º 8
0
def test_connect_to_keycloak_with_creds(mock_good_connection):
    keycloak_header = get_token(module_params_creds)
    assert keycloak_header == {
        'Authorization': 'Bearer alongtoken',
        'Content-Type': 'application/json'
    }
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
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')

    # 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)
Exemplo n.º 12
0
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)
Exemplo n.º 14
0
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)
Exemplo n.º 16
0
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()

    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)
Exemplo n.º 18
0
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()

    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)
Exemplo n.º 20
0
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)
Exemplo n.º 21
0
def main():
    """
    Module execution

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

    argument_spec.update(meta_args)

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

    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)