mirror of
https://github.com/Kozea/Radicale.git
synced 2025-06-26 16:45:52 +00:00
Implementing the evalutaion of indirect group membership.
Now member or uniqueMember can also be used for the group membership.
This commit is contained in:
parent
8318feacce
commit
e3b5a6040b
3 changed files with 64 additions and 34 deletions
7
config
7
config
|
@ -92,8 +92,11 @@
|
|||
# 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 to read the group memberships. This can be memberOf from the user's LDAP entry. member or uniqueMember can also be used. In this case an additional ldap search will be executed to find the groups where the user is member of.
|
||||
#ldap_groups_attribute =
|
||||
|
||||
# The base dn to find the groups. Will be used only if ldap_groups_attribute is member or uniqueMember. If not given ldap_base will be used.
|
||||
#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}))
|
||||
|
|
|
@ -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,9 @@ 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_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:
|
||||
|
@ -130,8 +134,8 @@ class Auth(auth.BaseAuth):
|
|||
else:
|
||||
logger.info("auth.ldap_ssl_ca_file : (not provided)")
|
||||
"""Extend attributes to to be returned in the user query"""
|
||||
if self._ldap_groups_attr:
|
||||
self._ldap_attributes.append(self._ldap_groups_attr)
|
||||
if self._ldap_groups_attr == "memberOf":
|
||||
self._ldap_attributes.append("memberOf")
|
||||
if self._ldap_user_attr:
|
||||
self._ldap_attributes.append(self._ldap_user_attr)
|
||||
logger.info("ldap_attributes : %r" % self._ldap_attributes)
|
||||
|
@ -172,17 +176,25 @@ class Auth(auth.BaseAuth):
|
|||
conn.set_option(self.ldap.OPT_REFERRALS, 0)
|
||||
conn.simple_bind_s(user_dn, password)
|
||||
tmp: 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: list[str] = []
|
||||
if self._ldap_groups_attr == "memberOf":
|
||||
gdns = user_entry[1][self._ldap_groups_attr]
|
||||
elif self._ldap_groups_attr == "member" or self._ldap_groups_attr == "uniqueMember":
|
||||
res = conn.search_s(
|
||||
self._ldap_groups_base,
|
||||
self.ldap.SCOPE_SUBTREE,
|
||||
filterstr="({0}={1})".format(self._ldap_groups_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]
|
||||
|
@ -226,7 +238,7 @@ class Auth(auth.BaseAuth):
|
|||
except Exception as e:
|
||||
logger.debug(f"_login3 error 1 {e} (reader)")
|
||||
pass
|
||||
if not conn.bind():
|
||||
if not conn.bind(read_server_info=False):
|
||||
logger.debug("_login3 cannot bind (reader)")
|
||||
raise RuntimeError("Unable to read from LDAP server")
|
||||
logger.debug(f"_login3 bind as {self._ldap_reader_dn}")
|
||||
|
@ -257,19 +269,30 @@ class Auth(auth.BaseAuth):
|
|||
conn.start_tls()
|
||||
except self.ldap3.core.exceptions.LDAPStartTLSError as e:
|
||||
raise RuntimeError(f"_login3 StartTLS Error: {e}")
|
||||
if not conn.bind():
|
||||
if not conn.bind(read_server_info=False):
|
||||
logger.debug(f"_login3 user '{login}' cannot be found")
|
||||
return ""
|
||||
tmp: list[str] = []
|
||||
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)
|
||||
gdns: list[str] = []
|
||||
"""Let's collect the groups of the user."""
|
||||
if self._ldap_groups_attr == "memberOf":
|
||||
gdns = user_entry['attributes']['memberOf']
|
||||
elif self._ldap_groups_attr == "member" or self._ldap_groups_attr == "uniqueMember":
|
||||
conn.search(
|
||||
search_base=self._ldap_groups_base,
|
||||
search_filter="({0}={1})".format(self._ldap_groups_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:
|
||||
|
|
|
@ -269,31 +269,35 @@ 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 to read the group memberships from. Valid values are memberOf, member or uniqueMember. If no value is given group memebership will be ignored.",
|
||||
"type": str}),
|
||||
("ldap_groups_base_dn", {
|
||||
"value": "",
|
||||
"help": "The base dn to find the groups. Necessary only if ldap_groups attribute is member or uniqueMember. If not given ldap_base will be used.",
|
||||
"type": str}),
|
||||
("ldap_use_ssl", {
|
||||
"value": "False",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue