From 5872cfaee191b55c60e9bf1682c1c759a067130a Mon Sep 17 00:00:00 2001 From: Julian Rother <julian@jrother.eu> Date: Sun, 9 Jan 2022 18:32:21 +0100 Subject: [PATCH] User BIND permission and rate limit error handling API permissions errors on BIND (i.e. lack of scope "checkpassword") are now reported as insufficientAccessRights (50) and rate limit errors as unwillingToPerform (53). Previously other (80) was used in both cases, which confused some clients (Nextcloud to be precise). --- uffd-ldapd | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/uffd-ldapd b/uffd-ldapd index 587c449..0b64cf4 100755 --- a/uffd-ldapd +++ b/uffd-ldapd @@ -11,7 +11,7 @@ from cachecontrol import CacheControl from cachecontrol.heuristics import ExpiresAfter import ldapserver -from ldapserver.exceptions import LDAPInvalidCredentials +from ldapserver.exceptions import LDAPInvalidCredentials, LDAPInsufficientAccessRights, LDAPUnwillingToPerform from ldapserver.schema import RFC2307BIS_SCHEMA, RFC2798_SCHEMA logger = logging.getLogger(__name__) @@ -96,8 +96,15 @@ class UffdLDAPRequestHandler(ldapserver.LDAPRequestHandler): return True if not dn.is_direct_child_of(self.subschema.DN('ou=users') + self.dn_base) or len(dn[0]) != 1 or dn[0][0].attribute != 'uid': raise LDAPInvalidCredentials() - if self.api.check_password(loginname=dn[0][0].value, password=password): - return True + try: + if self.api.check_password(loginname=dn[0][0].value, password=password): + return True + except requests.exceptions.HTTPError as exc: + if exc.response.status_code == 403: # We don't have "checkpassword" scope + raise LDAPInsufficientAccessRights() from exc + if exc.response.status_code == 429: # Ratelimited + raise LDAPUnwillingToPerform('Too Many Requests') from exc + raise exc raise LDAPInvalidCredentials() supports_sasl_plain = True @@ -105,10 +112,16 @@ class UffdLDAPRequestHandler(ldapserver.LDAPRequestHandler): def do_bind_sasl_plain(self, identity, password, authzid=None): if authzid is not None and identity != authzid: raise LDAPInvalidCredentials() - user = self.api.check_password(loginname=identity, password=password) - if user is None: - raise LDAPInvalidCredentials() - return user + try: + if self.api.check_password(loginname=identity, password=password): + return True + except requests.exceptions.HTTPError as exc: + if exc.response.status_code == 403: # We don't have "checkpassword" scope + raise LDAPInsufficientAccessRights() from exc + if exc.response.status_code == 429: # Ratelimited + raise LDAPUnwillingToPerform('Too Many Requests') from exc + raise exc + raise LDAPInvalidCredentials() def do_search(self, baseobj, scope, filterobj): yield from super().do_search(baseobj, scope, filterobj) -- GitLab