Python's ldap module, which is modelled along OpenLDAP's API, allows us to
keep the connection and doing a new bind as a different user, superseding
the previous bind.
Use this to simplify the code and avoid duplication.
Until now, every connection to the LDAP server was silently unencryptedr
when using Python's ldap module instead of the ldap3 module.
I.e. using Python's ldap module was inherently insecure, as there was not
even a hint that the config settings for encryption were ignored.
This commit changes this and brings LDAP authentication based on the ldap
module feature-wise on par with the one based on the ldap3 module.
LDAP URIs starting with the scheme 'ldaps' are - by definition - meant to use
LDAPS instead of plain LDAP: infer 'ldap_security' = "tls" if it is not set.
* stop treating it as class property
* refactor to consolidate logic into one big 'if' statement
(for easier removal when the config option gets removed in the future)
* make deprecation warning for 'ldap_use_ssl' more urgent
* raise error if conflicting settings 'ldap_security' = "starttls" and
'ldap_use_ssl' = True are set together
* if not set, infer 'ldap_security' = "tls" from 'ldap_use_ssl' = True,
logging a warning for the admin to update the config
Currently it is not used by _login2(), but it does not hurt to have it
available.
It is a preparation for supporting encrypted connections in _login2().
The evaluation of the quirk for the Authentik LDAP server changes the behaviour
of Python's `ldap3` module, and that module only.
Evaluating the quirk in `__init__` which is used for both, `ldap` and `ldap3`
is thus wrong, and may lead to errors when this setting is used together with
the `ldap` module.
Signed-off-by: Peter Marschall <peter@adpm.de>
Instead of searching for the membership attribute on the user side
(usually AD: memberOf, Novell eDirectory: groupMembership) to determine
the groups the user loging on is a member of, allow performing a separate
search for the groups having the user as member and use the found groups' DNs.
The group search is performed in the context of 'ldap_reader_dn', after
the user DN has been found in the directory, but before the authentication
has been performed by doing an LDAP bind in the user's context.
Although this may - in the case of unsuccessful login attempts -
double the number of queries to the LDAP server, it has been done
this way to keep the number of LDAP contexts minimal.
Doing the group search in the context of the user logging on is no viable
option, because there are known implementations where regular users do not
have the necessary permissions to query the groups they are a member in.
This commit modifies `_login3` to check if the attribute value is a
list and, if so, extracts the first element (`[0]`) as the login
identifier. If the value is not a list, it's used directly (fallback).
Bugfix, user_entry['attributes'][self._ldap_user_attr] is already the string so user_entry['attributes'][self._ldap_user_attr][0] would give only the first character and not the full user attribute
Use helper methods from the LDAP modules to get individual elements
(like in our case the RDN value) out of attributes with DN syntax
in a standard compliant way instead fiddling around ourselves.
If these methods fail, fall back to using the whole attribute value,
which allows us to also use attributes with non-DN syntax for groups
and permissions.
The same effect can be achieved using the option 'ldap_groups_attribute' alone,
if it's default becomes unset instead of 'memberOf'
Benefit: one config option less to deal with.
While at it, also fix header level for 'ldap_user_attribute' in documentation.
This attribute is supposed to hold the group membership information
if the config option 'ldap_load_groups' is True.
If not given, it defaults to 'memberOf' for Active Directory.
Introducing this options allows one to use radicale's LDAP auth with groups
even on LDAP servers that keep their group memberships in a different attribute
than 'memberOf', e.g. Novell eDirectory which uses 'groupMembership'.
Ask for the 'memberOf' attribute to be returned in the user query only
if 'ldap_load_groups' is set to True.
This fixes the issue that currently LDAP authentication can only be used on
LDAP servers that know this non-standard (it's an Active Directory extension)
attribute.
Other LDAP servers either do not necessarily have the group memberships
stored in the user object (e.g. OpenLDAP), or use different attributes for
this purpose (e.g. Novell eDirectory uses 'groupMembership')
This option gives us
- flexible authentication options where the name used for logging on
does not have to be the account name
e.g. use ldap_filter = (&(obhjectclass=inetOrgperson)(|(cn={0]})(mail={0})))
to allow loginng on using the cn or the mail address
- automatically consistent / canonicalized username values
(i.e. exactly the way the LDAP server returns them)
This makes sure not fail securely when the query returns multiple entries
- correct grammar in some cases
- we're doing _authentication here, not authorization
- uppercase LDAP in messages & comments
- rename variable _ldap_version to _ldap_module_version
to avoid misunderstanding it as LDAP's protocol version
- align formatting & messages better between _login2() and _login3()
- correct grammar in some cases
- we're doing authentication here, not authorization
- uppercase LDAP in messages & comments
- rename variable _ldap_version to _ldap_module_version
to avoid misunderstanding it as LDAP's protocol version