mirror of
https://github.com/Kozea/Radicale.git
synced 2025-06-26 16:45:52 +00:00
Merge 5b20813bd7
into 348b123b43
This commit is contained in:
commit
d216a9a2f6
12 changed files with 70 additions and 39 deletions
10
config
10
config
|
@ -93,8 +93,14 @@
|
|||
# Path of the file containing password of the reader DN
|
||||
#ldap_secret_file = /run/secrets/ldap_password
|
||||
|
||||
# the attribute to read the group memberships from in the user's LDAP entry (default: not set)
|
||||
#ldap_groups_attribute = memberOf
|
||||
# The attribute in user entry to read the group memberships.
|
||||
#ldap_groups_attribute =
|
||||
|
||||
# The attribute in group entries to read the group memberships.
|
||||
#ldap_group_members_attribute =
|
||||
|
||||
# The base dn to find the groups. Necessary only if ldap_group_members_attribute is defined and other then ldap_base.
|
||||
#ldap_groups_base =
|
||||
|
||||
# The filter to find the DN of the user. This filter must contain a python-style placeholder for the login
|
||||
#ldap_filter = (&(objectClass=person)(uid={0}))
|
||||
|
|
|
@ -27,7 +27,6 @@ from http import client
|
|||
from typing import Dict, Optional, cast
|
||||
|
||||
import defusedxml.ElementTree as DefusedET
|
||||
|
||||
import radicale.item as radicale_item
|
||||
from radicale import httputils, storage, types, xmlutils
|
||||
from radicale.app.base import Access, ApplicationBase
|
||||
|
|
|
@ -60,7 +60,6 @@ import time
|
|||
from typing import Any, Tuple
|
||||
|
||||
from passlib.hash import apr_md5_crypt, sha256_crypt, sha512_crypt
|
||||
|
||||
from radicale import auth, config, logger
|
||||
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@
|
|||
Authentication backend that checks credentials with a LDAP server.
|
||||
Following parameters are needed in the configuration:
|
||||
ldap_uri The LDAP URL to the server like ldap://localhost
|
||||
ldap_base The baseDN of the LDAP server
|
||||
ldap_base The baseDN of the LDAP server searching for users.
|
||||
ldap_reader_dn The DN of a LDAP user with read access to get the user accounts
|
||||
ldap_secret The password of the ldap_reader_dn
|
||||
ldap_secret_file The path of the file containing the password of the ldap_reader_dn
|
||||
ldap_filter The search filter to find the user to authenticate by the username
|
||||
ldap_user_attribute The attribute to be used as username after authentication
|
||||
ldap_groups_attribute The attribute containing group memberships in the LDAP user entry
|
||||
ldap_groups_base The baseDN of the LDAP server searching for groups.
|
||||
Following parameters controls SSL connections:
|
||||
ldap_use_ssl If ssl encryption should be used (to be deprecated)
|
||||
ldap_security The encryption mode to be used: *none*|tls|starttls
|
||||
|
@ -78,6 +79,10 @@ class Auth(auth.BaseAuth):
|
|||
self._ldap_filter = configuration.get("auth", "ldap_filter")
|
||||
self._ldap_user_attr = configuration.get("auth", "ldap_user_attribute")
|
||||
self._ldap_groups_attr = configuration.get("auth", "ldap_groups_attribute")
|
||||
self._ldap_group_members_attr = configuration.get("auth", "ldap_group_members_attribute")
|
||||
self._ldap_groups_base = configuration.get("auth", "ldap_groups_base")
|
||||
if self._ldap_groups_base == "":
|
||||
self._ldap_groups_base = self._ldap_base
|
||||
ldap_secret_file_path = configuration.get("auth", "ldap_secret_file")
|
||||
if ldap_secret_file_path:
|
||||
with open(ldap_secret_file_path, 'r') as file:
|
||||
|
@ -172,17 +177,25 @@ class Auth(auth.BaseAuth):
|
|||
conn.set_option(self.ldap.OPT_REFERRALS, 0)
|
||||
conn.simple_bind_s(user_dn, password)
|
||||
tmp: list[str] = []
|
||||
gdns: list[str] = []
|
||||
if self._ldap_groups_attr:
|
||||
tmp = []
|
||||
for g in user_entry[1][self._ldap_groups_attr]:
|
||||
"""Get group g's RDN's attribute value"""
|
||||
try:
|
||||
rdns = self.ldap.dn.explode_dn(g, notypes=True)
|
||||
tmp.append(rdns[0])
|
||||
except Exception:
|
||||
tmp.append(g.decode('utf8'))
|
||||
self._ldap_groups = set(tmp)
|
||||
logger.debug("_login2 LDAP groups of user: %s", ",".join(self._ldap_groups))
|
||||
gdns = user_entry[1][self._ldap_groups_attr]
|
||||
elif self._ldap_group_members_attr:
|
||||
res = conn.search_s(
|
||||
self._ldap_groups_base,
|
||||
self.ldap.SCOPE_SUBTREE,
|
||||
filterstr="({0}={1})".format(self._ldap_group_members_attr,user_dn),
|
||||
attrlist=self._ldap_attributes
|
||||
)
|
||||
for g in gdns:
|
||||
"""Get group g's RDN's attribute value"""
|
||||
try:
|
||||
rdns = self.ldap.dn.explode_dn(g, notypes=True)
|
||||
tmp.append(rdns[0])
|
||||
except Exception:
|
||||
tmp.append(g.decode('utf8'))
|
||||
self._ldap_groups = set(tmp)
|
||||
logger.debug("_login2 LDAP groups of user: %s", ",".join(self._ldap_groups))
|
||||
if self._ldap_user_attr:
|
||||
if user_entry[1][self._ldap_user_attr]:
|
||||
tmplogin = user_entry[1][self._ldap_user_attr][0]
|
||||
|
@ -261,17 +274,28 @@ class Auth(auth.BaseAuth):
|
|||
logger.debug(f"_login3 user '{login}' cannot be found")
|
||||
return ""
|
||||
tmp: list[str] = []
|
||||
gdns: list[str] = []
|
||||
"""Let's collect the groups of the user."""
|
||||
if self._ldap_groups_attr:
|
||||
tmp = []
|
||||
for g in user_entry['attributes'][self._ldap_groups_attr]:
|
||||
"""Get group g's RDN's attribute value"""
|
||||
try:
|
||||
rdns = self.ldap3.utils.dn.parse_dn(g)
|
||||
tmp.append(rdns[0][1])
|
||||
except Exception:
|
||||
tmp.append(g)
|
||||
self._ldap_groups = set(tmp)
|
||||
logger.debug("_login3 LDAP groups of user: %s", ",".join(self._ldap_groups))
|
||||
gdns = user_entry['attributes'][self._ldap_groups_attr]
|
||||
elif self._ldap_group_members_attr:
|
||||
conn.search(
|
||||
search_base=self._ldap_groups_base,
|
||||
search_filter="({0}={1})".format(self._ldap_group_members_attr,user_dn),
|
||||
search_scope=self.ldap3.SUBTREE,
|
||||
attributes="dn"
|
||||
)
|
||||
for group in conn.response:
|
||||
gdns.append(group['dn'])
|
||||
for g in gdns:
|
||||
"""Get group g's RDN's attribute value"""
|
||||
try:
|
||||
rdns = self.ldap3.utils.dn.parse_dn(g)
|
||||
tmp.append(rdns[0][1])
|
||||
except Exception:
|
||||
tmp.append(g)
|
||||
self._ldap_groups = set(tmp)
|
||||
logger.debug("_login3 LDAP groups of user: %s", ",".join(self._ldap_groups))
|
||||
if self._ldap_user_attr:
|
||||
if user_entry['attributes'][self._ldap_user_attr]:
|
||||
if isinstance(user_entry['attributes'][self._ldap_user_attr], list):
|
||||
|
|
|
@ -269,31 +269,39 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
|
|||
"type": str}),
|
||||
("ldap_base", {
|
||||
"value": "",
|
||||
"help": "LDAP base DN of the ldap server",
|
||||
"help": "The base DN of the ldap server where the user can be find.",
|
||||
"type": str}),
|
||||
("ldap_reader_dn", {
|
||||
"value": "",
|
||||
"help": "the DN of a ldap user with read access to get the user accounts",
|
||||
"help": "The DN of a ldap user with read access to get the user accounts",
|
||||
"type": str}),
|
||||
("ldap_secret", {
|
||||
"value": "",
|
||||
"help": "the password of the ldap_reader_dn",
|
||||
"help": "The password of the ldap_reader_dn",
|
||||
"type": str}),
|
||||
("ldap_secret_file", {
|
||||
"value": "",
|
||||
"help": "path of the file containing the password of the ldap_reader_dn",
|
||||
"help": "Path of the file containing the password of the ldap_reader_dn",
|
||||
"type": str}),
|
||||
("ldap_filter", {
|
||||
"value": "(cn={0})",
|
||||
"help": "the search filter to find the user DN to authenticate by the username",
|
||||
"help": "The search filter to find the user DN to authenticate by the username",
|
||||
"type": str}),
|
||||
("ldap_user_attribute", {
|
||||
"value": "",
|
||||
"help": "the attribute to be used as username after authentication",
|
||||
"help": "The attribute to be used as username after authentication",
|
||||
"type": str}),
|
||||
("ldap_groups_attribute", {
|
||||
"value": "",
|
||||
"help": "attribute to read the group memberships from",
|
||||
"help": "Attribute in the user entry to read the group memberships from.",
|
||||
"type": str}),
|
||||
("ldap_group_members_attribute", {
|
||||
"value": "",
|
||||
"help": "Attribute in the group entries to read the group memberships from.",
|
||||
"type": str}),
|
||||
("ldap_groups_base_dn", {
|
||||
"value": "",
|
||||
"help": "The base dn to find the groups. Necessary only if ldap_group_members_attribute is defined and other then ldap_base.",
|
||||
"type": str}),
|
||||
("ldap_use_ssl", {
|
||||
"value": "False",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import pika
|
||||
from pika.exceptions import ChannelWrongStateError, StreamLostError
|
||||
|
||||
from radicale import hook
|
||||
from radicale.hook import HookNotificationItem
|
||||
from radicale.log import logger
|
||||
|
|
|
@ -31,9 +31,9 @@ from io import BytesIO
|
|||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from urllib.parse import quote
|
||||
|
||||
import defusedxml.ElementTree as DefusedET
|
||||
import vobject
|
||||
|
||||
import defusedxml.ElementTree as DefusedET
|
||||
import radicale
|
||||
from radicale import app, config, types, xmlutils
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import sys
|
|||
from typing import Iterable, Tuple, Union
|
||||
|
||||
import pytest
|
||||
|
||||
from radicale import xmlutils
|
||||
from radicale.tests import BaseTest
|
||||
|
||||
|
|
|
@ -26,9 +26,9 @@ import os
|
|||
import posixpath
|
||||
from typing import Any, Callable, ClassVar, Iterable, List, Optional, Tuple
|
||||
|
||||
import defusedxml.ElementTree as DefusedET
|
||||
import vobject
|
||||
|
||||
import defusedxml.ElementTree as DefusedET
|
||||
from radicale import storage, xmlutils
|
||||
from radicale.tests import RESPONSES, BaseTest
|
||||
from radicale.tests.helpers import get_file_content
|
||||
|
|
|
@ -21,7 +21,6 @@ from configparser import RawConfigParser
|
|||
from typing import List, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from radicale import config, types
|
||||
from radicale.tests.helpers import configuration_to_dict
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ from urllib import request
|
|||
from urllib.error import HTTPError, URLError
|
||||
|
||||
import pytest
|
||||
|
||||
from radicale import config, server
|
||||
from radicale.tests import BaseTest
|
||||
from radicale.tests.helpers import configuration_to_dict, get_file_path
|
||||
|
|
|
@ -26,7 +26,6 @@ import shutil
|
|||
from typing import ClassVar, cast
|
||||
|
||||
import pytest
|
||||
|
||||
import radicale.tests.custom.storage_simple_sync
|
||||
from radicale.tests import BaseTest
|
||||
from radicale.tests.helpers import get_file_content
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue