Skip to content

Commit 07efd8b

Browse files
committed
Merge pull request #697 from dlewen/backend-add-ad-ldap-auth
Add Active Directory LDAP Auth support
2 parents cb4f45f + 8f50f8f commit 07efd8b

File tree

2 files changed

+75
-5
lines changed

2 files changed

+75
-5
lines changed

nipap/nipap.conf.dist

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,40 @@ db_path = /etc/nipap/local_auth.db ; path to SQLite database used
110110
#
111111
#basedn = dc=test,dc=com ; base DN
112112
#uri = ldaps://ldap.test.com ; LDAP server URI
113+
#
114+
# LDAP style
115+
#binddn_fmt = uid={},dc=test,dc=com
116+
#search = uid={} ; LDAP search filter
117+
#
118+
# Active Directory (UPN) style
119+
#binddn_fmt = {}@test.com
120+
#search = sAMAccountName={}
121+
#
122+
# {} in binddn_fmt is replaced by the username of the authenticating user.
123+
#
124+
## Group permissions
125+
# Non-empty values for rw_group/ro_group requires the memberOf attribute to be
126+
# present in LDAP.
127+
#
128+
# Examples:
129+
#
130+
# Everyone with an account can login and gets read/write access:
131+
#rw_group =
132+
#ro_group =
133+
#
134+
# Users in rw_group gets read/write access, everyone else gets read only access:
135+
#rw_group = cn=tech,dc=test,dc=com ; Users in this group get rw access
136+
#ro_group =
137+
#
138+
# Users in rw_group gets read/write access, users in ro_group get read
139+
# only access. You need to be in either group to authenticate at all:
140+
#rw_group = cn=tech,dc=test,dc=com ; Users in this group get rw access
141+
#ro_group = cn=staff,dc=test,dc=com ; Users in this group get ro access
142+
#
143+
# Users get read/write access by default, users in ro_group gets read
144+
# only access:
145+
#rw_group =
146+
#ro_group = cn=untrusted,dc=test,dc=com ; Users in this group get ro access
113147

114148
#
115149
# Options for the WWW UI

nipap/nipap/authlib.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ class LdapAuth(BaseAuth):
273273

274274
_ldap_uri = None
275275
_ldap_basedn = None
276+
_ldap_binddn_fmt = None
277+
_ldap_search = None
278+
_ldap_rw_group = None
279+
_ldap_ro_group = None
276280
_ldap_conn = None
277281
_authenticated = None
278282

@@ -303,6 +307,10 @@ def __init__(self, name, username, password, authoritative_source, auth_options=
303307
BaseAuth.__init__(self, username, password, authoritative_source, name, auth_options)
304308
self._ldap_uri = self._cfg.get('auth.backends.' + self.auth_backend, 'uri')
305309
self._ldap_basedn = self._cfg.get('auth.backends.' + self.auth_backend, 'basedn')
310+
self._ldap_binddn_fmt = self._cfg.get('auth.backends.' + self.auth_backend, 'binddn_fmt')
311+
self._ldap_search = self._cfg.get('auth.backends.' + self.auth_backend, 'search')
312+
self._ldap_ro_group = self._cfg.get('auth.backends.' + self.auth_backend, 'ro_group')
313+
self._ldap_rw_group = self._cfg.get('auth.backends.' + self.auth_backend, 'rw_group')
306314

307315
self._logger.debug('Creating LdapAuth instance')
308316

@@ -323,7 +331,7 @@ def authenticate(self):
323331
return self._authenticated
324332

325333
try:
326-
self._ldap_conn.simple_bind_s('uid=' + self.username + ',' + self._ldap_basedn, self.password)
334+
self._ldap_conn.simple_bind_s(self._ldap_binddn_fmt.format(ldap.dn.escape_dn_chars(self.username)), self.password)
327335
except ldap.SERVER_DOWN as exc:
328336
raise AuthError('Could not connect to LDAP server')
329337
except (ldap.INVALID_CREDENTIALS, ldap.INVALID_DN_SYNTAX,
@@ -336,17 +344,45 @@ def authenticate(self):
336344

337345
# auth succeeded
338346
self.authenticated_as = self.username
339-
self._authenticated = True
340347
self.trusted = False
341348
self.readonly = False
342349

343350
try:
344-
res = self._ldap_conn.search_s(self._ldap_basedn, ldap.SCOPE_SUBTREE, 'uid=' + self.username, ['cn'])
351+
res = self._ldap_conn.search_s(self._ldap_basedn, ldap.SCOPE_SUBTREE, self._ldap_search.format(ldap.dn.escape_dn_chars(self.username)), ['cn','memberOf'])
345352
self.full_name = res[0][1]['cn'][0]
346-
except:
353+
# check for ro_group membership if ro_group is configured
354+
if self._ldap_ro_group:
355+
if self._ldap_ro_group in res[0][1]['memberOf']:
356+
self.readonly = True
357+
# check for rw_group membership if rw_group is configured
358+
if self._ldap_rw_group:
359+
if self._ldap_rw_group in res[0][1]['memberOf']:
360+
self.readonly = False
361+
else:
362+
# if ro_group is configured, and the user is a member of
363+
# neither the ro_group nor the rw_group, fail authentication.
364+
if self._ldap_ro_group:
365+
if self._ldap_ro_group not in res[0][1]['memberOf']:
366+
self._authenticated = False
367+
return self._authenticated
368+
else:
369+
self.readonly = True
370+
371+
except (ldap.NO_SUCH_OBJECT,ldap.OPERATIONS_ERROR,ldap.FILTER_ERROR,ldap.INVALID_DN_SYNTAX,ldap.SERVER_DOWN) as exc:
372+
raise AuthError(exc)
373+
except KeyError:
374+
raise AuthError('LDAP attribute missing')
375+
except IndexError:
347376
self.full_name = ''
377+
# authentication fails if either ro_group or rw_group are configured
378+
# and the user is not found.
379+
if self._ldap_rw_group or self._ldap_ro_group:
380+
self._authenticated = False
381+
return self._authenticated
348382

349-
self._logger.debug('successfully authenticated as %s, username %s' % (self.authenticated_as, self.username))
383+
self._authenticated = True
384+
385+
self._logger.debug('successfully authenticated as %s, username %s, full_name %s, readonly %s' % (self.authenticated_as, self.username, self.full_name, str(self.readonly)))
350386
return self._authenticated
351387

352388

0 commit comments

Comments
 (0)