Merge "Freeze inspec version for salt-formulas tests - in inspec >= 3.0.0 there are changes in schema which require to have tests/integration dir to contain tests, otherwise fails - this is temporary workaround until we decide proper fix"
diff --git a/README.rst b/README.rst
index e969e1f..ec12c3d 100644
--- a/README.rst
+++ b/README.rst
@@ -17,6 +17,103 @@
server:
enabled: true
+Keycloak service with realm using LDAP identity provider
+
+.. code-block:: yaml
+
+ keycloak:
+ server:
+ realm:
+ ldap-realm:
+ enabled: true
+ id: ldap-realm
+ client:
+ ldap-app:
+ enabled: true
+ base_url: /ldap-portal
+ redirect_uris:
+ - /ldap-portal/*
+ admin_url: /ldap-portal
+ secret: password
+ protocol_mapper:
+ oidc-usermodel-property-mapper:
+ username:
+ name: username
+ user_attribute: username
+ claim_name: preferred_username
+ given_name:
+ name: given name
+ user_attribute: firstName
+ claim_name: given_name
+ family_name:
+ name: family name
+ user_attribute: lastName
+ claim_name: family_name
+ email:
+ name: email
+ user_attribute: email
+ claim_name: email
+ oidc-full-name-mapper:
+ full_name:
+ name: full_name
+ federation_provider:
+ ldap:
+ display_name: ldap-server
+ users_dn: ou=people,dc=keycloak,dc=org
+ user_object_classes: inetOrgPerson, organizationalPerson
+ username_ldap_attribute: uid
+ bind_dn: cn=admin,dc=keycloak,dc=org
+ bind_credential: password
+ rdn_ldap_attribute: uid
+ edit_mode: READ_ONLY
+ uuid_ldap_attribute: entryUUID
+ connection_url: ldap://localhost:10389
+ sync_registrations: false
+ federation_mapper:
+ user-attribute-ldap-mapper:
+ username:
+ name: username
+ provider_display_name: ldap-server
+ ldap_attribute: uid
+ model_attribute: username
+ mandatory: true
+ read_only: false
+ always_read: false
+ first_name:
+ name: first name
+ provider_display_name: ldap-server
+ ldap_attribute: cn
+ model_attribute: firstName
+ mandatory: true
+ read_only: false
+ always_read: false
+ last_name:
+ name: last name
+ provider_display_name: ldap-server
+ ldap_attribute: sn
+ model_attribute: lastName
+ mandatory: true
+ read_only: false
+ always_read: false
+ email:
+ name: email
+ provider_display_name: ldap-server
+ ldap_attribute: mail
+ model_attribute: email
+ mandatory: false
+ read_only: false
+ always_read: false
+ role-ldap-mapper:
+ realm_roles:
+ name: realm roles
+ provider_display_name: ldap-server
+ roles_dn: ou=groups,dc=cicd,dc=local
+ membership_ldap_attribute: member
+ role_name_ldap_attribute: cn
+ role_object_classes: groupOfNames
+ mode: LDAP_ONLY
+ realm_roles_mapping: true
+
References
==========
diff --git a/keycloak/files/server/realms.json b/keycloak/files/server/realms.json
new file mode 100644
index 0000000..0e8f450
--- /dev/null
+++ b/keycloak/files/server/realms.json
@@ -0,0 +1,120 @@
+{%- for realm_name, realm in realms.iteritems() %}
+
+{
+ "id": "{{ realm.get('id', realm_name) }}",
+ "realm": "{{ realm_name }}",
+ "enabled": {{ realm.get('enabled', true)|json }},
+ "sslRequired": "external",
+ "requiredCredentials": [ "password" ],
+ "clients": [
+ {%- for client_name, client in realm.get('client', {}).iteritems() %}
+ {
+ "clientId": "{{ client_name }}",
+ "enabled": {{ client.get("enabled", True)|json }},
+ "baseUrl": "{{ client.base_url }}",
+ "redirectUris": {{ client.redirect_uris|json }},
+ "adminUrl": "{{ client.admin_url }}",
+ "directAccessGrantsEnabled": {{ client.get('direct_access_grants_enabled', False)|json }},
+ "secret": "{{ client.secret }}",
+ "fullScopeAllowed": true,
+ "protocolMappers": [
+ {%- for type, mapper in client.get('protocol_mapper', {}).iteritems() %}
+ {%- set outer_loop = loop %}
+ {%- for _, params in mapper.iteritems() %}
+ {%- if type == "oidc-usermodel-property-mapper" %}
+ {
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "protocol" : "openid-connect",
+ "name" : "{{ params.name }}",
+ "config" : {
+ "Claim JSON Type" : "String",
+ "user.attribute" : "{{ params.user_attribute }}",
+ "claim.name" : "{{ params.claim_name }}",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true"
+ }
+ }{%- if not (loop.last and outer_loop.last) %},{%- endif %}
+ {%- elif type == "oidc-full-name-mapper" %}
+ {
+ "protocolMapper" : "oidc-full-name-mapper",
+ "protocol" : "openid-connect",
+ "name" : "{{ params.name }}",
+ "config" : {
+ "id.token.claim" : "true",
+ "access.token.claim" : "true"
+ }
+ }{%- if not (loop.last and outer_loop.last) %},{%- endif %}
+ {%- endif %}
+ {%- endfor %}
+ {%- endfor %}
+ ]
+ }{%- if not loop.last %},{%- endif %}
+ {%- endfor %}
+ ],
+ "userFederationProviders": [
+ {%- for provider_name, provider in realm.get("federation_provider", {}).iteritems() %}
+ {
+ "displayName": "{{ provider.display_name }}",
+ "providerName": "{{ provider_name }}",
+ "priority": 1,
+ "fullSyncPeriod": -1,
+ "changedSyncPeriod": -1,
+ "config": {
+ "pagination" : "true",
+ "debug" : "false",
+ "searchScope" : "1",
+ "connectionPooling" : "true",
+ "usersDn" : "{{ provider.users_dn }}",
+ "userObjectClasses" : "{{ provider.get('user_object_classes', 'inetOrgPerson') }}",
+ "usernameLDAPAttribute" : "{{ provider.get('username_ldap_attribute', 'uid') }}",
+ "bindDn" : "{{ provider.bind_dn }}",
+ "bindCredential" : "{{ provider.bind_credential }}",
+ "rdnLDAPAttribute" : "{{ provider.get('rdn_ldap_attribute', 'uid') }}",
+ "vendor" : "other",
+ "editMode" : "{{ provider.get('edit_mode', 'READ_ONLY') }}",
+ "uuidLDAPAttribute" : "{{ provider.get('uuid_ldap_attribute', 'entryUUID') }}",
+ "connectionUrl" : "{{ provider.get('connection_url', 'ldap://localhost:389') }}",
+ "syncRegistrations" : "{{ provider.get('sync_registrations', False)|json }}",
+ "authType" : "simple"
+ }
+ }{%- if not loop.last %},{%- endif %}
+ {%- endfor %}
+ ],
+ "userFederationMappers" : [
+ {%- for type, mapper in realm.get('federation_mapper', {}).iteritems() %}
+ {%- set outer_loop = loop %}
+ {%- for _, params in mapper.iteritems() %}
+ {%- if type == "user-attribute-ldap-mapper" %}
+ {
+ "name" : "{{ params.name }}",
+ "federationMapperType" : "user-attribute-ldap-mapper",
+ "federationProviderDisplayName" : "{{ params.provider_display_name }}",
+ "config" : {
+ "ldap.attribute" : "{{ params.ldap_attribute }}",
+ "user.model.attribute" : "{{ params.model_attribute }}",
+ "is.mandatory.in.ldap" : "{{ params.get('mandatory', True)|json }}",
+ "read.only" : "{{ params.get('read_only', False)|json }}",
+ "always.read.value.from.ldap" : "{{ params.get('always_read', False)|json }}"
+ }
+ }{%- if not (loop.last and outer_loop.last) %},{%- endif %}
+ {%- elif type == "role-ldap-mapper" %}
+ {
+ "name" : "{{ params.name }}",
+ "federationMapperType" : "role-ldap-mapper",
+ "federationProviderDisplayName" : "{{ params.provider_display_name }}",
+ "config" : {
+ "roles.dn" : "{{ params.roles_dn }}",
+ "membership.ldap.attribute" : "{{ params.get('membership_ldap_attribute', 'memberUid') }}",
+ "role.name.ldap.attribute" : "{{ params.get('role_name_ldap_attribute', 'cn') }}",
+ "role.object.classes" : "{{ params.get('role_object_classes', 'posixGroup') }}",
+ "mode" : "{{ params.get('mode', 'LDAP_ONLY') }}",
+ "use.realm.roles.mapping" : "{{ params.get('realm_roles_mapping', True)|json }}"
+ }
+ }{%- if not (loop.last and outer_loop.last) %},{%- endif %}
+ {%- endif %}
+ {%- endfor %}
+ {%- endfor %}
+ ]
+}{%- if not loop.last %},{%- endif %}
+
+{%- endfor %}
diff --git a/keycloak/map.jinja b/keycloak/map.jinja
index b0f53de..d699df6 100644
--- a/keycloak/map.jinja
+++ b/keycloak/map.jinja
@@ -1,6 +1,11 @@
-{%- load_yaml as base %}
-dir: /srv/volumes/keycloak
-{%- endload %}
+{% set proxy = salt['grains.filter_by']({
+ 'default': {
+ 'dir': '/srv/volumes/keycloak'
+ }
+}, merge=salt['pillar.get']('keycloak:proxy')) %}
-{%- set server = salt['pillar.get']('keycloak:server') %}
-{%- set proxy = salt['pillar.get']('keycloak:proxy') %}
+{% set server = salt['grains.filter_by']({
+ 'default': {
+ 'dir': '/srv/volumes/keycloak'
+ }
+}, merge=salt['pillar.get']('keycloak:server')) %}
diff --git a/keycloak/proxy/init.sls b/keycloak/proxy/init.sls
index 6ca45f4..a50b953 100644
--- a/keycloak/proxy/init.sls
+++ b/keycloak/proxy/init.sls
@@ -1,10 +1,9 @@
{%- from "keycloak/map.jinja" import proxy with context %}
-{%- from "keycloak/map.jinja" import base with context %}
{%- if proxy.enabled|default(False) %}
keycloak_proxy_dir:
file.directory:
- - name: {{ base.dir }}/proxy
+ - name: {{ proxy.dir }}/proxy
- user: root
- group: root
- mode: 0755
@@ -12,7 +11,7 @@
keycloak_proxy_conf:
file.managed:
- - name: {{ base.dir }}/proxy/proxy.json
+ - name: {{ proxy.dir }}/proxy/proxy.json
- source: salt://keycloak/files/proxy/proxy.json
- template: jinja
- user: root
diff --git a/keycloak/server/init.sls b/keycloak/server/init.sls
index e69de29..0b45945 100644
--- a/keycloak/server/init.sls
+++ b/keycloak/server/init.sls
@@ -0,0 +1,15 @@
+{%- from "keycloak/map.jinja" import server with context %}
+include:
+- keycloak.server.realm
+
+{%- if server.get("enabled", False) %}
+
+keycloak_server_dir:
+ file.directory:
+ - name: {{ server.dir }}/server
+ - user: root
+ - group: root
+ - mode: 0755
+ - makedirs: true
+
+{%- endif %}
diff --git a/keycloak/server/realm.sls b/keycloak/server/realm.sls
new file mode 100644
index 0000000..1b13ce9
--- /dev/null
+++ b/keycloak/server/realm.sls
@@ -0,0 +1,19 @@
+{%- from "keycloak/map.jinja" import server with context %}
+
+{%- if server.get("enabled", False) %}
+{%- if server.realm is defined %}
+
+keycloak_realms:
+ file.managed:
+ - name: {{ server.dir }}/server/realms.json
+ - source: salt://keycloak/files/server/realms.json
+ - template: jinja
+ - user: root
+ - mode: 0644
+ - defaults:
+ realms: {{ server.realm }}
+ - require:
+ - file: keycloak_server_dir
+
+{%- endif %}
+{%- endif %}