diff --git a/seed/openldap/dictionaries/21_openldap-server.xml b/seed/openldap/dictionaries/21_openldap-server.xml index d0f712d..c23a762 100644 --- a/seed/openldap/dictionaries/21_openldap-server.xml +++ b/seed/openldap/dictionaries/21_openldap-server.xml @@ -14,6 +14,7 @@ /secrets/admin_ldap.pwd /sysusers.d/risotto-openldap.conf /tmpfiles.d/0openldap-server.conf + /tests/openldap.yml diff --git a/seed/openldap/templates/config_acl.ldif b/seed/openldap/templates/config_acl.ldif index 30123bd..e26b4f6 100644 --- a/seed/openldap/templates/config_acl.ldif +++ b/seed/openldap/templates/config_acl.ldif @@ -1,10 +1,22 @@ +%set %%name_family = 'gnunux' %set %%dns = {} %set %%groups = [] +%%groups.append('cn=remote_test0,' + %%ldapclient_base_dn)%slurp +%%groups.append('cn=remote_test1,' + %%ldapclient_base_dn)%slurp +%%groups.append('cn=remote_test2,' + %%ldapclient_base_dn)%slurp +%%dns.setdefault(None, []).append(('cn=remote_test0,' + %%ldapclient_base_dn, 'read'))%slurp +%%dns.setdefault('all', []).append(('cn=remote_test1,' + %%ldapclient_base_dn, 'read'))%slurp +%%dns.setdefault(%%name_family, []).append(('cn=remote_test2,' + %%ldapclient_base_dn, 'read'))%slurp %for %%remote in %%accounts.remotes %set %%name = %%normalize_family(%%remote) %set %%family = %%accounts['remote_' + %%name]['family_' + %%name] %%groups.append(%%accounts['remote_' + %%name]['dn_' + %%name])%slurp -%%dns.setdefault(%%family, []).append((%%accounts['remote_' + %%name]['dn_' + %%name], %%accounts['remote_' + %%name]['read_only_' + %%name]))%slurp + %if %%accounts['remote_' + %%name]['read_only_' + %%name] + %set %%right = 'read' + %else + %set %%right = 'write' + %end if +%%dns.setdefault(%%family, []).append((%%accounts['remote_' + %%name]['dn_' + %%name], %%right))%slurp %end for dn: olcDatabase={2}mdb,cn=config changetype:modify @@ -21,19 +33,28 @@ olcAccess: {1}to dn.subtree="%%ldap_group_dn" %set %%aclidx = 2 %for %%family, %%remotes in %%dns.items() %if %%family == 'all' -olcAccess: {%%aclidx}to dn.subtree="%%ldap_account_dn" - %else -olcAccess: {%%aclidx}to dn.subtree="%%calc_ldapclient_base_dn(%%ldapclient_base_dn, %%family)" + %continue %end if +olcAccess: {%%aclidx}to dn.subtree="%%calc_ldapclient_base_dn(%%ldapclient_base_dn, %%family)" by self read %for %%remote in %%remotes - by dn="%%remote[0]" %slurp - %if %%remote[1] -read - %else -write - %end if + by dn="%%remote[0]" %%remote[1] %end for - %set %%aclidx += 1 + %if %%family != 'all' and 'all' in %%dns + %for %%remote in %%dns['all'] + by dn="%%remote[0]" %%remote[1] + %end for + %end if + %set %%aclidx += 1 + %if %%family != 'all' by * none + %end if %end for +%if 'all' in %%dns +olcAccess: {%%aclidx}to dn.subtree="%%ldap_account_dn" + by self read + %for %%remote in %%dns['all'] + by dn="%%remote[0]" %%remote[1] + %end for + by * none +%end if diff --git a/seed/openldap/templates/openldap.yml b/seed/openldap/templates/openldap.yml new file mode 100644 index 0000000..5466d09 --- /dev/null +++ b/seed/openldap/templates/openldap.yml @@ -0,0 +1,40 @@ +%set %%username = "rougail_test@silique.fr" +%set %%username_family = "rougail_test@gnunux.info" +%set %%familydn = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, family_name='gnunux') +address: %%ip_eth0 +admin_dn: %%ldapclient_user +admin_password: %%ldapclient_user_password +user_dn: cn=%%username,%%ldap_user_dn +user_password: %%get_password(server_name=%%ldap_server_address, username=%%username, description="ldap user", type="cleartext", hide=%%hide_secret, temporary=True) +user_family_dn: cn=%%username_family,%%familydn +user_family_password: %%get_password(server_name=%%ldap_server_address, username=%%username_family, description="ldap family user", type="cleartext", hide=%%hide_secret, temporary=True) +base_account_dn: %%ldap_account_dn +base_user_dn: %%ldap_user_dn +base_family_dn: %%familydn +base_group_dn: %%ldap_group_dn +%for %%idx in %%range(3) + %set %%name = 'remote_test' + %%str(%%idx) +remote%%idx: cn=%%name,%%ldapclient_base_dn +remote_password%%idx: %%get_password(server_name=%%domain_name_eth0, username=%%name, description="remote account", type="cleartext", hide=%%hide_secret, temporary=True) +%end for +users: +%for %%user in %%accounts.users.ldap_user_mail + %%user: cn=%%user,%%ldap_user_dn +%end for +%for %%family in %%accounts.families + %set %%families = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, %%family) + %for %%user in %%accounts['family_' + %%family]['users_' + %%family]['ldap_user_mail_' + %%family] + %%user: cn=%%user,%%families + %end for +%end for +groups: + users: +%for %%user in %%accounts.users.ldap_user_mail + - cn=%%user,%%ldap_user_dn +%end for +%for %%family in %%accounts.families + %%family: + %for %%user in %%accounts['family_' + %%family]['users_' + %%family]['ldap_user_mail_' + %%family] + - cn=%%user,%%families + %end for +%end for diff --git a/seed/openldap/templates/users.ldif b/seed/openldap/templates/users.ldif index 03c6f1d..057c5bc 100644 --- a/seed/openldap/templates/users.ldif +++ b/seed/openldap/templates/users.ldif @@ -1,3 +1,4 @@ +%set name_family = 'gnunux' # BaseDN %set groups = {} dn: %%ldapclient_base_dn @@ -11,13 +12,21 @@ objectClass: organizationalUnit %end if # Remote +%set %%acc = [] +%for %%idx in %%range(3) + %set %%name = 'remote_test' + %%str(%%idx) +%%acc.append(('cn=' + %%name + ',' + %%ldapclient_base_dn, %%name, %%get_password(server_name=%%domain_name_eth0, username=%%name, description="remote account", type="cleartext", hide=%%hide_secret, temporary=True)))%slurp +%end for %for %%remote in %%accounts.remotes %set %%name = %%normalize_family(%%remote) -dn: %%accounts['remote_' + %%name]['dn_' + %%name] +%%acc.append((%%accounts['remote_' + %%name]['dn_' + %%name], %%remote, %%accounts['remote_' + %%name]['password_' + %%name]))%slurp +%end for +%for %%dn, %%remote, %%password in %%acc +dn: %%dn cn: %%remote sn: %%remote uid: %%remote -userPassword:: %%ssha_encode(%%accounts['remote_' + %%name]['password_' + %%name]) +userPassword:: %%ssha_encode(%%password) objectClass: top objectClass: inetOrgPerson diff --git a/seed/openldap/templates/users_mod.ldif b/seed/openldap/templates/users_mod.ldif index 9ff3b24..f4fce20 100644 --- a/seed/openldap/templates/users_mod.ldif +++ b/seed/openldap/templates/users_mod.ldif @@ -1,16 +1,27 @@ +%set groups = {} # Remote +%set %%acc = [] +%for %%idx in %%range(3) + %set %%name = 'remote_test' + %%str(%%idx) +%%acc.append(('cn=' + %%name + ',' + %%ldapclient_base_dn, %%get_password(server_name=%%domain_name_eth0, username=%%name, description="remote account", type="cleartext", hide=%%hide_secret, temporary=True)))%slurp +%end for %for %%remote in %%accounts.remotes %set %%name = %%normalize_family(%%remote) -dn: %%accounts['remote_' + %%name]['dn_' + %%name] +%%acc.append((%%accounts['remote_' + %%name]['dn_' + %%name], %%accounts['remote_' + %%name]['password_' + %%name]))%slurp +%end for +%for %%dn, %%password in %%acc +dn: %%dn changetype: modify replace: userPassword -userPassword:: %%ssha_encode(%%accounts['remote_' + %%name]['password_' + %%name]) +userPassword:: %%ssha_encode(%%password) %end for # Users %set %%users = %%ldap_user_dn %for %%user in %%accounts.users.ldap_user_mail -dn: cn=%%user,%%users +%set %%userdn = 'cn=' + %%user + ',' + %%users +%%groups.setdefault('users', []).append(%%userdn)%slurp +dn: %%userdn changetype: modify #add: objectClass #objectClass: inetLocalMailRecipient @@ -28,7 +39,9 @@ mailLocalAddress: %%alias %for %%family in %%accounts.families %set %%families = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, %%family) %for %%user in %%accounts['family_' + %%family]['users_' + %%family]['ldap_user_mail_' + %%family] -dn: cn=%%user,%%families +%set %%userdn = 'cn=' + %%user + ',' + %%families +%%groups.setdefault(%%family, []).append(%%userdn)%slurp +dn: %%userdn changetype: modify #add: objectClass #objectClass: inetLocalMailRecipient @@ -43,3 +56,14 @@ mailLocalAddress: %%alias %end for %end for +# Groups +%set %%groupdn = %%ldap_group_dn +%for %%group, %%members in %%groups.items() +dn: cn=%%group,%%groupdn +changetype: modify +replace: member + %for %%member in %%members +member: %%member + %end for + +%end for diff --git a/seed/openldap/tests/test_openldap.py b/seed/openldap/tests/test_openldap.py new file mode 100644 index 0000000..62a8977 --- /dev/null +++ b/seed/openldap/tests/test_openldap.py @@ -0,0 +1,162 @@ +from yaml import load, SafeLoader +from os import environ +from pytest import raises +from ldap import NO_SUCH_OBJECT, INVALID_CREDENTIALS, OPT_X_TLS_NEVER, OPT_X_TLS_REQUIRE_CERT, SCOPE_SUBTREE, set_option, initialize + + +def test_ldap_wrong_password(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + with raises(INVALID_CREDENTIALS): + l.simple_bind_s(data['admin_dn'], 'a') + + +def test_ldap_admin(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + l.simple_bind_s(data['admin_dn'], data['admin_password']) + + assert l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + + +def test_ldap_accounts(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + l.simple_bind_s(data['admin_dn'], data['admin_password']) + + for dn, attrs in l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']): + cn = attrs['cn'][0].decode() + assert cn in data['users'] + assert data['users'][cn] == dn + del data['users'][cn] + + # all users are retrieved + assert not data['users'] + + +def test_ldap_groups(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + l.simple_bind_s(data['admin_dn'], data['admin_password']) + + for dn, attrs in l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn', 'member']): + cn = attrs['cn'][0].decode() + assert cn in data['groups'] + assert set(data['groups'][cn]) == set([member.decode() for member in attrs['member']]) + del data['groups'][cn] + + # all groups are retrieved + assert not data['groups'] + + +def test_ldap_user(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + l.simple_bind_s(data['user_dn'], data['user_password']) + + +def test_ldap_user_family(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + l.simple_bind_s(data['user_family_dn'], data['user_family_password']) + + +def test_ldap_remote_auth(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + l.simple_bind_s(data['remote0'], data['remote_password0']) + l.simple_bind_s(data['remote1'], data['remote_password1']) + l.simple_bind_s(data['remote2'], data['remote_password2']) + + +def test_ldap_remote_base(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + # + l.simple_bind_s(data['remote0'], data['remote_password0']) + with raises(NO_SUCH_OBJECT): + l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + # + l.simple_bind_s(data['remote1'], data['remote_password1']) + l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + # + l.simple_bind_s(data['remote2'], data['remote_password2']) + with raises(NO_SUCH_OBJECT): + l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + + +def test_ldap_remote_users(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + # + l.simple_bind_s(data['remote0'], data['remote_password0']) + l.search_s(data['base_user_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + # + l.simple_bind_s(data['remote1'], data['remote_password1']) + l.search_s(data['base_user_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + # + l.simple_bind_s(data['remote2'], data['remote_password2']) + with raises(NO_SUCH_OBJECT): + l.search_s(data['base_user_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + + +def test_ldap_remote_family(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + # + l.simple_bind_s(data['remote0'], data['remote_password0']) + with raises(NO_SUCH_OBJECT): + l.search_s(data['base_family_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + # + l.simple_bind_s(data['remote1'], data['remote_password1']) + l.search_s(data['base_family_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + # + l.simple_bind_s(data['remote2'], data['remote_password2']) + l.search_s(data['base_family_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']) + + +def test_ldap_remote_group(): + conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml' + with open(conf_file) as yaml: + data = load(yaml, Loader=SafeLoader) + set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER) + l = initialize(f'ldaps://{data["address"]}') + # + l.simple_bind_s(data['remote0'], data['remote_password0']) + l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn']) + # + l.simple_bind_s(data['remote1'], data['remote_password1']) + l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn']) + # + l.simple_bind_s(data['remote2'], data['remote_password2']) + l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn'])