mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-29 15:06:26 +02:00
Refactor LDAP variable schema to use top-level constant LDAP and nested ALL-CAPS keys.
- Converted group_vars/all/13_ldap.yml from lower-case to ALL-CAPS nested keys. - Updated all roles, tasks, templates, and filter_plugins to reference LDAP.* instead of ldap.*. - Fixed Keycloak JSON templates to properly quote Jinja variables. - Adjusted svc-db-openldap filter plugins and unit tests to handle new LDAP structure. - Updated integration test to only check uniqueness of TOP-LEVEL ALL-CAPS constants, ignoring nested keys. See: https://chatgpt.com/share/68b01017-efe0-800f-a508-7d7e2f1c8c8d
This commit is contained in:
@@ -14,22 +14,22 @@ _ldap_domain: "{{ PRIMARY_DOMAIN }}" # LDAP is jsut listening to
|
||||
_ldap_user_id: "uid"
|
||||
_ldap_filters_users_all: "(|(objectclass=inetOrgPerson))"
|
||||
|
||||
ldap:
|
||||
LDAP:
|
||||
# Distinguished Names (DN)
|
||||
dn:
|
||||
DN:
|
||||
# -------------------------------------------------------------------------
|
||||
# Base DN / Suffix
|
||||
# This is the top-level naming context for your directory, used as the
|
||||
# default search base for most operations (e.g. adding users, groups).
|
||||
# Example: “dc=example,dc=com”
|
||||
root: "{{ LDAP_DN_BASE }}"
|
||||
administrator:
|
||||
ROOT: "{{ LDAP_DN_BASE }}"
|
||||
ADMINISTRATOR:
|
||||
# -------------------------------------------------------------------------
|
||||
# Data-Tree Administrator Bind DN
|
||||
# The DN used to authenticate for regular directory operations under
|
||||
# the data tree (adding users, modifying attributes, creating OUs, etc.).
|
||||
# Typically: “cn=admin,dc=example,dc=com”
|
||||
data: "cn={{ applications['svc-db-openldap'].users.administrator.username }},{{ LDAP_DN_BASE }}"
|
||||
DATA: "cn={{ applications['svc-db-openldap'].users.administrator.username }},{{ LDAP_DN_BASE }}"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Config-Tree Administrator Bind DN
|
||||
@@ -37,9 +37,9 @@ ldap:
|
||||
# need to load or modify schema, overlays, modules, or other server-
|
||||
# level settings.
|
||||
# Typically: “cn=admin,cn=config”
|
||||
configuration: "cn={{ applications['svc-db-openldap'].users.administrator.username }},cn=config"
|
||||
CONFIGURATION: "cn={{ applications['svc-db-openldap'].users.administrator.username }},cn=config"
|
||||
|
||||
ou:
|
||||
OU:
|
||||
# -------------------------------------------------------------------------
|
||||
# Organizational Units (OUs)
|
||||
# Pre-created containers in the directory tree to logically separate entries:
|
||||
@@ -47,9 +47,9 @@ ldap:
|
||||
# – groups: Contains organizational or business groups (e.g., departments, teams).
|
||||
# – roles: Contains application-specific RBAC roles
|
||||
# (e.g., "cn=app1-user", "cn=yourls-admin").
|
||||
users: "ou=users,{{ LDAP_DN_BASE }}"
|
||||
groups: "ou=groups,{{ LDAP_DN_BASE }}"
|
||||
roles: "ou=roles,{{ LDAP_DN_BASE }}"
|
||||
USERS: "ou=users,{{ LDAP_DN_BASE }}"
|
||||
GROUPS: "ou=groups,{{ LDAP_DN_BASE }}"
|
||||
ROLES: "ou=roles,{{ LDAP_DN_BASE }}"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Additional Notes
|
||||
@@ -59,17 +59,17 @@ ldap:
|
||||
# for ordinary user/group operations, and vice versa.
|
||||
|
||||
# Password to access dn.bind
|
||||
bind_credential: "{{ applications | get_app_conf('svc-db-openldap', 'credentials.administrator_database_password') }}"
|
||||
server:
|
||||
domain: "{{ _ldap_name if _ldap_docker_network_enabled else _ldap_domain }}" # Mapping for public or locale access
|
||||
port: "{{ _ldap_server_port }}"
|
||||
uri: "{{ _ldap_protocol }}://{{ _ldap_name if _ldap_docker_network_enabled else _ldap_domain }}:{{ _ldap_server_port }}"
|
||||
security: "" #TLS, SSL - Leave empty for none
|
||||
network:
|
||||
local: "{{ _ldap_docker_network_enabled }}" # Uses the application configuration to define if local network should be available or not
|
||||
user:
|
||||
objects:
|
||||
structural:
|
||||
BIND_CREDENTIAL: "{{ applications | get_app_conf('svc-db-openldap', 'credentials.administrator_database_password') }}"
|
||||
SERVER:
|
||||
DOMAIN: "{{ _ldap_name if _ldap_docker_network_enabled else _ldap_domain }}" # Mapping for public or locale access
|
||||
PORT: "{{ _ldap_server_port }}"
|
||||
URI: "{{ _ldap_protocol }}://{{ _ldap_name if _ldap_docker_network_enabled else _ldap_domain }}:{{ _ldap_server_port }}"
|
||||
SECURITY: "" #TLS, SSL - Leave empty for none
|
||||
NETWORK:
|
||||
LOCAL: "{{ _ldap_docker_network_enabled }}" # Uses the application configuration to define if local network should be available or not
|
||||
USER:
|
||||
OBJECTS:
|
||||
STRUCTURAL:
|
||||
- person # Structural Classes define the core identity of an entry:
|
||||
# • Specify mandatory attributes (e.g. sn, cn)
|
||||
# • Each entry must have exactly one structural class
|
||||
@@ -77,26 +77,26 @@ ldap:
|
||||
# (e.g. mail, employeeNumber)
|
||||
- posixAccount # Provides UNIX account attributes (uidNumber, gidNumber,
|
||||
# homeDirectory)
|
||||
auxiliary:
|
||||
nextloud_user: "nextcloudUser" # Auxiliary Classes attach optional attributes without
|
||||
AUXILIARY:
|
||||
NEXTCLOUD_USER: "nextcloudUser" # Auxiliary Classes attach optional attributes without
|
||||
# changing the entry’s structural role. Here they add
|
||||
# nextcloudQuota and nextcloudEnabled for Nextcloud.
|
||||
ssh_public_key: "ldapPublicKey" # Allows storing SSH public keys for services like Gitea.
|
||||
attributes:
|
||||
SSH_PUBLIC_KEY: "ldapPublicKey" # Allows storing SSH public keys for services like Gitea.
|
||||
ATTRIBUTES:
|
||||
# Attribut to identify the user
|
||||
id: "{{ _ldap_user_id }}"
|
||||
mail: "mail"
|
||||
fullname: "cn"
|
||||
firstname: "givenname"
|
||||
surname: "sn"
|
||||
ssh_public_key: "sshPublicKey"
|
||||
nextcloud_quota: "nextcloudQuota"
|
||||
filters:
|
||||
users:
|
||||
login: "(&{{ _ldap_filters_users_all }}({{_ldap_user_id}}=%{{_ldap_user_id}}))"
|
||||
all: "{{ _ldap_filters_users_all }}"
|
||||
rbac:
|
||||
flavors:
|
||||
ID: "{{ _ldap_user_id }}"
|
||||
MAIL: "mail"
|
||||
FULLNAME: "cn"
|
||||
FIRSTNAME: "givenname"
|
||||
SURNAME: "sn"
|
||||
SSH_PUBLIC_KEY: "sshPublicKey"
|
||||
NEXTCLOUD_QUOTA: "nextcloudQuota"
|
||||
FILTERS:
|
||||
USERS:
|
||||
LOGIN: "(&{{ _ldap_filters_users_all }}({{_ldap_user_id}}=%{{_ldap_user_id}}))"
|
||||
ALL: "{{ _ldap_filters_users_all }}"
|
||||
RBAC:
|
||||
FLAVORS:
|
||||
# Valid values posixGroup, groupOfNames
|
||||
- groupOfNames
|
||||
# - posixGroup
|
||||
|
@@ -8,12 +8,12 @@ def build_ldap_nested_group_entries(applications, users, ldap):
|
||||
result = {}
|
||||
|
||||
# Base DN components
|
||||
role_dn_base = ldap["dn"]["ou"]["roles"]
|
||||
user_dn_base = ldap["dn"]["ou"]["users"]
|
||||
ldap_user_attr = ldap["user"]["attributes"]["id"]
|
||||
role_dn_base = ldap["DN"]["OU"]["ROLES"]
|
||||
user_dn_base = ldap["DN"]["OU"]["USERS"]
|
||||
ldap_user_attr = ldap["USER"]["ATTRIBUTES"]["ID"]
|
||||
|
||||
# Supported objectClass flavors
|
||||
flavors = ldap.get("rbac", {}).get("flavors", [])
|
||||
flavors = ldap.get("RBAC").get("FLAVORS")
|
||||
|
||||
for application_id, app_config in applications.items():
|
||||
# Compute the DN for the application-level OU
|
||||
|
@@ -16,10 +16,10 @@ def build_ldap_role_entries(applications, users, ldap):
|
||||
}
|
||||
|
||||
group_id = application_config.get("group_id")
|
||||
user_dn_base = ldap["dn"]["ou"]["users"]
|
||||
ldap_user_attr = ldap["user"]["attributes"]["id"]
|
||||
role_dn_base = ldap["dn"]["ou"]["roles"]
|
||||
flavors = ldap.get("rbac", {}).get("flavors", [])
|
||||
user_dn_base = ldap["DN"]["OU"]["USERS"]
|
||||
ldap_user_attr = ldap["USER"]["ATTRIBUTES"]["ID"]
|
||||
role_dn_base = ldap["DN"]["OU"]["ROLES"]
|
||||
flavors = ldap.get("RBAC").get("FLAVORS")
|
||||
|
||||
for role_name, role_conf in roles.items():
|
||||
group_cn = f"{application_id}-{role_name}"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
- name: Load memberof module from file in OpenLDAP container
|
||||
shell: >
|
||||
docker exec -i {{ openldap_name }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{openldap_ldif_docker_path}}configuration/01_member_of_configuration.ldif
|
||||
docker exec -i {{ openldap_name }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{ openldap_ldif_docker_path }}configuration/01_member_of_configuration.ldif
|
||||
listen:
|
||||
- "Import configuration LDIF files"
|
||||
- "Import all LDIF files"
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
- name: Refint Module Activation for OpenLDAP
|
||||
shell: >
|
||||
docker exec -i {{ openldap_name }} ldapadd -Y EXTERNAL -H ldapi:/// -f {{openldap_ldif_docker_path}}configuration/02_member_of_configuration.ldif
|
||||
docker exec -i {{ openldap_name }} ldapadd -Y EXTERNAL -H ldapi:/// -f {{ openldap_ldif_docker_path }}configuration/02_member_of_configuration.ldif
|
||||
listen:
|
||||
- "Import configuration LDIF files"
|
||||
- "Import all LDIF files"
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- name: "Import schemas"
|
||||
shell: >
|
||||
docker exec -i {{ openldap_name }} ldapadd -Y EXTERNAL -H ldapi:/// -f "{{openldap_ldif_docker_path}}schema/{{ item | basename | regex_replace('\.j2$', '') }}"
|
||||
docker exec -i {{ openldap_name }} ldapadd -Y EXTERNAL -H ldapi:/// -f "{{ openldap_ldif_docker_path }}schema/{{ item | basename | regex_replace('\.j2$', '') }}"
|
||||
register: ldapadd_result
|
||||
changed_when: "'adding new entry' in ldapadd_result.stdout"
|
||||
failed_when: ldapadd_result.rc not in [0, 80]
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
- name: Refint Overlay Configuration for OpenLDAP
|
||||
shell: >
|
||||
docker exec -i {{ openldap_name }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{openldap_ldif_docker_path}}configuration/03_member_of_configuration.ldif
|
||||
docker exec -i {{ openldap_name }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{ openldap_ldif_docker_path }}configuration/03_member_of_configuration.ldif
|
||||
listen:
|
||||
- "Import configuration LDIF files"
|
||||
- "Import all LDIF files"
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
- name: "Import users, groups, etc. to LDAP"
|
||||
shell: >
|
||||
docker exec -i {{ openldap_name }} ldapadd -x -D "{{ldap.dn.administrator.data}}" -w "{{ldap.bind_credential}}" -c -f "{{openldap_ldif_docker_path}}groups/{{ item | basename | regex_replace('\.j2$', '') }}"
|
||||
docker exec -i {{ openldap_name }} ldapadd -x -D "{{LDAP.DN.ADMINISTRATOR.DATA}}" -w "{{ LDAP.BIND_CREDENTIAL }}" -c -f "{{ openldap_ldif_docker_path }}groups/{{ item | basename | regex_replace('\.j2$', '') }}"
|
||||
register: ldapadd_result
|
||||
changed_when: "'adding new entry' in ldapadd_result.stdout"
|
||||
failed_when: ldapadd_result.rc not in [0, 20, 68, 65]
|
||||
|
@@ -28,7 +28,7 @@
|
||||
- name: "Generate hash for Database Admin password"
|
||||
shell: |
|
||||
docker exec {{ openldap_name }} \
|
||||
slappasswd -s "{{ ldap.bind_credential }}"
|
||||
slappasswd -s "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
register: database_admin_pw_hash
|
||||
|
||||
- name: "Reset Database Admin password in LDAP (olcRootPW)"
|
||||
|
@@ -3,11 +3,11 @@
|
||||
###############################################################################
|
||||
- name: Ensure LDAP users exist
|
||||
community.general.ldap_entry:
|
||||
dn: "{{ ldap.user.attributes.id }}={{ item.key }},{{ ldap.dn.ou.users }}"
|
||||
dn: "{{ LDAP.USER.ATTRIBUTES.ID }}={{ item.key }},{{ LDAP.DN.OU.USERS }}"
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
bind_dn: "{{ ldap.dn.administrator.data }}"
|
||||
bind_pw: "{{ ldap.bind_credential }}"
|
||||
objectClass: "{{ ldap.user.objects.structural }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
objectClass: "{{ LDAP.USER.OBJECTS.STRUCTURAL }}"
|
||||
attributes:
|
||||
uid: "{{ item.value.username }}"
|
||||
sn: "{{ item.value.sn | default(item.key) }}"
|
||||
@@ -29,12 +29,12 @@
|
||||
###############################################################################
|
||||
- name: Ensure required objectClass values and mail address are present
|
||||
community.general.ldap_attrs:
|
||||
dn: "{{ ldap.user.attributes.id }}={{ item.key }},{{ ldap.dn.ou.users }}"
|
||||
dn: "{{ LDAP.USER.ATTRIBUTES.ID }}={{ item.key }},{{ LDAP.DN.OU.USERS }}"
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
bind_dn: "{{ ldap.dn.administrator.data }}"
|
||||
bind_pw: "{{ ldap.bind_credential }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
attributes:
|
||||
objectClass: "{{ ldap.user.objects.structural }}"
|
||||
objectClass: "{{ LDAP.USER.OBJECTS.STRUCTURAL }}"
|
||||
mail: "{{ item.value.email }}"
|
||||
state: exact
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
@@ -45,10 +45,10 @@
|
||||
|
||||
- name: "Ensure container for application roles exists"
|
||||
community.general.ldap_entry:
|
||||
dn: "{{ ldap.dn.ou.roles }}"
|
||||
dn: "{{ LDAP.DN.OU.ROLES }}"
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
bind_dn: "{{ ldap.dn.administrator.data }}"
|
||||
bind_pw: "{{ ldap.bind_credential }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
objectClass: organizationalUnit
|
||||
attributes:
|
||||
ou: roles
|
||||
|
@@ -1,22 +1,22 @@
|
||||
- name: Gather all users with their current objectClass list
|
||||
community.general.ldap_search:
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
bind_dn: "{{ ldap.dn.administrator.data }}"
|
||||
bind_pw: "{{ ldap.bind_credential }}"
|
||||
dn: "{{ ldap.dn.ou.users }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
dn: "{{ LDAP.DN.OU.USERS }}"
|
||||
scope: subordinate
|
||||
filter: "{{ ldap.filters.users.all }}"
|
||||
filter: "{{ LDAP.FILTERS.USERS.ALL }}"
|
||||
attrs:
|
||||
- dn
|
||||
- objectClass
|
||||
- "{{ ldap.user.attributes.id }}"
|
||||
- "{{ LDAP.USER.ATTRIBUTES.ID }}"
|
||||
register: ldap_users_with_classes
|
||||
|
||||
- name: Add only missing auxiliary classes
|
||||
community.general.ldap_attrs:
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
bind_dn: "{{ ldap.dn.administrator.data }}"
|
||||
bind_pw: "{{ ldap.bind_credential }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
dn: "{{ item.dn }}"
|
||||
attributes:
|
||||
objectClass: "{{ missing_auxiliary }}"
|
||||
@@ -28,7 +28,7 @@
|
||||
label: "{{ item.dn }}"
|
||||
vars:
|
||||
missing_auxiliary: >-
|
||||
{{ (ldap.user.objects.auxiliary.values() | list)
|
||||
{{ (LDAP.USER.OBJECTS.AUXILIARY.values() | list)
|
||||
| difference(item.objectClass | default([]))
|
||||
}}
|
||||
when: missing_auxiliary | length > 0
|
||||
|
@@ -8,9 +8,9 @@
|
||||
vars:
|
||||
schema_name: "nextcloud"
|
||||
attribute_defs:
|
||||
- "( 1.3.6.1.4.1.99999.1 NAME '{{ ldap.user.attributes.nextcloud_quota }}' DESC 'Quota for Nextcloud' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )"
|
||||
- "( 1.3.6.1.4.1.99999.1 NAME '{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}' DESC 'Quota for Nextcloud' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )"
|
||||
objectclass_defs:
|
||||
- "( 1.3.6.1.4.1.99999.2 NAME 'nextcloudUser' DESC 'Auxiliary class for Nextcloud attributes' AUXILIARY MAY ( {{ ldap.user.attributes.nextcloud_quota }} ) )"
|
||||
- "( 1.3.6.1.4.1.99999.2 NAME '{{ LDAP.USER.OBJECTS.AUXILIARY.NEXTCLOUD_USER }}' DESC 'Auxiliary class for Nextcloud attributes' AUXILIARY MAY ( {{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }} ) )"
|
||||
command: >
|
||||
ldapsm
|
||||
-s {{ openldap_server_uri }}
|
||||
|
@@ -8,16 +8,16 @@
|
||||
vars:
|
||||
schema_name: "openssh-lpk"
|
||||
attribute_defs:
|
||||
- "( 1.3.6.1.4.1.24552.1.1 NAME '{{ ldap.user.attributes.ssh_public_key }}' DESC 'OpenSSH Public Key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )"
|
||||
- "( 1.3.6.1.4.1.24552.1.1 NAME '{{ LDAP.USER.ATTRIBUTES.SSH_PUBLIC_KEY }}' DESC 'OpenSSH Public Key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )"
|
||||
- "( 1.3.6.1.4.1.24552.1.2 NAME 'sshFingerprint' DESC 'OpenSSH Public Key Fingerprint' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )"
|
||||
objectclass_defs:
|
||||
- >-
|
||||
( 1.3.6.1.4.1.24552.2.1
|
||||
NAME '{{ ldap.user.objects.auxiliary.ssh_public_key }}'
|
||||
NAME '{{ LDAP.USER.OBJECTS.AUXILIARY.SSH_PUBLIC_KEY }}'
|
||||
DESC 'Auxiliary class for OpenSSH public keys'
|
||||
SUP top
|
||||
AUXILIARY
|
||||
MAY ( {{ ldap.user.attributes.ssh_public_key }} $ sshFingerprint ) )
|
||||
MAY ( {{ LDAP.USER.ATTRIBUTES.SSH_PUBLIC_KEY }} $ sshFingerprint ) )
|
||||
|
||||
command: >
|
||||
ldapsm
|
||||
|
@@ -10,12 +10,12 @@
|
||||
{% endif %}
|
||||
volumes:
|
||||
- 'data:/bitnami/openldap'
|
||||
- '{{openldap_ldif_host_path}}:{{openldap_ldif_docker_path}}:ro'
|
||||
- '{{openldap_ldif_host_path}}:{{ openldap_ldif_docker_path }}:ro'
|
||||
healthcheck:
|
||||
test: >
|
||||
bash -c '
|
||||
ldapsearch -x -H ldap://localhost:{{ openldap_docker_port_open }} \
|
||||
-D "{{ ldap.dn.administrator.data }}" -w "{{ ldap.bind_credential }}" -b "{{ ldap.dn.root }}" > /dev/null \
|
||||
-D "{{ LDAP.DN.ADMINISTRATOR.DATA }}" -w "{{ LDAP.BIND_CREDENTIAL }}" -b "{{ LDAP.DN.ROOT }}" > /dev/null \
|
||||
&& ldapsearch -Y EXTERNAL -H ldapi:/// \
|
||||
-b cn=config "(&(objectClass=olcOverlayConfig)(olcOverlay=memberof))" \
|
||||
| grep "olcOverlay:" | grep -q "memberof"
|
||||
|
@@ -4,15 +4,15 @@
|
||||
# GENERAL
|
||||
## Admin (Data)
|
||||
LDAP_ADMIN_USERNAME= {{ applications | get_app_conf(application_id, 'users.administrator.username') }} # LDAP database admin user.
|
||||
LDAP_ADMIN_PASSWORD= {{ldap.bind_credential}} # LDAP database admin password.
|
||||
LDAP_ADMIN_PASSWORD= {{ LDAP.BIND_CREDENTIAL }} # LDAP database admin password.
|
||||
|
||||
## Users
|
||||
LDAP_USERS= ' ' # Comma separated list of LDAP users to create in the default LDAP tree. Default: user01,user02
|
||||
LDAP_PASSWORDS= ' ' # Comma separated list of passwords to use for LDAP users. Default: bitnami1,bitnami2
|
||||
LDAP_ROOT= {{ldap.dn.root}} # LDAP baseDN (or suffix) of the LDAP tree. Default: dc=example,dc=org
|
||||
LDAP_ROOT= {{ LDAP.DN.ROOT }} # LDAP baseDN (or suffix) of the LDAP tree. Default: dc=example,dc=org
|
||||
|
||||
## Admin (Config)
|
||||
LDAP_ADMIN_DN= {{ldap.dn.administrator.data}}
|
||||
LDAP_ADMIN_DN= {{LDAP.DN.ADMINISTRATOR.DATA}}
|
||||
LDAP_CONFIG_ADMIN_ENABLED= yes
|
||||
LDAP_CONFIG_ADMIN_USERNAME= {{ applications | get_app_conf(application_id, 'users.administrator.username') }}
|
||||
LDAP_CONFIG_ADMIN_PASSWORD= {{ applications | get_app_conf(application_id, 'credentials.administrator_password') }}
|
||||
|
@@ -4,7 +4,7 @@ application_id: "svc-db-openldap"
|
||||
openldap_docker_port_secure: 636
|
||||
openldap_docker_port_open: 389
|
||||
openldap_server_uri: "ldap://127.0.0.1:{{ ports.localhost.ldap[application_id] }}"
|
||||
openldap_bind_dn: "{{ ldap.dn.administrator.configuration }}"
|
||||
openldap_bind_dn: "{{ LDAP.DN.ADMINISTRATOR.CONFIGURATION }}"
|
||||
openldap_bind_pw: "{{ applications | get_app_conf(application_id, 'credentials.administrator_password', True) }}"
|
||||
|
||||
# LDIF Variables
|
||||
|
@@ -170,21 +170,21 @@ OAUTH2_REDIRECT=
|
||||
# LDAP_SERVER=ldap.example.com
|
||||
# LDAP_PORT=389
|
||||
# LDAP_METHOD=plain
|
||||
# LDAP_UID={{ ldap.user.attributes.id }}
|
||||
# LDAP_UID={{ LDAP.USER.ATTRIBUTES.ID }}
|
||||
# LDAP_BASE=dc=example,dc=com
|
||||
# LDAP_AUTH=simple
|
||||
# LDAP_BIND_DN=cn=admin,dc=example,dc=com
|
||||
# LDAP_PASSWORD=password
|
||||
# LDAP_ROLE_FIELD=ou
|
||||
# LDAP_FILTER=(&(attr1=value1)(attr2=value2))
|
||||
LDAP_SERVER="{{ ldap.server.domain }}"
|
||||
LDAP_PORT="{{ ldap.server.port }}"
|
||||
LDAP_SERVER="{{ LDAP.SERVER.DOMAIN }}"
|
||||
LDAP_PORT="{{ LDAP.SERVER.PORT }}"
|
||||
LDAP_METHOD=
|
||||
LDAP_UID={{ ldap.user.attributes.id }}
|
||||
LDAP_BASE="{{ ldap.dn.root }}"
|
||||
LDAP_BIND_DN="{{ ldap.dn.administrator.data }}"
|
||||
LDAP_UID={{ LDAP.USER.ATTRIBUTES.ID }}
|
||||
LDAP_BASE="{{ LDAP.DN.ROOT }}"
|
||||
LDAP_BIND_DN="{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
LDAP_AUTH=password
|
||||
LDAP_PASSWORD="{{ ldap.bind_credential }}"
|
||||
LDAP_PASSWORD="{{ LDAP.BIND_CREDENTIAL }}"
|
||||
LDAP_ROLE_FIELD=
|
||||
LDAP_FILTER=
|
||||
{% endif %}
|
||||
|
@@ -150,16 +150,16 @@ run:
|
||||
- exec: rails r "SiteSetting.ldap_sync_enabled = true"
|
||||
|
||||
# LDAP connection settings
|
||||
- exec: rails r "SiteSetting.ldap_sync_host = '{{ ldap.server.domain }}'"
|
||||
- exec: rails r "SiteSetting.ldap_sync_port = {{ ldap.server.port }}"
|
||||
- exec: rails r "SiteSetting.ldap_sync_host = '{{ LDAP.SERVER.DOMAIN }}'"
|
||||
- exec: rails r "SiteSetting.ldap_sync_port = {{ LDAP.SERVER.PORT }}"
|
||||
- exec: rails r "SiteSetting.ldap_encryption = 'simple_tls'"
|
||||
- exec: rails r "SiteSetting.ldap_base_dn = '{{ ldap.dn.root }}'"
|
||||
- exec: rails r "SiteSetting.ldap_bind_dn = '{{ ldap.dn.administrator.data }}'"
|
||||
- exec: rails r "SiteSetting.ldap_bind_password = '{{ ldap.bind_credential }}'"
|
||||
- exec: rails r "SiteSetting.ldap_base_dn = '{{ LDAP.DN.ROOT }}'"
|
||||
- exec: rails r "SiteSetting.ldap_bind_dn = '{{ LDAP.DN.ADMINISTRATOR.DATA }}'"
|
||||
- exec: rails r "SiteSetting.ldap_bind_password = '{{ LDAP.BIND_CREDENTIAL }}'"
|
||||
|
||||
# LDAP additional configuration
|
||||
- exec: rails r "SiteSetting.ldap_user_filter = '{{ ldap.filters.users.login }}'"
|
||||
- exec: rails r "SiteSetting.ldap_group_base_dn = '{{ ldap.dn.ou.groups }}'"
|
||||
- exec: rails r "SiteSetting.ldap_user_filter = '{{ LDAP.FILTERS.USERS.LOGIN }}'"
|
||||
- exec: rails r "SiteSetting.ldap_group_base_dn = '{{ LDAP.DN.OU.GROUPS }}'"
|
||||
- exec: rails r "SiteSetting.ldap_group_member_check = 'memberUid'"
|
||||
|
||||
- exec: rails r "SiteSetting.ldap_sync_period = 1"
|
||||
|
@@ -63,13 +63,13 @@ ESPOCRM_CONFIG_OUTBOUND_EMAIL_FROM_ADDRESS={{ users['contact'].email }}
|
||||
# ------------------------------------------------
|
||||
{% if applications | get_app_conf(application_id, 'features.ldap', False) %}
|
||||
ESPOCRM_CONFIG_AUTHENTICATION_METHOD=Ldap
|
||||
ESPOCRM_CONFIG_LDAP_HOST={{ ldap.server.domain }}
|
||||
ESPOCRM_CONFIG_LDAP_PORT={{ ldap.server.port }}
|
||||
ESPOCRM_CONFIG_LDAP_HOST={{ LDAP.SERVER.DOMAIN }}
|
||||
ESPOCRM_CONFIG_LDAP_PORT={{ LDAP.SERVER.PORT }}
|
||||
# ESPOCRM_CONFIG_LDAP_SECURITY: "", SSL or TLS
|
||||
ESPOCRM_CONFIG_LDAP_SECURITY={{ ldap.server.security }}
|
||||
ESPOCRM_CONFIG_LDAP_USERNAME={{ ldap.dn.administrator.data }}
|
||||
ESPOCRM_CONFIG_LDAP_PASSWORD={{ ldap.bind_credential }}
|
||||
ESPOCRM_CONFIG_LDAP_BASE_DN={{ ldap.dn.ou.users }}
|
||||
ESPOCRM_CONFIG_LDAP_SECURITY={{ LDAP.SERVER.SECURITY }}
|
||||
ESPOCRM_CONFIG_LDAP_USERNAME={{ LDAP.DN.ADMINISTRATOR.DATA }}
|
||||
ESPOCRM_CONFIG_LDAP_PASSWORD={{ LDAP.BIND_CREDENTIAL }}
|
||||
ESPOCRM_CONFIG_LDAP_BASE_DN={{ LDAP.DN.OU.USERS }}
|
||||
ESPOCRM_CONFIG_LDAP_USER_LOGIN_FILTER=(sAMAccountName=%USERNAME%)
|
||||
{% endif %}
|
||||
|
||||
|
@@ -9,26 +9,26 @@ return [
|
||||
// ldap_server (String)
|
||||
// ldap hostname server - required
|
||||
// Example: ldap_server = host.example.com
|
||||
'ldap_server' => '{{ ldap.server.uri }}',
|
||||
'ldap_server' => '{{ LDAP.SERVER.URI }}',
|
||||
|
||||
// ldap_binddn (String)
|
||||
// admin dn - optional - only if ldap server dont have anonymous access
|
||||
// Example: ldap_binddn = cn=admin,dc=example,dc=com
|
||||
'ldap_binddn' => '{{ ldap.dn.administrator.data }}',
|
||||
'ldap_binddn' => '{{ LDAP.DN.ADMINISTRATOR.DATA }}',
|
||||
|
||||
// ldap_bindpw (String)
|
||||
// admin password - optional - only if ldap server dont have anonymous access
|
||||
'ldap_bindpw' => '{{ ldap.bind_credential }}',
|
||||
'ldap_bindpw' => '{{ LDAP.BIND_CREDENTIAL }}',
|
||||
|
||||
// ldap_searchdn (String)
|
||||
// dn to search users - required
|
||||
// Example: ldap_searchdn = ou=users,dc=example,dc=com
|
||||
'ldap_searchdn' => '{{ ldap.dn.ou.users }}',
|
||||
'ldap_searchdn' => '{{ LDAP.DN.OU.USERS }}',
|
||||
|
||||
// ldap_userattr (String)
|
||||
// attribute to find username - required
|
||||
// Example: ldap_userattr = uid
|
||||
'ldap_userattr' => '{{ ldap.user.attributes.id }}',
|
||||
'ldap_userattr' => '{{ LDAP.USER.ATTRIBUTES.ID }}',
|
||||
|
||||
// ldap_group (String)
|
||||
// DN of the group whose member can auth on Friendica - optional
|
||||
@@ -42,10 +42,10 @@ return [
|
||||
|
||||
// ldap_autocreateaccount_emailattribute (String)
|
||||
// attribute to get email - optional - default : 'mail'
|
||||
'ldap_autocreateaccount_emailattribute' => '{{ ldap.user.attributes.mail }}',
|
||||
'ldap_autocreateaccount_emailattribute' => '{{ LDAP.USER.ATTRIBUTES.MAIL }}',
|
||||
|
||||
// ldap_autocreateaccount_nameattribute (String)
|
||||
// attribute to get nickname - optional - default : 'givenName'
|
||||
'ldap_autocreateaccount_nameattribute' => '{{ ldap.user.attributes.firstname }}',
|
||||
'ldap_autocreateaccount_nameattribute' => '{{ LDAP.USER.ATTRIBUTES.FIRSTNAME }}',
|
||||
],
|
||||
];
|
@@ -108,12 +108,12 @@ DJANGO_SECRET_KEY={{applications | get_app_conf(application_id, 'credentials.dja
|
||||
# Commit: https://gitea.fudaoyuan.icu/Github/funkwhale/commit/4ce46ff2a000646a3dbab80f0ca9fd8d7f8ae24c
|
||||
|
||||
LDAP_ENABLED = True
|
||||
LDAP_SERVER_URI = "{{ ldap.server.uri }}"
|
||||
LDAP_BIND_DN = "{{ ldap.dn.administrator.data }}"
|
||||
LDAP_BIND_PASSWORD = "{{ ldap.bind_credential }}"
|
||||
LDAP_SEARCH_FILTER = "{{ ldap.filters.users.login | replace('%' ~ ldap.user.attributes.id, '{0}') }}"
|
||||
LDAP_SERVER_URI = "{{ LDAP.SERVER.URI }}"
|
||||
LDAP_BIND_DN = "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
LDAP_BIND_PASSWORD = "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
LDAP_SEARCH_FILTER = "{{ LDAP.FILTERS.USERS.LOGIN | replace('%' ~ LDAP.USER.ATTRIBUTES.ID, '{0}') }}"
|
||||
LDAP_START_TLS = False
|
||||
LDAP_ROOT_DN = "{{ldap.dn.root}}"
|
||||
LDAP_ROOT_DN = "{{ LDAP.DN.ROOT }}"
|
||||
LDAP_USER_ATTR_MAP='{"username":"uid","first_name":"givenName","last_name":"sn","email":"mail"}'
|
||||
{% endif %}
|
||||
|
||||
|
@@ -8,7 +8,7 @@ LAM_PASSWORD= {{ applications | get_app_conf(application_id, 'cred
|
||||
LAM_CONFIGURATION_DATABASE= files # configuration database (files or mysql) @todo implement mariadb
|
||||
|
||||
# LDAP Configuration
|
||||
LDAP_SERVER= {{ldap.server.domain}} # domain of LDAP database root entry
|
||||
LDAP_BASE_DN= {{ldap.dn.root}} # LDAP base DN to overwrite value generated by LDAP_DOMAIN
|
||||
LDAP_USER= {{ldap.dn.administrator.data}} # LDAP admin user (set as login user for LAM)
|
||||
LDAP_ADMIN_PASSWORD= {{ldap.bind_credential}} # LDAP admin password
|
||||
LDAP_SERVER= {{ LDAP.SERVER.DOMAIN }} # domain of LDAP database root entry
|
||||
LDAP_BASE_DN= {{ LDAP.DN.ROOT }} # LDAP base DN to overwrite value generated by LDAP_DOMAIN
|
||||
LDAP_USER= {{ LDAP.DN.ADMINISTRATOR.DATA }} # LDAP admin user (set as login user for LAM)
|
||||
LDAP_ADMIN_PASSWORD= {{ LDAP.BIND_CREDENTIAL }} # LDAP admin password
|
@@ -9,18 +9,18 @@ docker_compose_flush_handlers: true
|
||||
# Gitea
|
||||
GITEA_LDAP_AUTH_ARGS:
|
||||
- '--name "LDAP ({{ PRIMARY_DOMAIN }})"'
|
||||
- '--host "{{ ldap.server.domain }}"'
|
||||
- '--port {{ ldap.server.port }}'
|
||||
- '--security-protocol "{{ ldap.server.security | trim or "unencrypted" }}"'
|
||||
- '--bind-dn "{{ ldap.dn.administrator.data }}"'
|
||||
- '--bind-password "{{ ldap.bind_credential }}"'
|
||||
- '--user-search-base "{{ ldap.dn.ou.users }}"'
|
||||
- '--host "{{ LDAP.SERVER.DOMAIN }}"'
|
||||
- '--port {{ LDAP.SERVER.PORT }}'
|
||||
- '--security-protocol "{{ LDAP.SERVER.SECURITY | trim or "unencrypted" }}"'
|
||||
- '--bind-dn "{{ LDAP.DN.ADMINISTRATOR.DATA }}"'
|
||||
- '--bind-password "{{ LDAP.BIND_CREDENTIAL }}"'
|
||||
- '--user-search-base "{{ LDAP.DN.OU.USERS }}"'
|
||||
- '--user-filter "(&(objectClass=inetOrgPerson)(uid=%s))"'
|
||||
- '--username-attribute "{{ ldap.user.attributes.id }}"'
|
||||
- '--firstname-attribute "{{ ldap.user.attributes.firstname }}"'
|
||||
- '--surname-attribute "{{ ldap.user.attributes.surname }}"'
|
||||
- '--email-attribute "{{ ldap.user.attributes.mail }}"'
|
||||
- '--public-ssh-key-attribute "{{ ldap.user.attributes.ssh_public_key }}"'
|
||||
- '--username-attribute "{{ LDAP.USER.ATTRIBUTES.ID }}"'
|
||||
- '--firstname-attribute "{{ LDAP.USER.ATTRIBUTES.FIRSTNAME }}"'
|
||||
- '--surname-attribute "{{ LDAP.USER.ATTRIBUTES.SURNAME }}"'
|
||||
- '--email-attribute "{{ LDAP.USER.ATTRIBUTES.MAIL }}"'
|
||||
- '--public-ssh-key-attribute "{{ LDAP.USER.ATTRIBUTES.SSH_PUBLIC_KEY }}"'
|
||||
- '--synchronize-users'
|
||||
GITEA_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.gitea.version') }}"
|
||||
GITEA_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.gitea.image') }}"
|
||||
|
@@ -11,7 +11,7 @@
|
||||
"providerId": "user-attribute-ldap-mapper",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"ldap.attribute": [ "{{ ldap.user.attributes.firstname }}" ],
|
||||
"ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.FIRSTNAME }}" ],
|
||||
"attribute.force.default": [ "true" ],
|
||||
"is.mandatory.in.ldap": [ "true" ],
|
||||
"is.binary.attribute": [ "false" ],
|
||||
@@ -27,7 +27,7 @@
|
||||
"providerId": "user-attribute-ldap-mapper",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"ldap.attribute": [ "{{ ldap.user.attributes.surname }}" ],
|
||||
"ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.SURNAME }}" ],
|
||||
"is.mandatory.in.ldap": [ "true" ],
|
||||
"always.read.value.from.ldap": [ "true" ],
|
||||
"read.only": [ "false" ],
|
||||
@@ -43,7 +43,7 @@
|
||||
"config": {
|
||||
"read.only": [ "false" ],
|
||||
"write.only": [ "true" ],
|
||||
"ldap.full.name.attribute": [ "{{ ldap.user.attributes.fullname }}" ]
|
||||
"ldap.full.name.attribute": [ "{{ LDAP.USER.ATTRIBUTES.FULLNAME }}" ]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"providerId": "user-attribute-ldap-mapper",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"ldap.attribute": [ "{{ ldap.user.attributes.id }}" ],
|
||||
"ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.ID }}" ],
|
||||
"is.mandatory.in.ldap": [ "true" ],
|
||||
"attribute.force.default": [ "false" ],
|
||||
"is.binary.attribute": [ "false" ],
|
||||
@@ -69,7 +69,7 @@
|
||||
"providerId": "user-attribute-ldap-mapper",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"ldap.attribute": [ "{{ ldap.user.attributes.mail }}" ],
|
||||
"ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.MAIL }}" ],
|
||||
"is.mandatory.in.ldap": [ "false" ],
|
||||
"read.only": [ "false" ],
|
||||
"always.read.value.from.ldap": [ "false" ],
|
||||
@@ -83,29 +83,29 @@
|
||||
"providerId": "user-attribute-ldap-mapper",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"ldap.attribute": [ "{{ ldap.user.attributes.ssh_public_key }}" ],
|
||||
"ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.SSH_PUBLIC_KEY }}" ],
|
||||
"is.mandatory.in.ldap": [ "false" ],
|
||||
"attribute.force.default": [ "false" ],
|
||||
"is.binary.attribute": [ "false" ],
|
||||
"read.only": [ "false" ],
|
||||
"always.read.value.from.ldap": [ "true" ],
|
||||
"user.model.attribute": [ "{{ ldap.user.attributes.ssh_public_key }}" ]
|
||||
"user.model.attribute": [ "{{ LDAP.USER.ATTRIBUTES.SSH_PUBLIC_KEY }}" ]
|
||||
}
|
||||
},
|
||||
|
||||
{# ---------------------- Nextcloud Quota ----------------- #}
|
||||
{
|
||||
"name": "{{ ldap.user.attributes.nextcloud_quota }}",
|
||||
"name": "{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}",
|
||||
"providerId": "user-attribute-ldap-mapper",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"ldap.attribute": [ "{{ ldap.user.attributes.nextcloud_quota }}" ],
|
||||
"ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}" ],
|
||||
"is.mandatory.in.ldap": [ "false" ],
|
||||
"attribute.force.default": [ "false" ],
|
||||
"is.binary.attribute": [ "false" ],
|
||||
"always.read.value.from.ldap": [ "false" ],
|
||||
"read.only": [ "false" ],
|
||||
"user.model.attribute": [ "{{ ldap.user.attributes.nextcloud_quota }}" ]
|
||||
"user.model.attribute": [ "{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}" ]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -145,12 +145,12 @@
|
||||
"config": {
|
||||
"membership.attribute.type": [ "DN" ],
|
||||
"group.name.ldap.attribute": [ "cn" ],
|
||||
"membership.user.ldap.attribute": [ "{{ ldap.user.attributes.id }}" ],
|
||||
"membership.user.ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.ID }}" ],
|
||||
"preserve.group.inheritance": [ "true" ],
|
||||
"groups.dn": [ "{{ ldap.dn.ou.roles }}" ],
|
||||
"groups.dn": [ "{{ LDAP.DN.OU.ROLES }}" ],
|
||||
"mode": [ "LDAP_ONLY" ],
|
||||
"user.roles.retrieve.strategy": [ "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE" ],
|
||||
"groups.ldap.filter": ["{{ ldap.rbac.flavors | ldap_groups_filter }}"],
|
||||
"groups.ldap.filter": ["{{ LDAP.RBAC.FLAVORS | ldap_groups_filter }}"],
|
||||
"membership.ldap.attribute": [ "member" ],
|
||||
"ignore.missing.groups": [ "true" ],
|
||||
"group.object.classes": [ "groupOfNames" ],
|
||||
@@ -205,9 +205,9 @@
|
||||
"mode": [ "LDAP_ONLY" ],
|
||||
"membership.attribute.type": [ "DN" ],
|
||||
"user.roles.retrieve.strategy": [ "LOAD_ROLES_BY_MEMBER_ATTRIBUTE" ],
|
||||
"roles.dn": [ "{{ ldap.dn.ou.roles }}" ],
|
||||
"roles.dn": [ "{{ LDAP.DN.OU.ROLES }}" ],
|
||||
"membership.ldap.attribute": [ "member" ],
|
||||
"membership.user.ldap.attribute": [ "{{ ldap.user.attributes.id }}" ],
|
||||
"membership.user.ldap.attribute": [ "{{ LDAP.USER.ATTRIBUTES.ID }}" ],
|
||||
"memberof.ldap.attribute": [ "memberOf" ],
|
||||
"role.name.ldap.attribute": [ "cn" ],
|
||||
"use.realm.roles.mapping": [ "true" ],
|
||||
@@ -221,7 +221,7 @@
|
||||
"pagination": [ "true" ],
|
||||
"connectionTrace": [ "false" ],
|
||||
"startTls": [ "false" ],
|
||||
"usersDn": [ "{{ ldap.dn.ou.users }}" ],
|
||||
"usersDn": [ "{{ LDAP.DN.OU.USERS }}" ],
|
||||
"connectionPooling": [ "true" ],
|
||||
"cachePolicy": [ "DEFAULT" ],
|
||||
"useKerberosForPasswordAuthentication": [ "false" ],
|
||||
@@ -229,10 +229,10 @@
|
||||
"enabled": [ "true" ],
|
||||
"bindCredential": [ "{{ KEYCLOAK_LDAP_BIND_PW }}" ],
|
||||
"changedSyncPeriod": [ "-1" ],
|
||||
"usernameLDAPAttribute": [ "{{ ldap.user.attributes.id }}" ],
|
||||
"usernameLDAPAttribute": [ "{{ LDAP.USER.ATTRIBUTES.ID }}" ],
|
||||
"bindDn": [ "{{ KEYCLOAK_LDAP_BIND_DN }}" ],
|
||||
"vendor": [ "other" ],
|
||||
"uuidLDAPAttribute": [ "{{ ldap.user.attributes.id }}" ],
|
||||
"uuidLDAPAttribute": [ "{{ LDAP.USER.ATTRIBUTES.ID }}" ],
|
||||
"allowKerberosAuthentication": [ "false" ],
|
||||
"connectionUrl": [ "{{ KEYCLOAK_LDAP_URL }}" ],
|
||||
"syncRegistrations": [ "true" ],
|
||||
@@ -248,7 +248,7 @@
|
||||
{{ KEYCLOAK_LDAP_USER_OBJECT_CLASSES | trim | tojson }}
|
||||
],
|
||||
|
||||
"rdnLDAPAttribute": [ "{{ ldap.user.attributes.id }}" ],
|
||||
"rdnLDAPAttribute": [ "{{ LDAP.USER.ATTRIBUTES.ID }}" ],
|
||||
"editMode": [ "WRITABLE" ],
|
||||
"validatePasswordPolicy": [ "false" ],
|
||||
|
||||
|
@@ -33,7 +33,7 @@
|
||||
"multivalued": false
|
||||
},
|
||||
{
|
||||
"name": ldap.user.attributes.ssh_public_key,
|
||||
"name": "{{ LDAP.USER.ATTRIBUTES.SSH_PUBLIC_KEY }}",
|
||||
"displayName": "SSH Public Key",
|
||||
"validations": {},
|
||||
"annotations": {},
|
||||
|
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "{{ ldap.user.attributes.nextcloud_quota }}",
|
||||
"name": "{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
@@ -19,11 +19,11 @@
|
||||
"introspection.token.claim": "true",
|
||||
"multivalued": "false",
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "{{ ldap.user.attributes.nextcloud_quota }}",
|
||||
"user.attribute": "{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}",
|
||||
"id.token.claim": "true",
|
||||
"lightweight.claim": "false",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "{{ ldap.user.attributes.nextcloud_quota }}",
|
||||
"claim.name": "{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}",
|
||||
"jsonType.label": "int"
|
||||
}
|
||||
},
|
||||
@@ -41,7 +41,7 @@
|
||||
"id.token.claim": "true",
|
||||
"lightweight.claim": "false",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "{{ldap.user.attributes.id}}",
|
||||
"claim.name": "{{LDAP.USER.ATTRIBUTES.ID}}",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
}
|
||||
|
@@ -55,17 +55,17 @@ KEYCLOAK_POST_LOGOUT_URIS: "+"
|
||||
|
||||
## LDAP
|
||||
KEYCLOAK_LDAP_ENABLED: "{{ applications | get_app_conf(application_id, 'features.ldap', False) }}"
|
||||
KEYCLOAK_LDAP_CMP_NAME: "{{ ldap.server.domain }}" # Name of the LDAP User Federation component in Keycloak (as shown in UI)
|
||||
KEYCLOAK_LDAP_BIND_DN: "{{ ldap.dn.administrator.data }}"
|
||||
KEYCLOAK_LDAP_BIND_PW: "{{ ldap.bind_credential }}"
|
||||
KEYCLOAK_LDAP_URL: "{{ ldap.server.uri }}"
|
||||
KEYCLOAK_LDAP_CMP_NAME: "{{ LDAP.SERVER.DOMAIN }}" # Name of the LDAP User Federation component in Keycloak (as shown in UI)
|
||||
KEYCLOAK_LDAP_BIND_DN: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
KEYCLOAK_LDAP_BIND_PW: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
KEYCLOAK_LDAP_URL: "{{ LDAP.SERVER.URI }}"
|
||||
|
||||
# It's important to filter the posixAccount class out, because it is just used by ansible
|
||||
KEYCLOAK_LDAP_USER_OBJECT_CLASSES: >
|
||||
{{
|
||||
(
|
||||
(ldap.user.objects.structural | reject('equalto','posixAccount') | list)
|
||||
+ (ldap.user.objects.auxiliary | dict2items | map(attribute='value') | list)
|
||||
(LDAP.USER.OBJECTS.STRUCTURAL | reject('equalto','posixAccount') | list)
|
||||
+ (LDAP.USER.OBJECTS.AUXILIARY | dict2items | map(attribute='value') | list)
|
||||
) | join(', ')
|
||||
}}
|
||||
|
||||
|
@@ -8,7 +8,7 @@ LAM_PASSWORD= {{ applications | get_app_conf(application_id, 'cred
|
||||
LAM_CONFIGURATION_DATABASE= files # configuration database (files or mysql) @todo implement mariadb
|
||||
|
||||
# LDAP Configuration
|
||||
LDAP_SERVER= {{ldap.server.domain}} # domain of LDAP database root entry
|
||||
LDAP_BASE_DN= {{ldap.dn.root}} # LDAP base DN to overwrite value generated by LDAP_DOMAIN
|
||||
LDAP_USER= {{ldap.dn.administrator.data}} # LDAP admin user (set as login user for LAM)
|
||||
LDAP_ADMIN_PASSWORD= {{ldap.bind_credential}} # LDAP admin password
|
||||
LDAP_SERVER= {{ LDAP.SERVER.DOMAIN }} # domain of LDAP database root entry
|
||||
LDAP_BASE_DN= {{ LDAP.DN.ROOT }} # LDAP base DN to overwrite value generated by LDAP_DOMAIN
|
||||
LDAP_USER= {{ LDAP.DN.ADMINISTRATOR.DATA }} # LDAP admin user (set as login user for LAM)
|
||||
LDAP_ADMIN_PASSWORD= {{ LDAP.BIND_CREDENTIAL }} # LDAP admin password
|
@@ -3,7 +3,7 @@
|
||||
|
||||
- name: Set Nextcloud LDAP bind password
|
||||
command: >
|
||||
{{ NEXTCLOUD_DOCKER_EXEC_OCC }} ldap:set-config s01 ldapAgentPassword "{{ ldap.bind_credential }}"
|
||||
{{ NEXTCLOUD_DOCKER_EXEC_OCC }} ldap:set-config s01 ldapAgentPassword "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
@@ -97,10 +97,10 @@ return array (
|
||||
// note: on Keycloak, OIDC name claim = "${given_name} ${family_name}" or one of them if any is missing
|
||||
//
|
||||
'oidc_login_attributes' => array (
|
||||
'id' => '{{ldap.user.attributes.id}}',
|
||||
'id' => '{{LDAP.USER.ATTRIBUTES.ID}}',
|
||||
'name' => 'name',
|
||||
'mail' => 'email',
|
||||
'quota' => '{{ ldap.user.attributes.nextcloud_quota }}',
|
||||
'quota' => '{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}',
|
||||
# 'home' => 'homeDirectory', # Not implemented yet
|
||||
'ldap_uid' => '{{ OIDC.ATTRIBUTES.USERNAME }}',
|
||||
# 'groups' => 'ownCloudGroups', # Not implemented yet
|
||||
|
@@ -32,17 +32,17 @@ plugin_configuration:
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_base"
|
||||
configvalue: "{{ldap.dn.root}}"
|
||||
configvalue: "{{ LDAP.DN.ROOT }}"
|
||||
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_base_groups"
|
||||
configvalue: "{{ldap.dn.root}}"
|
||||
configvalue: "{{ LDAP.DN.ROOT }}"
|
||||
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_base_users"
|
||||
configvalue: "{{ldap.dn.ou.users}}"
|
||||
configvalue: "{{LDAP.DN.OU.USERS}}"
|
||||
|
||||
-
|
||||
appid: "user_ldap"
|
||||
@@ -67,7 +67,7 @@ plugin_configuration:
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_dn"
|
||||
configvalue: "{{ldap.dn.administrator.data}}"
|
||||
configvalue: "{{LDAP.DN.ADMINISTRATOR.DATA}}"
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_email_attr"
|
||||
@@ -103,11 +103,11 @@ plugin_configuration:
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_host"
|
||||
configvalue: "{{ldap.server.domain}}"
|
||||
configvalue: "{{ LDAP.SERVER.DOMAIN }}"
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_login_filter"
|
||||
configvalue: "{{ ldap.filters.users.login }}"
|
||||
configvalue: "{{ LDAP.FILTERS.USERS.LOGIN }}"
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_login_filter_mode"
|
||||
@@ -165,7 +165,7 @@ plugin_configuration:
|
||||
configkey: "s01ldap_userlist_filter"
|
||||
configvalue: |-
|
||||
{% if applications | get_app_conf(application_id, 'plugins.user_ldap.user_directory.enabled', True) %}
|
||||
{{ ldap.filters.users.all }}
|
||||
{{ LDAP.FILTERS.USERS.ALL }}
|
||||
{% else %}
|
||||
()
|
||||
{% endif %}
|
||||
@@ -181,4 +181,4 @@ plugin_configuration:
|
||||
-
|
||||
appid: "user_ldap"
|
||||
configkey: "s01ldap_expert_username_attr"
|
||||
configvalue: "{{ldap.user.attributes.id}}"
|
||||
configvalue: "{{LDAP.USER.ATTRIBUTES.ID}}"
|
@@ -1,14 +1,14 @@
|
||||
openproject_ldap:
|
||||
name: "{{ PRIMARY_DOMAIN }}" # Display name for the LDAP connection in OpenProject
|
||||
host: "{{ ldap.server.domain }}" # LDAP server address
|
||||
port: "{{ ldap.server.port }}" # LDAP server port (typically 389 or 636)
|
||||
account: "{{ ldap.dn.administrator.data }}" # Bind DN (used for authentication)
|
||||
account_password: "{{ ldap.bind_credential }}" # Bind password
|
||||
base_dn: "{{ ldap.dn.ou.users }}" # Base DN for user search
|
||||
attr_login: "{{ ldap.user.attributes.id }}" # LDAP attribute used for login
|
||||
host: "{{ LDAP.SERVER.DOMAIN }}" # LDAP server address
|
||||
port: "{{ LDAP.SERVER.PORT }}" # LDAP server port (typically 389 or 636)
|
||||
account: "{{ LDAP.DN.ADMINISTRATOR.DATA }}" # Bind DN (used for authentication)
|
||||
account_password: "{{ LDAP.BIND_CREDENTIAL }}" # Bind password
|
||||
base_dn: "{{ LDAP.DN.OU.USERS }}" # Base DN for user search
|
||||
attr_login: "{{ LDAP.USER.ATTRIBUTES.ID }}" # LDAP attribute used for login
|
||||
attr_firstname: "givenName" # LDAP attribute for first name
|
||||
attr_lastname: "{{ ldap.user.attributes.surname }}" # LDAP attribute for last name
|
||||
attr_mail: "{{ ldap.user.attributes.mail }}" # LDAP attribute for email
|
||||
attr_lastname: "{{ LDAP.USER.ATTRIBUTES.SURNAME }}" # LDAP attribute for last name
|
||||
attr_mail: "{{ LDAP.USER.ATTRIBUTES.MAIL }}" # LDAP attribute for email
|
||||
attr_admin: "{{ openproject_filters.administrators }}" # Optional: LDAP attribute for admin group (leave empty if unused)
|
||||
onthefly_register: true # Automatically create users on first login
|
||||
tls_mode: 0 # 0 = No TLS, 1 = TLS, 2 = STARTTLS
|
||||
|
@@ -5,17 +5,17 @@ application_id: "web-app-openproject"
|
||||
database_type: "postgres"
|
||||
|
||||
# Open Project Specific
|
||||
openproject_version: "{{ applications | get_app_conf(application_id, 'docker.services.web.version', True) }}"
|
||||
openproject_image: "{{ applications | get_app_conf(application_id, 'docker.services.web.image', True) }}"
|
||||
openproject_volume: "{{ applications | get_app_conf(application_id, 'docker.volumes.data', True) }}"
|
||||
openproject_web_name: "{{ applications | get_app_conf(application_id, 'docker.services.web.name', True) }}"
|
||||
openproject_seeder_name: "{{ applications | get_app_conf(application_id, 'docker.services.seeder.name', True) }}"
|
||||
openproject_cron_name: "{{ applications | get_app_conf(application_id, 'docker.services.cron.name', True) }}"
|
||||
openproject_proxy_name: "{{ applications | get_app_conf(application_id, 'docker.services.proxy.name', True) }}"
|
||||
openproject_worker_name: "{{ applications | get_app_conf(application_id, 'docker.services.worker.name', True) }}"
|
||||
openproject_version: "{{ applications | get_app_conf(application_id, 'docker.services.web.version') }}"
|
||||
openproject_image: "{{ applications | get_app_conf(application_id, 'docker.services.web.image') }}"
|
||||
openproject_volume: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
|
||||
openproject_web_name: "{{ applications | get_app_conf(application_id, 'docker.services.web.name') }}"
|
||||
openproject_seeder_name: "{{ applications | get_app_conf(application_id, 'docker.services.seeder.name') }}"
|
||||
openproject_cron_name: "{{ applications | get_app_conf(application_id, 'docker.services.cron.name') }}"
|
||||
openproject_proxy_name: "{{ applications | get_app_conf(application_id, 'docker.services.proxy.name') }}"
|
||||
openproject_worker_name: "{{ applications | get_app_conf(application_id, 'docker.services.worker.name') }}"
|
||||
|
||||
# Open Project Cache
|
||||
openproject_cache_name: "{{ applications | get_app_conf(application_id, 'docker.services.cache.name', True) }}"
|
||||
openproject_cache_name: "{{ applications | get_app_conf(application_id, 'docker.services.cache.name') }}"
|
||||
openproject_cache_image: "{{ applications
|
||||
| get_app_conf(application_id, 'docker.services.cache.image')
|
||||
or applications
|
||||
@@ -45,11 +45,11 @@ openproject_rails_settings:
|
||||
smtp_ssl: false
|
||||
|
||||
openproject_filters:
|
||||
administrators: "{{ '(memberOf=cn=openproject-admins,' ~ ldap.dn.ou.roles ~ ')'
|
||||
if applications | get_app_conf(application_id, 'ldap.filters.administrators', True) else '' }}"
|
||||
administrators: "{{ '(memberOf=cn=openproject-admins,' ~ LDAP.DN.OU.ROLES ~ ')'
|
||||
if applications | get_app_conf(application_id, 'ldap.filters.administrators') else '' }}"
|
||||
|
||||
users: "{{ '(memberOf=cn=openproject-users,' ~ ldap.dn.ou.roles ~ ')'
|
||||
if applications | get_app_conf(application_id, 'ldap.filters.users', True) else '' }}"
|
||||
users: "{{ '(memberOf=cn=openproject-users,' ~ LDAP.DN.OU.ROLES ~ ')'
|
||||
if applications | get_app_conf(application_id, 'ldap.filters.users') else '' }}"
|
||||
|
||||
# Docker
|
||||
docker_repository_branch: "stable/{{ openproject_version }}"
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# @See https://github.com/leenooks/phpLDAPadmin/wiki/Docker-Container
|
||||
APP_URL= {{ domains | get_url(application_id, WEB_PROTOCOL) }}
|
||||
LDAP_HOST= {{ldap.server.domain}}
|
||||
LDAP_HOST= {{ LDAP.SERVER.DOMAIN }}
|
@@ -21,21 +21,21 @@
|
||||
sh -c 'php artisan tinker << "EOF"
|
||||
$s = \App\Models\Setting::getSettings();
|
||||
$s->ldap_enabled = 1;
|
||||
$s->ldap_server = "{{ ldap.server.uri }}";
|
||||
$s->ldap_port = {{ ldap.server.port }};
|
||||
$s->ldap_uname = "{{ ldap.dn.administrator.data }}";
|
||||
$s->ldap_basedn = "{{ ldap.dn.ou.users }}";
|
||||
$s->ldap_server = "{{ LDAP.SERVER.URI }}";
|
||||
$s->ldap_port = {{ LDAP.SERVER.PORT }};
|
||||
$s->ldap_uname = "{{ LDAP.DN.ADMINISTRATOR.DATA }}";
|
||||
$s->ldap_basedn = "{{ LDAP.DN.OU.USERS }}";
|
||||
$s->ldap_filter = "&(objectClass=inetOrgPerson)";
|
||||
$s->ldap_username_field = "{{ ldap.user.attributes.id }}";
|
||||
$s->ldap_fname_field = "{{ ldap.user.attributes.firstname }}";
|
||||
$s->ldap_lname_field = "{{ ldap.user.attributes.surname }}";
|
||||
$s->ldap_username_field = "{{ LDAP.USER.ATTRIBUTES.ID }}";
|
||||
$s->ldap_fname_field = "{{ LDAP.USER.ATTRIBUTES.FIRSTNAME }}";
|
||||
$s->ldap_lname_field = "{{ LDAP.USER.ATTRIBUTES.SURNAME }}";
|
||||
$s->ldap_auth_filter_query = "uid=";
|
||||
$s->ldap_version = 3;
|
||||
$s->ldap_pw_sync = 0;
|
||||
$s->is_ad = 0;
|
||||
$s->ad_domain = "";
|
||||
$s->ldap_default_group = "";
|
||||
$s->ldap_email = "{{ ldap.user.attributes.mail }}";
|
||||
$s->ldap_email = "{{ LDAP.USER.ATTRIBUTES.MAIL }}";
|
||||
$s->custom_forgot_pass_url = "{{ OIDC.CLIENT.RESET_CREDENTIALS }}";
|
||||
$s->save();
|
||||
EOF'
|
||||
@@ -62,7 +62,7 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/* encrypt the clear-text password */
|
||||
\$encrypted = Crypt::encrypt('{{ ldap.bind_credential }}');
|
||||
\$encrypted = Crypt::encrypt('{{ LDAP.BIND_CREDENTIAL }}');
|
||||
|
||||
/* write it straight into settings.ldap_pword */
|
||||
/* update the one and only row in `settings` */
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Integration test: ensure every ALL-CAPS variable is defined only once project-wide.
|
||||
Integration test: ensure every TOP-LEVEL ALL-CAPS variable is defined only once project-wide.
|
||||
|
||||
Scope (by design):
|
||||
- group_vars/**/*.yml
|
||||
@@ -8,9 +8,11 @@ Scope (by design):
|
||||
- roles/*/defaults/*.yml
|
||||
- roles/*/defauls/*.yml # included on purpose in case of folder typos
|
||||
|
||||
A variable is considered a “constant” if its key matches: ^[A-Z0-9_]+$
|
||||
If a constant is declared more than once across the scanned files, the test fails
|
||||
with a clear message explaining that such constants must be defined only once.
|
||||
A variable is considered a “constant” if its KEY (at the top level of a YAML document)
|
||||
matches: ^[A-Z0-9_]+$
|
||||
|
||||
Only TOP-LEVEL keys are checked for uniqueness. Nested keys are ignored to allow
|
||||
namespacing like DICTIONARYA.ENTRY and DICTIONARYB.ENTRY without conflicts.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -41,42 +43,32 @@ def _iter_yaml_files():
|
||||
seen = set()
|
||||
for pattern in patterns:
|
||||
for path in glob.glob(pattern, recursive=True):
|
||||
# Normalize and deduplicate
|
||||
norm = os.path.normpath(path)
|
||||
if norm not in seen and os.path.isfile(norm):
|
||||
seen.add(norm)
|
||||
yield norm
|
||||
|
||||
|
||||
def _extract_uppercase_keys_from_mapping(mapping):
|
||||
def _extract_top_level_uppercase_keys(docs):
|
||||
"""
|
||||
Recursively extract ALL-CAPS keys from any YAML mapping.
|
||||
Returns a set of keys found in this mapping (deduplicated for the file).
|
||||
Return a set of TOP-LEVEL ALL-CAPS keys found across all mapping documents in a file.
|
||||
Nested keys are intentionally ignored.
|
||||
"""
|
||||
found = set()
|
||||
|
||||
def walk(node):
|
||||
if isinstance(node, dict):
|
||||
for k, v in node.items():
|
||||
# Only consider string keys
|
||||
for doc in docs:
|
||||
if isinstance(doc, dict):
|
||||
for k in doc.keys():
|
||||
if isinstance(k, str) and UPPER_CONST_RE.match(k):
|
||||
found.add(k)
|
||||
# Recurse into values to catch nested mappings too
|
||||
walk(v)
|
||||
elif isinstance(node, list):
|
||||
for item in node:
|
||||
walk(item)
|
||||
|
||||
walk(mapping)
|
||||
return found
|
||||
|
||||
|
||||
class TestUppercaseConstantVarsUnique(unittest.TestCase):
|
||||
def test_uppercase_constants_unique(self):
|
||||
# Track where each constant is defined
|
||||
# Track where each TOP-LEVEL constant is defined
|
||||
constant_to_files = defaultdict(set)
|
||||
|
||||
# Track YAML parse errors to fail fast with a helpful message
|
||||
# Track YAML parse errors to fail with a helpful message
|
||||
parse_errors = []
|
||||
|
||||
yaml_files = list(_iter_yaml_files())
|
||||
@@ -88,35 +80,32 @@ class TestUppercaseConstantVarsUnique(unittest.TestCase):
|
||||
parse_errors.append(f"{yml}: {e}")
|
||||
continue
|
||||
|
||||
# Some files may be empty or contain only comments
|
||||
if not docs:
|
||||
continue
|
||||
|
||||
# Collect ALL-CAPS keys for this file (dedup per file)
|
||||
file_constants = set()
|
||||
for doc in docs:
|
||||
if isinstance(doc, dict):
|
||||
file_constants |= _extract_uppercase_keys_from_mapping(doc)
|
||||
# Non-mapping documents (e.g., lists/None) are ignored
|
||||
file_constants = _extract_top_level_uppercase_keys(docs)
|
||||
|
||||
for const in file_constants:
|
||||
constant_to_files[const].add(yml)
|
||||
|
||||
# Fail if YAML parsing had errors
|
||||
if parse_errors:
|
||||
self.fail(
|
||||
"YAML parsing failed for one or more files:\n"
|
||||
+ "\n".join(f"- {err}" for err in parse_errors)
|
||||
)
|
||||
|
||||
# Find duplicates (same constant in more than one file)
|
||||
duplicates = {c: sorted(files) for c, files in constant_to_files.items() if len(files) > 1}
|
||||
# Duplicates are same TOP-LEVEL constant appearing in >1 files
|
||||
duplicates = {
|
||||
c: sorted(files)
|
||||
for c, files in constant_to_files.items()
|
||||
if len(files) > 1
|
||||
}
|
||||
|
||||
if duplicates:
|
||||
msg_lines = [
|
||||
"Found constants defined more than once. "
|
||||
"ALL-CAPS variables are treated as constants and must be defined only once project-wide.\n"
|
||||
"Please consolidate each duplicated constant into a single authoritative location (e.g., one vars/defaults file).",
|
||||
"Found TOP-LEVEL constants defined more than once. "
|
||||
"ALL-CAPS top-level variables are treated as constants and must be defined only once project-wide.\n"
|
||||
"Nested ALL-CAPS keys are allowed and ignored by this test.",
|
||||
"",
|
||||
]
|
||||
for const, files in sorted(duplicates.items()):
|
||||
|
@@ -42,19 +42,19 @@ class TestBuildLdapRoleEntries(unittest.TestCase):
|
||||
}
|
||||
|
||||
self.ldap = {
|
||||
"dn": {
|
||||
"ou": {
|
||||
"users": "ou=users,dc=example,dc=org",
|
||||
"roles": "ou=roles,dc=example,dc=org"
|
||||
"DN": {
|
||||
"OU": {
|
||||
"USERS": "ou=users,dc=example,dc=org",
|
||||
"ROLES": "ou=roles,dc=example,dc=org"
|
||||
}
|
||||
},
|
||||
"user":{
|
||||
"attributes": {
|
||||
"id": "uid"
|
||||
"USER":{
|
||||
"ATTRIBUTES": {
|
||||
"ID": "uid"
|
||||
}
|
||||
},
|
||||
"rbac": {
|
||||
"flavors": ["posixGroup", "groupOfNames"]
|
||||
"RBAC": {
|
||||
"FLAVORS": ["posixGroup", "groupOfNames"]
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user