diff --git a/MANIFEST.in b/MANIFEST.in index 4d1242fe..30003764 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ include canaille/conf/*.sample.* -graft canaille/ldap_backend/schemas +graft canaille/backends/ldap/schemas graft canaille/templates graft canaille/themes graft canaille/translations diff --git a/canaille/__init__.py b/canaille/__init__.py index 17a4fd02..b43ee6cb 100644 --- a/canaille/__init__.py +++ b/canaille/__init__.py @@ -173,7 +173,7 @@ def create_app(config=None, validate=True): sentry_sdk = setup_sentry(app) try: from .oidc.oauth import setup_oauth - from .ldap_backend.backend import init_backend + from .backends.ldap.backend import init_backend from .app.i18n import setup_i18n setup_logging(app) diff --git a/canaille/app/commands.py b/canaille/app/commands.py index 1b36516b..83070b99 100644 --- a/canaille/app/commands.py +++ b/canaille/app/commands.py @@ -9,7 +9,7 @@ from flask.cli import with_appcontext def with_backendcontext(func): @functools.wraps(func) def _func(*args, **kwargs): - from canaille.ldap_backend.backend import ( + from canaille.backends.ldap.backend import ( setup_backend, teardown_backend, ) diff --git a/canaille/app/configuration.py b/canaille/app/configuration.py index c80ac2ac..adf3b249 100644 --- a/canaille/app/configuration.py +++ b/canaille/app/configuration.py @@ -16,7 +16,7 @@ def validate(config, validate_remote=False): if not validate_remote: return - from canaille.ldap_backend.backend import validate_configuration + from canaille.backends.ldap.backend import validate_configuration validate_configuration(config) validate_smtp_configuration(config) diff --git a/canaille/ldap_backend/__init__.py b/canaille/backends/__init__.py similarity index 100% rename from canaille/ldap_backend/__init__.py rename to canaille/backends/__init__.py diff --git a/tests/ldap/__init__.py b/canaille/backends/ldap/__init__.py similarity index 100% rename from tests/ldap/__init__.py rename to canaille/backends/ldap/__init__.py diff --git a/canaille/ldap_backend/backend.py b/canaille/backends/ldap/backend.py similarity index 100% rename from canaille/ldap_backend/backend.py rename to canaille/backends/ldap/backend.py diff --git a/canaille/ldap_backend/installation.py b/canaille/backends/ldap/installation.py similarity index 100% rename from canaille/ldap_backend/installation.py rename to canaille/backends/ldap/installation.py diff --git a/canaille/ldap_backend/ldapobject.py b/canaille/backends/ldap/ldapobject.py similarity index 100% rename from canaille/ldap_backend/ldapobject.py rename to canaille/backends/ldap/ldapobject.py diff --git a/canaille/ldap_backend/schemas/oauth2-openldap.ldif b/canaille/backends/ldap/schemas/oauth2-openldap.ldif similarity index 100% rename from canaille/ldap_backend/schemas/oauth2-openldap.ldif rename to canaille/backends/ldap/schemas/oauth2-openldap.ldif diff --git a/canaille/ldap_backend/schemas/oauth2-openldap.schema b/canaille/backends/ldap/schemas/oauth2-openldap.schema similarity index 100% rename from canaille/ldap_backend/schemas/oauth2-openldap.schema rename to canaille/backends/ldap/schemas/oauth2-openldap.schema diff --git a/canaille/ldap_backend/utils.py b/canaille/backends/ldap/utils.py similarity index 100% rename from canaille/ldap_backend/utils.py rename to canaille/backends/ldap/utils.py diff --git a/canaille/core/models.py b/canaille/core/models.py index a8cf331a..107fe082 100644 --- a/canaille/core/models.py +++ b/canaille/core/models.py @@ -1,5 +1,5 @@ import ldap.filter -from canaille.ldap_backend.ldapobject import LDAPObject +from canaille.backends.ldap.ldapobject import LDAPObject from flask import current_app from flask import session diff --git a/canaille/oidc/installation.py b/canaille/oidc/installation.py index be42ebb5..ea1b2a40 100644 --- a/canaille/oidc/installation.py +++ b/canaille/oidc/installation.py @@ -1,7 +1,7 @@ import os -from canaille.ldap_backend.installation import install_schema -from canaille.ldap_backend.installation import ldap_connection +from canaille.backends.ldap.installation import install_schema +from canaille.backends.ldap.installation import ldap_connection from canaille.oidc.models import AuthorizationCode from canaille.oidc.models import Client from canaille.oidc.models import Consent @@ -54,5 +54,5 @@ def setup_schemas(config): install_schema( config, os.path.dirname(os.path.dirname(__file__)) - + "/ldap_backend/schemas/oauth2-openldap.ldif", + + "/backends/ldap/schemas/oauth2-openldap.ldif", ) diff --git a/canaille/oidc/models.py b/canaille/oidc/models.py index a0d551d0..c0fb7630 100644 --- a/canaille/oidc/models.py +++ b/canaille/oidc/models.py @@ -4,7 +4,7 @@ from authlib.oauth2.rfc6749 import AuthorizationCodeMixin from authlib.oauth2.rfc6749 import ClientMixin from authlib.oauth2.rfc6749 import TokenMixin from authlib.oauth2.rfc6749 import util -from canaille.ldap_backend.ldapobject import LDAPObject +from canaille.backends.ldap.ldapobject import LDAPObject class Client(LDAPObject, ClientMixin): diff --git a/demo/docker-compose.yml b/demo/docker-compose.yml index 47438421..0e4834ae 100644 --- a/demo/docker-compose.yml +++ b/demo/docker-compose.yml @@ -10,7 +10,7 @@ services: - ./ldif/memberof-config.ldif:/container/service/slapd/assets/config/bootstrap/ldif/03-memberOf.ldif:ro # 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 + - ../canaille/backends/ldap/schemas/oauth2-openldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/40-oauth2.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 diff --git a/demo/ldap-server.py b/demo/ldap-server.py index eaaa4954..046b2d88 100644 --- a/demo/ldap-server.py +++ b/demo/ldap-server.py @@ -14,7 +14,7 @@ slapd = slapd.Slapd( "cosine.ldif", "nis.ldif", "inetorgperson.ldif", - "../canaille/ldap_backend/schemas/oauth2-openldap.ldif", + "../canaille/backends/ldap/schemas/oauth2-openldap.ldif", "ldif/memberof-config.ldif", ), ) diff --git a/doc/install.rst b/doc/install.rst index 5f47db98..90c6cc98 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -68,8 +68,8 @@ Old fashion: Copy the schemas in your filesystem .. code-block:: bash - test -d /etc/openldap/schema && sudo cp "$CANAILLE_INSTALL_DIR/env/lib/python*/site-packages/canaille/ldap_backend/schemas/*" /etc/openldap/schema - test -d /etc/ldap/schema && sudo cp "$CANAILLE_INSTALL_DIR/env/lib/python*/site-packages/canaille/ldap_backend/schemas/*" /etc/ldap/schema + test -d /etc/openldap/schema && sudo cp "$CANAILLE_INSTALL_DIR/env/lib/python*/site-packages/canaille/backends/ldap/schemas/*" /etc/openldap/schema + test -d /etc/ldap/schema && sudo cp "$CANAILLE_INSTALL_DIR/env/lib/python*/site-packages/canaille/backends/ldap/schemas/*" /etc/ldap/schema sudo service slapd restart New fashion: Use slapadd to add the schemas @@ -80,7 +80,7 @@ Be careful to stop your ldap server before running ``slapadd`` .. code-block:: bash sudo service slapd stop - sudo -u openldap slapadd -n0 -l "$CANAILLE_INSTALL_DIR/env/lib/python*/site-packages/canaille/ldap_backend/schemas/*.ldif" + sudo -u openldap slapadd -n0 -l "$CANAILLE_INSTALL_DIR/env/lib/python*/site-packages/canaille/backends/ldap/schemas/*.ldif" sudo service slapd start Generate the key pair diff --git a/tests/app/test_configuration.py b/tests/app/test_configuration.py index 4f20b370..bef3fe6f 100644 --- a/tests/app/test_configuration.py +++ b/tests/app/test_configuration.py @@ -1,7 +1,5 @@ import os -from unittest import mock -import ldap import pytest from canaille import create_app from canaille.app.configuration import ConfigurationException @@ -9,61 +7,9 @@ from canaille.app.configuration import validate from flask_webtest import TestApp -def test_ldap_connection_no_remote(testclient, configuration): - validate(configuration) - - -def test_ldap_connection_remote(testclient, configuration, slapd_connection): - validate(configuration, validate_remote=True) - - -def test_ldap_connection_remote_ldap_unreachable(testclient, configuration): - configuration["BACKENDS"]["LDAP"]["URI"] = "ldap://invalid-ldap.com" - with pytest.raises( - ConfigurationException, - match=r"Could not connect to the LDAP server", - ): - validate(configuration, validate_remote=True) - - -def test_ldap_connection_remote_ldap_wrong_credentials(testclient, configuration): - configuration["BACKENDS"]["LDAP"]["BIND_PW"] = "invalid-password" - with pytest.raises( - ConfigurationException, - match=r"LDAP authentication failed with user", - ): - validate(configuration, validate_remote=True) - - -def test_ldap_cannot_create_users(testclient, configuration, slapd_connection): - from canaille.core.models import User - - def fake_init(*args, **kwarg): - raise ldap.INSUFFICIENT_ACCESS - - with mock.patch.object(User, "__init__", fake_init): - with pytest.raises( - ConfigurationException, - match=r"cannot create users at", - ): - validate(configuration, validate_remote=True) - - -def test_ldap_cannot_create_groups(testclient, configuration, slapd_connection): - from canaille.core.models import Group - - def fake_init(*args, **kwarg): - raise ldap.INSUFFICIENT_ACCESS - - with mock.patch.object(Group, "__init__", fake_init): - with pytest.raises( - ConfigurationException, - match=r"cannot create groups at", - ): - validate(configuration, validate_remote=True) - - -def test_smtp_connection_remote_smtp_unreachable(testclient, configuration): +def test_smtp_connection_remote_smtp_unreachable( + testclient, slapd_connection, configuration +): configuration["SMTP"]["HOST"] = "smtp://invalid-smtp.com" with pytest.raises( ConfigurationException, @@ -72,7 +18,9 @@ def test_smtp_connection_remote_smtp_unreachable(testclient, configuration): validate(configuration, validate_remote=True) -def test_smtp_connection_remote_smtp_wrong_credentials(testclient, configuration): +def test_smtp_connection_remote_smtp_wrong_credentials( + testclient, slapd_connection, configuration +): configuration["SMTP"]["PASSWORD"] = "invalid-password" with pytest.raises( ConfigurationException, @@ -81,7 +29,9 @@ def test_smtp_connection_remote_smtp_wrong_credentials(testclient, configuration validate(configuration, validate_remote=True) -def test_smtp_connection_remote_smtp_no_credentials(testclient, configuration): +def test_smtp_connection_remote_smtp_no_credentials( + testclient, slapd_connection, configuration +): del configuration["SMTP"]["LOGIN"] del configuration["SMTP"]["PASSWORD"] validate(configuration, validate_remote=True) @@ -97,7 +47,11 @@ def test_smtp_bad_tls(testclient, slapd_connection, smtpd, configuration): @pytest.fixture -def themed_testclient(app, configuration): +def themed_testclient( + app, + configuration, + slapd_connection, +): configuration["TESTING"] = True root = os.path.dirname(os.path.abspath(__file__)) @@ -109,7 +63,7 @@ def themed_testclient(app, configuration): return TestApp(app) -def test_theme(testclient, themed_testclient): +def test_theme(testclient, themed_testclient, slapd_connection): res = testclient.get("/login") res.mustcontain(no="TEST_THEME") @@ -117,7 +71,7 @@ def test_theme(testclient, themed_testclient): res.mustcontain("TEST_THEME") -def test_invalid_theme(configuration): +def test_invalid_theme(configuration, slapd_connection): validate(configuration, validate_remote=False) with pytest.raises( diff --git a/tests/backends/__init__.py b/tests/backends/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/backends/ldap/__init__.py b/tests/backends/ldap/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/app/test_errors.py b/tests/backends/ldap/test_errors.py similarity index 100% rename from tests/app/test_errors.py rename to tests/backends/ldap/test_errors.py diff --git a/tests/ldap/test_utils.py b/tests/backends/ldap/test_utils.py similarity index 74% rename from tests/ldap/test_utils.py rename to tests/backends/ldap/test_utils.py index bc10155e..71c10463 100644 --- a/tests/ldap/test_utils.py +++ b/tests/backends/ldap/test_utils.py @@ -1,14 +1,18 @@ import datetime +from unittest import mock import ldap.dn +import pytest +from canaille.app.configuration import ConfigurationException +from canaille.app.configuration import validate +from canaille.backends.ldap.backend import setup_ldap_models +from canaille.backends.ldap.ldapobject import LDAPObject +from canaille.backends.ldap.ldapobject import python_attrs_to_ldap +from canaille.backends.ldap.utils import ldap_to_python +from canaille.backends.ldap.utils import python_to_ldap +from canaille.backends.ldap.utils import Syntax from canaille.core.models import Group from canaille.core.models import User -from canaille.ldap_backend.backend import setup_ldap_models -from canaille.ldap_backend.ldapobject import LDAPObject -from canaille.ldap_backend.ldapobject import python_attrs_to_ldap -from canaille.ldap_backend.utils import ldap_to_python -from canaille.ldap_backend.utils import python_to_ldap -from canaille.ldap_backend.utils import Syntax def test_object_creation(slapd_connection): @@ -224,3 +228,57 @@ def test_object_class_update(slapd_connection, testclient): user1.delete() user2.delete() + + +def test_ldap_connection_no_remote(testclient, configuration): + validate(configuration) + + +def test_ldap_connection_remote(testclient, configuration, slapd_connection): + validate(configuration, validate_remote=True) + + +def test_ldap_connection_remote_ldap_unreachable(testclient, configuration): + configuration["BACKENDS"]["LDAP"]["URI"] = "ldap://invalid-ldap.com" + with pytest.raises( + ConfigurationException, + match=r"Could not connect to the LDAP server", + ): + validate(configuration, validate_remote=True) + + +def test_ldap_connection_remote_ldap_wrong_credentials(testclient, configuration): + configuration["BACKENDS"]["LDAP"]["BIND_PW"] = "invalid-password" + with pytest.raises( + ConfigurationException, + match=r"LDAP authentication failed with user", + ): + validate(configuration, validate_remote=True) + + +def test_ldap_cannot_create_users(testclient, configuration, slapd_connection): + from canaille.core.models import User + + def fake_init(*args, **kwarg): + raise ldap.INSUFFICIENT_ACCESS + + with mock.patch.object(User, "__init__", fake_init): + with pytest.raises( + ConfigurationException, + match=r"cannot create users at", + ): + validate(configuration, validate_remote=True) + + +def test_ldap_cannot_create_groups(testclient, configuration, slapd_connection): + from canaille.core.models import Group + + def fake_init(*args, **kwarg): + raise ldap.INSUFFICIENT_ACCESS + + with mock.patch.object(Group, "__init__", fake_init): + with pytest.raises( + ConfigurationException, + match=r"cannot create groups at", + ): + validate(configuration, validate_remote=True) diff --git a/tests/conftest.py b/tests/conftest.py index e99d2f8a..6667385a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,9 +2,9 @@ import ldap.ldapobject import pytest import slapd from canaille import create_app +from canaille.backends.ldap.backend import setup_ldap_models from canaille.core.models import Group from canaille.core.models import User -from canaille.ldap_backend.backend import setup_ldap_models from canaille.oidc.installation import setup_ldap_tree from flask import g from flask_webtest import TestApp @@ -52,7 +52,7 @@ def slapd_server(): slapd.init_tree() for ldif in ( "demo/ldif/memberof-config.ldif", - "canaille/ldap_backend/schemas/oauth2-openldap.ldif", + "canaille/backends/ldap/schemas/oauth2-openldap.ldif", "demo/ldif/bootstrap-users-tree.ldif", "demo/ldif/bootstrap-oidc-tree.ldif", ): diff --git a/tests/oidc/commands/test_install.py b/tests/oidc/commands/test_install.py index 18df4553..9761e839 100644 --- a/tests/oidc/commands/test_install.py +++ b/tests/oidc/commands/test_install.py @@ -4,8 +4,8 @@ import ldap import pytest from canaille import create_app from canaille.app.installation import InstallationException +from canaille.backends.ldap.ldapobject import LDAPObject from canaille.commands import cli -from canaille.ldap_backend.ldapobject import LDAPObject from canaille.oidc.installation import setup_schemas from flask_webtest import TestApp from tests.conftest import CustomSlapdObject