2022-01-11 16:57:58 +00:00
|
|
|
import logging
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
import ldap
|
|
|
|
from canaille.configuration import ConfigurationException
|
|
|
|
from flask import g
|
|
|
|
from flask import render_template
|
|
|
|
from flask import request
|
|
|
|
from flask_babel import gettext as _
|
|
|
|
|
|
|
|
|
2022-05-18 11:44:54 +00:00
|
|
|
def setup_ldap_models(config):
|
2022-01-11 16:57:58 +00:00
|
|
|
from .ldapobject import LDAPObject
|
|
|
|
from ..models import Group
|
|
|
|
from ..models import User
|
|
|
|
|
2022-05-18 11:44:54 +00:00
|
|
|
LDAPObject.root_dn = config["LDAP"]["ROOT_DN"]
|
2022-01-11 16:57:58 +00:00
|
|
|
|
2022-12-15 10:53:51 +00:00
|
|
|
user_base = config["LDAP"]["USER_BASE"].replace(f',{config["LDAP"]["ROOT_DN"]}', "")
|
2022-01-11 16:57:58 +00:00
|
|
|
User.base = user_base
|
2023-03-08 15:25:50 +00:00
|
|
|
User.rdn_attribute = config["LDAP"].get(
|
|
|
|
"USER_ID_ATTRIBUTE", User.DEFAULT_ID_ATTRIBUTE
|
|
|
|
)
|
2023-03-11 18:46:12 +00:00
|
|
|
object_class = config["LDAP"].get("USER_CLASS", User.DEFAULT_OBJECT_CLASS)
|
|
|
|
User.ldap_object_class = (
|
|
|
|
object_class if isinstance(object_class, list) else [object_class]
|
|
|
|
)
|
2022-05-18 11:44:54 +00:00
|
|
|
|
2022-12-13 18:04:33 +00:00
|
|
|
group_base = (
|
2022-12-15 10:59:00 +00:00
|
|
|
config["LDAP"]
|
|
|
|
.get("GROUP_BASE", "")
|
|
|
|
.replace(f',{config["LDAP"]["ROOT_DN"]}', "")
|
2022-12-13 18:04:33 +00:00
|
|
|
)
|
|
|
|
Group.base = group_base or None
|
2023-03-08 15:25:50 +00:00
|
|
|
Group.rdn_attribute = config["LDAP"].get(
|
|
|
|
"GROUP_ID_ATTRIBUTE", Group.DEFAULT_ID_ATTRIBUTE
|
|
|
|
)
|
2023-03-11 18:46:12 +00:00
|
|
|
object_class = config["LDAP"].get("GROUP_CLASS", Group.DEFAULT_OBJECT_CLASS)
|
|
|
|
Group.ldap_object_class = (
|
|
|
|
object_class if isinstance(object_class, list) else [object_class]
|
|
|
|
)
|
2022-01-11 16:57:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def setup_backend(app):
|
2022-12-10 00:11:33 +00:00
|
|
|
try: # pragma: no cover
|
2022-01-11 16:57:58 +00:00
|
|
|
if request.endpoint == "static":
|
|
|
|
return
|
2022-12-13 18:04:33 +00:00
|
|
|
except RuntimeError: # pragma: no cover
|
2022-01-11 16:57:58 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
2023-03-07 13:37:07 +00:00
|
|
|
g.ldap_connection = ldap.initialize(app.config["LDAP"]["URI"])
|
|
|
|
g.ldap_connection.set_option(
|
|
|
|
ldap.OPT_NETWORK_TIMEOUT, app.config["LDAP"].get("TIMEOUT")
|
|
|
|
)
|
|
|
|
g.ldap_connection.simple_bind_s(
|
2022-01-11 16:57:58 +00:00
|
|
|
app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"]
|
|
|
|
)
|
|
|
|
|
|
|
|
except ldap.SERVER_DOWN:
|
|
|
|
message = _("Could not connect to the LDAP server '{uri}'").format(
|
|
|
|
uri=app.config["LDAP"]["URI"]
|
|
|
|
)
|
|
|
|
logging.error(message)
|
|
|
|
return (
|
|
|
|
render_template(
|
|
|
|
"error.html",
|
|
|
|
error=500,
|
|
|
|
icon="database",
|
|
|
|
debug=app.config.get("DEBUG", False),
|
|
|
|
description=message,
|
|
|
|
),
|
|
|
|
500,
|
|
|
|
)
|
|
|
|
|
|
|
|
except ldap.INVALID_CREDENTIALS:
|
|
|
|
message = _("LDAP authentication failed with user '{user}'").format(
|
|
|
|
user=app.config["LDAP"]["BIND_DN"]
|
|
|
|
)
|
|
|
|
logging.error(message)
|
|
|
|
return (
|
|
|
|
render_template(
|
|
|
|
"error.html",
|
|
|
|
error=500,
|
|
|
|
icon="key",
|
|
|
|
debug=app.config.get("DEBUG", False),
|
|
|
|
description=message,
|
|
|
|
),
|
|
|
|
500,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def teardown_backend(app):
|
2023-03-07 19:10:21 +00:00
|
|
|
if g.get("ldap_connection"): # pragma: no branch
|
2023-03-07 13:37:07 +00:00
|
|
|
g.ldap_connection.unbind_s()
|
|
|
|
g.ldap_connection = None
|
2022-01-11 16:57:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def init_backend(app):
|
2022-05-18 11:44:54 +00:00
|
|
|
setup_ldap_models(app.config)
|
2022-01-11 16:57:58 +00:00
|
|
|
|
|
|
|
@app.before_request
|
|
|
|
def before_request():
|
2022-05-08 14:31:17 +00:00
|
|
|
if not app.config["TESTING"]:
|
|
|
|
return setup_backend(app)
|
2022-01-11 16:57:58 +00:00
|
|
|
|
|
|
|
@app.after_request
|
|
|
|
def after_request(response):
|
2022-05-08 14:31:17 +00:00
|
|
|
if not app.config["TESTING"]:
|
|
|
|
teardown_backend(app)
|
2022-01-11 16:57:58 +00:00
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
|
|
def validate_configuration(config):
|
|
|
|
from canaille.models import User, Group
|
|
|
|
|
|
|
|
try:
|
|
|
|
conn = ldap.initialize(config["LDAP"]["URI"])
|
2022-12-27 19:25:59 +00:00
|
|
|
conn.set_option(ldap.OPT_NETWORK_TIMEOUT, config["LDAP"].get("TIMEOUT"))
|
2022-01-11 16:57:58 +00:00
|
|
|
conn.simple_bind_s(config["LDAP"]["BIND_DN"], config["LDAP"]["BIND_PW"])
|
|
|
|
|
|
|
|
except ldap.SERVER_DOWN as exc:
|
|
|
|
raise ConfigurationException(
|
|
|
|
f'Could not connect to the LDAP server \'{config["LDAP"]["URI"]}\''
|
|
|
|
) from exc
|
|
|
|
|
|
|
|
except ldap.INVALID_CREDENTIALS as exc:
|
|
|
|
raise ConfigurationException(
|
|
|
|
f'LDAP authentication failed with user \'{config["LDAP"]["BIND_DN"]}\''
|
|
|
|
) from exc
|
|
|
|
|
|
|
|
try:
|
|
|
|
User.ldap_object_classes(conn)
|
|
|
|
user = User(
|
|
|
|
cn=f"canaille_{uuid.uuid4()}",
|
|
|
|
sn=f"canaille_{uuid.uuid4()}",
|
|
|
|
uid=f"canaille_{uuid.uuid4()}",
|
|
|
|
mail=f"canaille_{uuid.uuid4()}@mydomain.tld",
|
|
|
|
userPassword="{SSHA}fw9DYeF/gHTHuVMepsQzVYAkffGcU8Fz",
|
|
|
|
)
|
|
|
|
user.save(conn)
|
|
|
|
user.delete(conn)
|
|
|
|
|
|
|
|
except ldap.INSUFFICIENT_ACCESS as exc:
|
|
|
|
raise ConfigurationException(
|
|
|
|
f'LDAP user \'{config["LDAP"]["BIND_DN"]}\' cannot create '
|
|
|
|
f'users at \'{config["LDAP"]["USER_BASE"]}\''
|
|
|
|
) from exc
|
|
|
|
|
|
|
|
try:
|
|
|
|
Group.ldap_object_classes(conn)
|
|
|
|
|
|
|
|
user = User(
|
|
|
|
cn=f"canaille_{uuid.uuid4()}",
|
|
|
|
sn=f"canaille_{uuid.uuid4()}",
|
|
|
|
uid=f"canaille_{uuid.uuid4()}",
|
|
|
|
mail=f"canaille_{uuid.uuid4()}@mydomain.tld",
|
|
|
|
userPassword="{SSHA}fw9DYeF/gHTHuVMepsQzVYAkffGcU8Fz",
|
|
|
|
)
|
|
|
|
user.save(conn)
|
|
|
|
|
|
|
|
group = Group(
|
2023-02-05 18:39:52 +00:00
|
|
|
display_name=f"canaille_{uuid.uuid4()}",
|
|
|
|
members=[user],
|
2022-01-11 16:57:58 +00:00
|
|
|
)
|
|
|
|
group.save(conn)
|
|
|
|
group.delete(conn)
|
|
|
|
|
|
|
|
except ldap.INSUFFICIENT_ACCESS as exc:
|
|
|
|
raise ConfigurationException(
|
|
|
|
f'LDAP user \'{config["LDAP"]["BIND_DN"]}\' cannot create '
|
|
|
|
f'groups at \'{config["LDAP"]["GROUP_BASE"]}\''
|
|
|
|
) from exc
|
|
|
|
|
|
|
|
finally:
|
|
|
|
user.delete(conn)
|
|
|
|
|
|
|
|
conn.unbind_s()
|