From 6b2fe125d425fa1b897b4f39e8dac76b7fbb4645 Mon Sep 17 00:00:00 2001 From: BrianCArnold Date: Thu, 27 Oct 2022 02:20:41 -0400 Subject: [PATCH] Add LDAP attribute mapping env variables. (#344) * Added ability to specify LDAP attribute mapping. Specifically made sure that LDAP auth works the same if the new environment variables aren't set, in order to maintain behavior for users who are already using LDAP if they don't set the new envvars. * Updated env var name to match the name of the parent global. * Updated README.md to include information on new user attribute mapping environment variables. * Added additional environment var options, and updated comment explaining why there are multiple. * I'm not a python programmer, so these were stupid mistakes. Works now. * Addressing lint error. * Finished updating formatting according to black linter. * Shortened comments to fit within line length. * fix whitespace linting Co-authored-by: simon --- README.md | 6 ++- tubearchivist/config/settings.py | 67 +++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6279af2..f012dc2 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,12 @@ You can configure LDAP with the following environment variables: - `TA_LDAP_DISABLE_CERT_CHECK` (ex: `true`) Set to anything besides empty string to disable certificate checking when connecting over LDAPS. - `TA_LDAP_BIND_DN` (ex: `uid=search-user,ou=users,dc=your-server`) DN of the user that is able to perform searches on your LDAP account. - `TA_LDAP_BIND_PASSWORD` (ex: `yoursecretpassword`) Password for the search user. + - `TA_LDAP_USER_ATTR_MAP_USERNAME` (default: `uid`) Bind attribute used to map LDAP user's username + - `TA_LDAP_USER_ATTR_MAP_PERSONALNAME` (default: `givenName`) Bind attribute used to match LDAP user's First Name/Personal Name. + - `TA_LDAP_USER_ATTR_MAP_SURNAME` (default: `sn`) Bind attribute used to match LDAP user's Last Name/Surname. + - `TA_LDAP_USER_ATTR_MAP_EMAIL` (default: `mail`) Bind attribute used to match LDAP user's EMail address - `TA_LDAP_USER_BASE` (ex: `ou=users,dc=your-server`) Search base for user filter. - - `TA_LDAP_USER_FILTER` (ex: `(objectClass=user)`) Filter for valid users. Login usernames are automatically matched using `uid` and does not need to be specified in this filter. + - `TA_LDAP_USER_FILTER` (ex: `(objectClass=user)`) Filter for valid users. Login usernames are matched using the attribute specified in `TA_LDAP_USER_ATTR_MAP_USERNAME` and should not be specified in this filter. When LDAP authentication is enabled, django passwords (e.g. the password defined in TA_PASSWORD), will not allow you to login, only the LDAP server is used. diff --git a/tubearchivist/config/settings.py b/tubearchivist/config/settings.py index 744c8e0..9e5f016 100644 --- a/tubearchivist/config/settings.py +++ b/tubearchivist/config/settings.py @@ -103,20 +103,75 @@ if bool(environ.get("TA_LDAP")): global AUTH_LDAP_BIND_PASSWORD AUTH_LDAP_BIND_PASSWORD = environ.get("TA_LDAP_BIND_PASSWORD") + """ + Since these are new environment variables, taking the opporunity to use + more accurate env names. + Given Names are *_technically_* different from Personal names, as people + who change their names have different given names and personal names, + and they go by personal names. Additionally, "LastName" is actually + incorrect for many cultures, such as Korea, where the + family name comes first, and the personal name comes last. + + But we all know people are going to try to guess at these, so still want + to include names that people will guess, hence using first/last as well. + """ + + # Attribute mapping options + + global AUTH_LDAP_USER_ATTR_MAP_USERNAME + AUTH_LDAP_USER_ATTR_MAP_USERNAME = ( + environ.get("TA_LDAP_USER_ATTR_MAP_USERNAME") + or environ.get("TA_LDAP_USER_ATTR_MAP_UID") + or "uid" + ) + + global AUTH_LDAP_USER_ATTR_MAP_PERSONALNAME + AUTH_LDAP_USER_ATTR_MAP_PERSONALNAME = ( + environ.get("TA_LDAP_USER_ATTR_MAP_PERSONALNAME") + or environ.get("TA_LDAP_USER_ATTR_MAP_FIRSTNAME") + or environ.get("TA_LDAP_USER_ATTR_MAP_GIVENNAME") + or "givenName" + ) + + global AUTH_LDAP_USER_ATTR_MAP_SURNAME + AUTH_LDAP_USER_ATTR_MAP_SURNAME = ( + environ.get("TA_LDAP_USER_ATTR_MAP_SURNAME") + or environ.get("TA_LDAP_USER_ATTR_MAP_LASTNAME") + or environ.get("TA_LDAP_USER_ATTR_MAP_FAMILYNAME") + or "sn" + ) + + global AUTH_LDAP_USER_ATTR_MAP_EMAIL + AUTH_LDAP_USER_ATTR_MAP_EMAIL = ( + environ.get("TA_LDAP_USER_ATTR_MAP_EMAIL") + or environ.get("TA_LDAP_USER_ATTR_MAP_MAIL") + or "mail" + ) + + global AUTH_LDAP_USER_BASE + AUTH_LDAP_USER_BASE = environ.get("TA_LDAP_USER_BASE") + + global AUTH_LDAP_USER_FILTER + AUTH_LDAP_USER_FILTER = environ.get("TA_LDAP_USER_FILTER") + global AUTH_LDAP_USER_SEARCH # pylint: disable=no-member AUTH_LDAP_USER_SEARCH = LDAPSearch( - environ.get("TA_LDAP_USER_BASE"), + AUTH_LDAP_USER_BASE, ldap.SCOPE_SUBTREE, - "(&(uid=%(user)s)" + environ.get("TA_LDAP_USER_FILTER") + ")", + "(&(" + + AUTH_LDAP_USER_ATTR_MAP_USERNAME + + "=%(user)s)" + + AUTH_LDAP_USER_FILTER + + ")", ) global AUTH_LDAP_USER_ATTR_MAP AUTH_LDAP_USER_ATTR_MAP = { - "username": "uid", - "first_name": "givenName", - "last_name": "sn", - "email": "mail", + "username": AUTH_LDAP_USER_ATTR_MAP_USERNAME, + "first_name": AUTH_LDAP_USER_ATTR_MAP_PERSONALNAME, + "last_name": AUTH_LDAP_USER_ATTR_MAP_SURNAME, + "email": AUTH_LDAP_USER_ATTR_MAP_EMAIL, } if bool(environ.get("TA_LDAP_DISABLE_CERT_CHECK")):