unit tests: slapd initialization refactoring

This commit is contained in:
Éloi Rivard 2022-12-06 18:17:32 +01:00
parent 890888930e
commit c2e93b8773
9 changed files with 206 additions and 272 deletions

View file

@ -11,8 +11,10 @@ services:
# memberof overlay is already present in openldap docker image but only for groupOfUniqueNames. We need to overwrite it (until canaille can handle groupOfUniqueNames).
# https://github.com/osixia/docker-openldap/blob/master/image/service/slapd/assets/config/bootstrap/ldif/03-memberOf.ldif
- ../canaille/ldap_backend/schemas/oauth2-openldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/40-oauth2.ldif:ro
- ./ldif/bootstrap-tree.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-boostrap-tree.ldif:ro
- ./ldif/bootstrap-data.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/60-boostrap-data.ldif:ro
- ./ldif/bootstrap-users-tree.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-bootstrap-users-tree.ldif:ro
- ./ldif/bootstrap-oidc-tree.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-bootstrap-oidc-tree.ldif:ro
- ./ldif/bootstrap-users.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/60-bootstrap-users.ldif:ro
- ./ldif/bootstrap-oidc.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/70-bootstrap-oidc.ldif:ro
command: --copy-service --loglevel debug
ports:
- 5389:389

View file

@ -40,8 +40,10 @@ try:
)
for ldif in (
"ldif/bootstrap-tree.ldif",
"ldif/bootstrap-data.ldif",
"ldif/bootstrap-users-tree.ldif",
"ldif/bootstrap-oidc-tree.ldif",
"ldif/bootstrap-users.ldif",
"ldif/bootstrap-oidc.ldif",
):
with open(ldif) as fd:
try:

View file

@ -1,123 +0,0 @@
dn: ou=users,dc=mydomain,dc=tld
objectclass: organizationalUnit
ou: users
dn: ou=groups,dc=mydomain,dc=tld
objectclass: organizationalUnit
ou: groups
dn: uid=admin,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: Jane Doe
gn: Jane
sn: Doe
uid: admin
mail: admin@mydomain.tld
telephoneNumber: 555-000-000
employeeNumber: 1000
labeledURI: https://admin.example
postalAddress: 123, Admin Lane - Gotham City 12345
userPassword: {SSHA}7zQVLckaEc6cJEsS0ylVipvb2PAR/4tS
displayName: Jane.D
dn: uid=moderator,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: Jack Doe
gn: Jack
sn: Doe
uid: moderator
mail: moderator@mydomain.tld
telephoneNumber: 555-000-002
employeeNumber: 1002
labeledURI: https://moderator.example
userPassword: {SSHA}+eHyxWqajMHsOWnhONC2vbtfNZzKTkag
displayName: 👮 Jack 👮
dn: uid=user,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: John Doe
gn: John
sn: Doe
uid: user
mail: user@mydomain.tld
telephoneNumber: 555-000-001
employeeNumber: 1001
labeledURI: https://user.example
userPassword: {SSHA}Yr1ZxSljRsKyaTB30suY2iZ1KRTStF1X
displayName: Johnny
dn: uid=james,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: James Doe
gn: James
sn: Doe
uid: james
mail: james@mydomain.tld
telephoneNumber: 555-000-003
dn: cn=users,ou=groups,dc=mydomain,dc=tld
objectclass: groupOfNames
cn: users
description: The regulars users.
member: uid=admin,ou=users,dc=mydomain,dc=tld
member: uid=user,ou=users,dc=mydomain,dc=tld
dn: cn=admins,ou=groups,dc=mydomain,dc=tld
objectclass: groupOfNames
cn: admins
description: The administrators.
member: uid=admin,ou=users,dc=mydomain,dc=tld
dn: cn=moderators,ou=groups,dc=mydomain,dc=tld
objectclass: groupOfNames
cn: moderators
description: People who can manage users.
member: uid=moderator,ou=users,dc=mydomain,dc=tld
dn: oauthClientID=1JGkkzCbeHpGtlqgI5EENByf,ou=clients,ou=oauth,dc=mydomain,dc=tld
objectclass: oauthClient
oauthClientID: 1JGkkzCbeHpGtlqgI5EENByf
oauthClientSecret: 2xYPSReTQRmGG1yppMVZQ0ASXwFejPyirvuPbKhNa6TmKC5x
oauthClientName: Client1
oauthClientContact: admin@mydomain.tld
oauthClientURI: http://localhost:5001
oauthRedirectURIs: http://localhost:5001/authorize
oauthPostLogoutRedirectURI: http://localhost:5001/
oauthGrantType: authorization_code
oauthGrantType: refresh_token
oauthScope: openid
oauthScope: profile
oauthScope: email
oauthScope: groups
oauthScope: address
oauthScope: phone
oauthResponseType: code
oauthResponseType: id_token
oauthTokenEndpointAuthMethod: client_secret_basic
oauthAudience: oauthClientID=1JGkkzCbeHpGtlqgI5EENByf,ou=clients,ou=oauth,dc=mydomain,dc=tld
dn: oauthClientID=gn4yFN7GDykL7QP8v8gS9YfV,ou=clients,ou=oauth,dc=mydomain,dc=tld
objectclass: oauthClient
oauthClientID: gn4yFN7GDykL7QP8v8gS9YfV
oauthClientSecret: ouFJE5WpICt6hxTyf8icXPeeklMektMY4gV0Rmf3aY60VElA
oauthClientName: Client2
oauthClientContact: admin@mydomain.tld
oauthClientURI: http://localhost:5002
oauthRedirectURIs: http://localhost:5002/authorize
oauthPostLogoutRedirectURI: http://localhost:5002/
oauthGrantType: authorization_code
oauthGrantType: refresh_token
oauthScope: openid
oauthScope: profile
oauthScope: email
oauthScope: groups
oauthScope: address
oauthScope: phone
oauthResponseType: code
oauthResponseType: id_token
oauthTokenEndpointAuthMethod: client_secret_basic
oauthAudience: oauthClientID=gn4yFN7GDykL7QP8v8gS9YfV,ou=clients,ou=oauth,dc=mydomain,dc=tld

View file

@ -0,0 +1,43 @@
dn: oauthClientID=1JGkkzCbeHpGtlqgI5EENByf,ou=clients,ou=oauth,dc=mydomain,dc=tld
objectclass: oauthClient
oauthClientID: 1JGkkzCbeHpGtlqgI5EENByf
oauthClientSecret: 2xYPSReTQRmGG1yppMVZQ0ASXwFejPyirvuPbKhNa6TmKC5x
oauthClientName: Client1
oauthClientContact: admin@mydomain.tld
oauthClientURI: http://localhost:5001
oauthRedirectURIs: http://localhost:5001/authorize
oauthPostLogoutRedirectURI: http://localhost:5001/
oauthGrantType: authorization_code
oauthGrantType: refresh_token
oauthScope: openid
oauthScope: profile
oauthScope: email
oauthScope: groups
oauthScope: address
oauthScope: phone
oauthResponseType: code
oauthResponseType: id_token
oauthTokenEndpointAuthMethod: client_secret_basic
oauthAudience: oauthClientID=1JGkkzCbeHpGtlqgI5EENByf,ou=clients,ou=oauth,dc=mydomain,dc=tld
dn: oauthClientID=gn4yFN7GDykL7QP8v8gS9YfV,ou=clients,ou=oauth,dc=mydomain,dc=tld
objectclass: oauthClient
oauthClientID: gn4yFN7GDykL7QP8v8gS9YfV
oauthClientSecret: ouFJE5WpICt6hxTyf8icXPeeklMektMY4gV0Rmf3aY60VElA
oauthClientName: Client2
oauthClientContact: admin@mydomain.tld
oauthClientURI: http://localhost:5002
oauthRedirectURIs: http://localhost:5002/authorize
oauthPostLogoutRedirectURI: http://localhost:5002/
oauthGrantType: authorization_code
oauthGrantType: refresh_token
oauthScope: openid
oauthScope: profile
oauthScope: email
oauthScope: groups
oauthScope: address
oauthScope: phone
oauthResponseType: code
oauthResponseType: id_token
oauthTokenEndpointAuthMethod: client_secret_basic
oauthAudience: oauthClientID=gn4yFN7GDykL7QP8v8gS9YfV,ou=clients,ou=oauth,dc=mydomain,dc=tld

View file

@ -0,0 +1,7 @@
dn: ou=users,dc=mydomain,dc=tld
objectclass: organizationalUnit
ou: users
dn: ou=groups,dc=mydomain,dc=tld
objectclass: organizationalUnit
ou: groups

View file

@ -0,0 +1,71 @@
dn: uid=admin,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: Jane Doe
gn: Jane
sn: Doe
uid: admin
mail: admin@mydomain.tld
telephoneNumber: 555-000-000
employeeNumber: 1000
labeledURI: https://admin.example
postalAddress: 123, Admin Lane - Gotham City 12345
userPassword: {SSHA}7zQVLckaEc6cJEsS0ylVipvb2PAR/4tS
displayName: Jane.D
dn: uid=moderator,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: Jack Doe
gn: Jack
sn: Doe
uid: moderator
mail: moderator@mydomain.tld
telephoneNumber: 555-000-002
employeeNumber: 1002
labeledURI: https://moderator.example
userPassword: {SSHA}+eHyxWqajMHsOWnhONC2vbtfNZzKTkag
displayName: 👮 Jack 👮
dn: uid=user,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: John Doe
gn: John
sn: Doe
uid: user
mail: user@mydomain.tld
telephoneNumber: 555-000-001
employeeNumber: 1001
labeledURI: https://user.example
userPassword: {SSHA}Yr1ZxSljRsKyaTB30suY2iZ1KRTStF1X
displayName: Johnny
dn: uid=james,ou=users,dc=mydomain,dc=tld
objectclass: top
objectclass: inetOrgPerson
cn: James Doe
gn: James
sn: Doe
uid: james
mail: james@mydomain.tld
telephoneNumber: 555-000-003
dn: cn=users,ou=groups,dc=mydomain,dc=tld
objectclass: groupOfNames
cn: users
description: The regulars users.
member: uid=admin,ou=users,dc=mydomain,dc=tld
member: uid=user,ou=users,dc=mydomain,dc=tld
dn: cn=admins,ou=groups,dc=mydomain,dc=tld
objectclass: groupOfNames
cn: admins
description: The administrators.
member: uid=admin,ou=users,dc=mydomain,dc=tld
dn: cn=moderators,ou=groups,dc=mydomain,dc=tld
objectclass: groupOfNames
cn: moderators
description: People who can manage users.
member: uid=moderator,ou=users,dc=mydomain,dc=tld

View file

@ -4,79 +4,28 @@ import ldap
import pytest
from canaille import create_app
from canaille.commands import cli
from canaille.installation import InstallationException
from canaille.installation import setup_schemas
from canaille.ldap_backend.ldapobject import LDAPObject
from canaille.models import Group
from canaille.models import User
from flask_webtest import TestApp
from slapd import Slapd
from tests.conftest import CustomSlapdObject
@pytest.fixture(scope="module")
@pytest.fixture
def slapd_server():
slapd = CustomSlapdObject()
try:
slapd.start()
suffix_dc = slapd.suffix.split(",")[0][3:]
slapd.ldapadd(
"\n".join(
[
"dn: " + slapd.suffix,
"objectClass: dcObject",
"objectClass: organization",
"dc: " + suffix_dc,
"o: " + suffix_dc,
"",
"dn: " + slapd.root_dn,
"objectClass: applicationProcess",
"cn: " + slapd.root_cn,
"",
"dn: ou=users," + slapd.suffix,
"objectClass: organizationalUnit",
"ou: users",
"",
"dn: ou=groups," + slapd.suffix,
"objectClass: organizationalUnit",
"ou: groups",
]
)
+ "\n"
)
yield slapd
finally:
slapd.stop()
@pytest.fixture
def slapd_server_without_schemas():
slapd = Slapd()
try:
slapd.start()
suffix_dc = slapd.suffix.split(",")[0][3:]
slapd.ldapadd(
"\n".join(
[
"dn: " + slapd.suffix,
"objectClass: dcObject",
"objectClass: organization",
"dc: " + suffix_dc,
"o: " + suffix_dc,
"",
"dn: " + slapd.root_dn,
"objectClass: applicationProcess",
"cn: " + slapd.root_cn,
"",
"dn: ou=users," + slapd.suffix,
"objectClass: organizationalUnit",
"ou: users",
"",
"dn: ou=groups," + slapd.suffix,
"objectClass: organizationalUnit",
"ou: groups",
]
)
+ "\n"
)
slapd.init_tree()
for ldif in (
"demo/ldif/memberof-config.ldif",
"demo/ldif/bootstrap-users-tree.ldif",
"demo/ldif/bootstrap-users.ldif",
):
with open(ldif) as fd:
slapd.ldapadd(fd.read())
yield slapd
finally:
@ -111,17 +60,15 @@ def test_install_keypair(configuration, tmpdir):
assert os.path.exists(configuration["JWT"]["PUBLIC_KEY"])
def test_install_schemas(configuration, slapd_server_without_schemas):
configuration["LDAP"]["ROOT_DN"] = slapd_server_without_schemas.suffix
configuration["LDAP"]["URI"] = slapd_server_without_schemas.ldap_uri
configuration["LDAP"]["BIND_DN"] = slapd_server_without_schemas.root_dn
configuration["LDAP"]["BIND_PW"] = slapd_server_without_schemas.root_pw
def test_install_schemas(configuration, slapd_server):
configuration["LDAP"]["ROOT_DN"] = slapd_server.suffix
configuration["LDAP"]["URI"] = slapd_server.ldap_uri
configuration["LDAP"]["BIND_DN"] = slapd_server.root_dn
configuration["LDAP"]["BIND_PW"] = slapd_server.root_pw
conn = ldap.ldapobject.SimpleLDAPObject(slapd_server_without_schemas.ldap_uri)
conn = ldap.ldapobject.SimpleLDAPObject(slapd_server.ldap_uri)
conn.protocol_version = 3
conn.simple_bind_s(
slapd_server_without_schemas.root_dn, slapd_server_without_schemas.root_pw
)
conn.simple_bind_s(slapd_server.root_dn, slapd_server.root_pw)
assert "oauthClient" not in LDAPObject.ldap_object_classes(conn=conn, force=True)
@ -130,20 +77,18 @@ def test_install_schemas(configuration, slapd_server_without_schemas):
assert "oauthClient" in LDAPObject.ldap_object_classes(conn=conn, force=True)
conn.unbind_s()
slapd_server_without_schemas.stop()
slapd_server.stop()
def test_install_schemas_command(configuration, slapd_server_without_schemas):
configuration["LDAP"]["ROOT_DN"] = slapd_server_without_schemas.suffix
configuration["LDAP"]["URI"] = slapd_server_without_schemas.ldap_uri
configuration["LDAP"]["BIND_DN"] = slapd_server_without_schemas.root_dn
configuration["LDAP"]["BIND_PW"] = slapd_server_without_schemas.root_pw
def test_install_schemas_command(configuration, slapd_server):
configuration["LDAP"]["ROOT_DN"] = slapd_server.suffix
configuration["LDAP"]["URI"] = slapd_server.ldap_uri
configuration["LDAP"]["BIND_DN"] = slapd_server.root_dn
configuration["LDAP"]["BIND_PW"] = slapd_server.root_pw
conn = ldap.ldapobject.SimpleLDAPObject(slapd_server_without_schemas.ldap_uri)
conn = ldap.ldapobject.SimpleLDAPObject(slapd_server.ldap_uri)
conn.protocol_version = 3
conn.simple_bind_s(
slapd_server_without_schemas.root_dn, slapd_server_without_schemas.root_pw
)
conn.simple_bind_s(slapd_server.root_dn, slapd_server.root_pw)
assert "oauthClient" not in LDAPObject.ldap_object_classes(conn=conn, force=True)
@ -154,4 +99,4 @@ def test_install_schemas_command(configuration, slapd_server_without_schemas):
assert "oauthClient" in LDAPObject.ldap_object_classes(conn=conn, force=True)
conn.unbind_s()
slapd_server_without_schemas.stop()
slapd_server.stop()

View file

@ -26,11 +26,58 @@ class CustomSlapdObject(slapd.Slapd):
"cosine.ldif",
"nis.ldif",
"inetorgperson.ldif",
"canaille/ldap_backend/schemas/oauth2-openldap.ldif",
"demo/ldif/memberof-config.ldif",
),
)
def init_tree(self):
suffix_dc = self.suffix.split(",")[0][3:]
self.ldapadd(
"\n".join(
[
"dn: " + self.suffix,
"objectClass: dcObject",
"objectClass: organization",
"dc: " + suffix_dc,
"o: " + suffix_dc,
"",
"dn: " + self.root_dn,
"objectClass: applicationProcess",
"cn: " + self.root_cn,
]
)
+ "\n"
)
@pytest.fixture(scope="session")
def slapd_server():
slapd = CustomSlapdObject()
try:
slapd.start()
slapd.init_tree()
for ldif in (
"demo/ldif/memberof-config.ldif",
"canaille/ldap_backend/schemas/oauth2-openldap.ldif",
"demo/ldif/bootstrap-users-tree.ldif",
"demo/ldif/bootstrap-oidc-tree.ldif",
):
with open(ldif) as fd:
slapd.ldapadd(fd.read())
yield slapd
finally:
slapd.stop()
@pytest.fixture
def slapd_connection(slapd_server, testclient):
g.ldap = ldap.ldapobject.SimpleLDAPObject(slapd_server.ldap_uri)
g.ldap.protocol_version = 3
g.ldap.simple_bind_s(slapd_server.root_dn, slapd_server.root_pw)
yield g.ldap
if g.ldap:
g.ldap.unbind_s()
@pytest.fixture(scope="session")
def keypair():
@ -63,66 +110,6 @@ def keypair_path(keypair, tmp_path):
return private_key_path, public_key_path
@pytest.fixture(scope="session")
def raw_slapd_server():
slapd = CustomSlapdObject()
try:
slapd.start()
suffix_dc = slapd.suffix.split(",")[0][3:]
slapd.ldapadd(
"\n".join(
[
"dn: " + slapd.suffix,
"objectClass: dcObject",
"objectClass: organization",
"dc: " + suffix_dc,
"o: " + suffix_dc,
"",
"dn: " + slapd.root_dn,
"objectClass: applicationProcess",
"cn: " + slapd.root_cn,
"",
"dn: ou=users," + slapd.suffix,
"objectClass: organizationalUnit",
"ou: users",
"",
"dn: ou=groups," + slapd.suffix,
"objectClass: organizationalUnit",
"ou: groups",
]
)
+ "\n"
)
yield slapd
finally:
slapd.stop()
@pytest.fixture(scope="session")
def slapd_server(raw_slapd_server):
for ldif in ("demo/ldif/bootstrap-tree.ldif",):
with open(ldif) as fd:
raw_slapd_server.ldapadd(fd.read())
LDAPObject.root_dn = raw_slapd_server.suffix
User.base = "ou=users"
User.object_class = ["inetOrgPerson"]
Group.base = "ou=groups"
Group.object_class = ["groupOfNames"]
yield raw_slapd_server
@pytest.fixture
def slapd_connection(slapd_server, testclient):
g.ldap = ldap.ldapobject.SimpleLDAPObject(slapd_server.ldap_uri)
g.ldap.protocol_version = 3
g.ldap.simple_bind_s(slapd_server.root_dn, slapd_server.root_pw)
yield g.ldap
if g.ldap:
g.ldap.unbind_s()
@pytest.fixture
def configuration(slapd_server, smtpd, keypair_path):
smtpd.config.use_starttls = True