Attachment 'ldap_group3.patch'
Download 1 diff -r a8a777074233 MoinMoin/auth/ldap_login.py
2 --- a/MoinMoin/auth/ldap_login.py Mon Jul 27 01:55:59 2009 +0200
3 +++ b/MoinMoin/auth/ldap_login.py Thu Jul 30 16:20:22 2009 +0200
4 @@ -41,26 +41,18 @@
5 name = 'ldap'
6
7 def __init__(self,
8 - server_uri='ldap://localhost', # ldap / active directory server URI
9 - # use ldaps://server:636 url for ldaps,
10 - # use ldap://server for ldap without tls (and set start_tls to 0),
11 - # use ldap://server for ldap with tls (and set start_tls to 1 or 2).
12 - bind_dn='', # We can either use some fixed user and password for binding to LDAP.
13 - # Be careful if you need a % char in those strings - as they are used as
14 - # a format string, you have to write %% to get a single % in the end.
15 - #bind_dn = 'binduser@example.org' # (AD)
16 - #bind_dn = 'cn=admin,dc=example,dc=org' # (OpenLDAP)
17 - #bind_pw = 'secret'
18 - # or we can use the username and password we got from the user:
19 - #bind_dn = '%(username)s@example.org' # DN we use for first bind (AD)
20 - #bind_pw = '%(password)s' # password we use for first bind
21 - # or we can bind anonymously (if that is supported by your directory).
22 - # In any case, bind_dn and bind_pw must be defined.
23 - bind_pw='',
24 + conn_mgr, # a LDAPConnectionManager object
25 + bind_dn=None, # You can override the conn_mgr parameters here.
26 + # if you do this you won't be able to share connection with LDAPGroups
27 + # Be careful if you need a % char in those strings - as they are used as
28 + # a format string, you have to write %% to get a single % in the end.
29 + #
30 + # You can use the username and password we got from the user:
31 + # bind_dn = '%(username)s@example.org' # DN we use for first bind (AD)
32 + bind_pw=None, # bind_pw = '%(password)s' # password we use for first bind
33 base_dn='', # base DN we use for searching
34 #base_dn = 'ou=SOMEUNIT,dc=example,dc=org'
35 scope=ldap.SCOPE_SUBTREE, # scope of the search we do (2 == ldap.SCOPE_SUBTREE)
36 - referrals=0, # LDAP REFERRALS (0 needed for AD)
37 search_filter='(uid=%(username)s)', # ldap filter used for searching:
38 #search_filter = '(sAMAccountName=%(username)s)' # (AD)
39 #search_filter = '(uid=%(username)s)' # (OpenLDAP)
40 @@ -73,23 +65,15 @@
41 email_attribute=None, # ('mail') ldap attribute we get the email address from
42 email_callback=None, # called to make up email address
43 name_callback=None, # called to use a Wiki name different from the login name
44 - coding='utf-8', # coding used for ldap queries and result values
45 - timeout=10, # how long we wait for the ldap server [s]
46 - start_tls=0, # 0 = No, 1 = Try, 2 = Required
47 - tls_cacertdir=None,
48 - tls_cacertfile=None,
49 - tls_certfile=None,
50 - tls_keyfile=None,
51 - tls_require_cert=0, # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
52 bind_once=False, # set to True to only do one bind - useful if configured to bind as the user on the first attempt
53 autocreate=False, # set to True if you want to autocreate user profiles
54 ):
55 - self.server_uri = server_uri
56 + self.conn_mgr = conn_mgr
57 +
58 self.bind_dn = bind_dn
59 self.bind_pw = bind_pw
60 self.base_dn = base_dn
61 self.scope = scope
62 - self.referrals = referrals
63 self.search_filter = search_filter
64
65 self.givenname_attribute = givenname_attribute
66 @@ -98,16 +82,6 @@
67 self.email_attribute = email_attribute
68 self.email_callback = email_callback
69 self.name_callback = name_callback
70 -
71 - self.coding = coding
72 - self.timeout = timeout
73 -
74 - self.start_tls = start_tls
75 - self.tls_cacertdir = tls_cacertdir
76 - self.tls_cacertfile = tls_cacertfile
77 - self.tls_certfile = tls_certfile
78 - self.tls_keyfile = tls_keyfile
79 - self.tls_require_cert = tls_require_cert
80
81 self.bind_once = bind_once
82 self.autocreate = autocreate
83 @@ -127,44 +101,14 @@
84 try:
85 u = None
86 dn = None
87 - coding = self.coding
88 - logging.debug("Setting misc. ldap options...")
89 - ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
90 - ldap.set_option(ldap.OPT_REFERRALS, self.referrals)
91 - ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
92 -
93 - if hasattr(ldap, 'TLS_AVAIL') and ldap.TLS_AVAIL:
94 - for option, value in (
95 - (ldap.OPT_X_TLS_CACERTDIR, self.tls_cacertdir),
96 - (ldap.OPT_X_TLS_CACERTFILE, self.tls_cacertfile),
97 - (ldap.OPT_X_TLS_CERTFILE, self.tls_certfile),
98 - (ldap.OPT_X_TLS_KEYFILE, self.tls_keyfile),
99 - (ldap.OPT_X_TLS_REQUIRE_CERT, self.tls_require_cert),
100 - (ldap.OPT_X_TLS, self.start_tls),
101 - #(ldap.OPT_X_TLS_ALLOW, 1),
102 - ):
103 - if value is not None:
104 - ldap.set_option(option, value)
105 -
106 - server = self.server_uri
107 - logging.debug("Trying to initialize %r." % server)
108 - l = ldap.initialize(server)
109 - logging.debug("Connected to LDAP server %r." % server)
110 -
111 - if self.start_tls and server.startswith('ldap:'):
112 - logging.debug("Trying to start TLS to %r." % server)
113 - try:
114 - l.start_tls_s()
115 - logging.debug("Using TLS to %r." % server)
116 - except (ldap.SERVER_DOWN, ldap.CONNECT_ERROR), err:
117 - logging.warning("Couldn't establish TLS to %r (err: %s)." % (server, str(err)))
118 - raise
119 -
120 + coding = self.conn_mgr.coding
121 + binddn = self.bind_dn
122 + bindpw = self.bind_pw
123 # you can use %(username)s and %(password)s here to get the stuff entered in the form:
124 - binddn = self.bind_dn % locals()
125 - bindpw = self.bind_pw % locals()
126 - l.simple_bind_s(binddn.encode(coding), bindpw.encode(coding))
127 - logging.debug("Bound with binddn %r" % binddn)
128 + if binddn is not None:
129 + binddn = binddn % locals()
130 + bindpw = bindpw % locals()
131 + l = self.conn_mgr.get_connection(binddn, bindpw)
132
133 # you can use %(username)s here to get the stuff entered in the form:
134 filterstr = self.search_filter % locals()
135 @@ -175,8 +119,8 @@
136 'surname_attribute',
137 'givenname_attribute',
138 ] if getattr(self, attr) is not None]
139 - lusers = l.search_st(self.base_dn, self.scope, filterstr.encode(coding),
140 - attrlist=attrs, timeout=self.timeout)
141 + lusers = l.search_st(self.base_dn, self.scope,
142 + filterstr.encode(coding), attrlist=attrs)
143 # we remove entries with dn == None to get the real result list:
144 lusers = [(dn, ldap_dict) for dn, ldap_dict in lusers if dn is not None]
145 for dn, ldap_dict in lusers:
146 @@ -195,7 +139,7 @@
147 dn, ldap_dict = lusers[0]
148 if not self.bind_once:
149 logging.debug("DN found is %r, trying to bind with pw" % dn)
150 - l.simple_bind_s(dn, password.encode(coding))
151 + self.conn_mgr.get_connection(dn, password)
152 logging.debug("Bound with dn %r (username: %r)" % (dn, username))
153
154 if self.email_callback is None:
155 @@ -229,6 +173,7 @@
156 else:
157 u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ))
158 u.name = username
159 + u.dn = dn
160 u.aliasname = aliasname
161 u.remember_me = 0 # 0 enforces cookie_lifetime config param
162 logging.debug("creating userprefs with name %r email %r alias %r" % (username, email, aliasname))
163 diff -r a8a777074233 MoinMoin/datastruct/backends/ldap_groups.py
164 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
165 +++ b/MoinMoin/datastruct/backends/ldap_groups.py Thu Jul 30 16:20:22 2009 +0200
166 @@ -0,0 +1,90 @@
167 +# -*- coding: iso-8859-1 -*-
168 +"""
169 + MoinMoin - ldap group lazy backend.
170 +
171 + The ldap group backend allows one to define groups in an
172 + ldap server. Group members must be authenticated via
173 + ldap.
174 +
175 + @copyright: 2009 Benoit Peccatte (peck)
176 + @license: GPL, see COPYING for details
177 +"""
178 +
179 +from MoinMoin import log
180 +logging = log.getLogger(__name__)
181 +
182 +from MoinMoin.user import User
183 +from MoinMoin.datastruct.backends import LazyGroup, LazyGroupsBackend
184 +
185 +import ldap
186 +
187 +class LdapGroup(LazyGroup):
188 + pass
189 +
190 +
191 +class LdapGroups(LazyGroupsBackend):
192 +
193 + def __init__(self, request,
194 + conn_mgr, # LDAPConnectionManager to use for connections
195 + base_dn='', # base ldap dn to use when searching for groups
196 + scope=ldap.SCOPE_SUBTREE, # scope of the search
197 + filter='', # filter to use when searching for groups
198 + member_attribute='member', # attribute name for group members
199 + name_attribute='cn', # attribute name for group name
200 + ):
201 +
202 + super(LdapGroups, self).__init__(request)
203 + self.base = base_dn
204 + self.scope = scope
205 + if filter != '' and not filter.startswith('('):
206 + filter = filter = '(%s)' % filter
207 + self.filter = filter
208 + self.member_attribute = member_attribute
209 + self.name_attribute = name_attribute
210 + self.ldap = conn_mgr
211 +
212 + def __contains__(self, group_name):
213 + filter = self._getfilter(group_name)
214 + res = self.ldap.search_single(self.base, self.scope, filter, 'dn')
215 + return res != []
216 +
217 + def __iter__(self):
218 + filter = self._getfilter('*')
219 + res = self.ldap.search_single(self.base, self.scope, filter, self.name_attribute)
220 + # XXX better group name rebuilding ? needs configurability
221 + res = [ '%sGroup' % x for x in res ]
222 + return res.__iter__()
223 +
224 + def __getitem__(self, group_name):
225 + return LdapGroup(self.request, group_name, self)
226 +
227 + def _iter_group_members(self, group_name):
228 + filter = self._getfilter(group_name)
229 + return self.ldap.search_single( self.base, self.scope, filter, self.member_attribute)
230 +
231 + def _group_has_member(self, group_name, member):
232 + # only check users that are authenticated using ldap
233 + u = User(self.request, auth_username=member)
234 + if not hasattr(u, 'dn'):
235 + return False
236 + filter = self._getfilter(group_name, u.dn)
237 + res = self.ldap.search_single( self.base, self.scope, filter, 'dn')
238 + return res != []
239 +
240 + def _getfilter(self, group_name, member=None):
241 + """ Create a filter string for a group.
242 + Add a member filter if one is provided."""
243 + # extract real group name from the form XxxGroup
244 + grp = self.page_group_regex.search(group_name)
245 + if grp:
246 + group = grp.group(2)
247 + else:
248 + group = group_name
249 +
250 + filter = '(%s=%s)' % (self.name_attribute, group)
251 + if self.filter:
252 + filter = '(&%s%s)' % (self.filter, filter)
253 + if member:
254 + filter = '(&%s(%s=%s))' % (filter, self.member_attribute, member)
255 + return filter
256 +
257 diff -r a8a777074233 MoinMoin/util/ldap_connection.py
258 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
259 +++ b/MoinMoin/util/ldap_connection.py Thu Jul 30 16:20:22 2009 +0200
260 @@ -0,0 +1,131 @@
261 +# -*- coding: iso-8859-1 -*-
262 +"""
263 + MoinMoin - ldap connection object
264 +
265 + The connexion object holds informations to connect to a single
266 + ldap server.
267 +
268 + @copyright: 2006-2009 MoinMoin:ThomasWaldmann,
269 + 2006 Nick Phillips
270 + 2009 Benoit Peccatte (peck)
271 + @license: GPL, see COPYING for details
272 +"""
273 +
274 +from MoinMoin import log
275 +logging = log.getLogger(__name__)
276 +
277 +try:
278 + import ldap
279 +except ImportError, err:
280 + logging.error("You need to have python-ldap installed (%s)." % str(err))
281 + raise
282 +
283 +class LDAPConnectionManager(object):
284 + def __init__(self,
285 + server_uri='ldap://localhost', # ldap / active directory server URI
286 + # use ldaps://server:636 url for ldaps,
287 + # use ldap://server for ldap without tls (and set start_tls to 0),
288 + # use ldap://server for ldap with tls (and set start_tls to 1 or 2).
289 + bind_dn='', # bind_dn = 'binduser@example.org' # (AD)
290 + # bind_dn = 'cn=admin,dc=example,dc=org' # (OpenLDAP)
291 + bind_pw='',
292 + referrals=0, # LDAP REFERRALS (0 needed for AD)
293 + coding='utf-8', # coding used for ldap queries and result values
294 + timeout=10, # how long we wait for the ldap server [s]
295 + start_tls=0, # 0 = No, 1 = Try, 2 = Required
296 + tls_cacertdir=None,
297 + tls_cacertfile=None,
298 + tls_certfile=None,
299 + tls_keyfile=None,
300 + tls_require_cert=0, # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
301 + ):
302 +
303 + self.server_uri = server_uri
304 + self.bind_dn = bind_dn
305 + self.bind_pw = bind_pw
306 + self.referrals = referrals
307 + self.coding = coding
308 + self.timeout = timeout
309 + self.start_tls = start_tls
310 + self.tls_cacertdir = tls_cacertdir
311 + self.tls_cacertfile = tls_cacertfile
312 + self.tls_certfile = tls_certfile
313 + self.tls_keyfile = tls_keyfile
314 + self.tls_require_cert = tls_require_cert
315 +
316 + self.ldap = None
317 +
318 + def get_connection(self, bind_dn=None, bind_pw=None):
319 + """ Create a connection object and bind.
320 + Always return the default connection when bind_dn=None """
321 + default = bind_dn is None
322 + # only one default connection
323 + if default and self.ldap:
324 + return self.ldap
325 + try:
326 + # options
327 + logging.debug("Setting misc. ldap options...")
328 + ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)
329 + ldap.set_option(ldap.OPT_REFERRALS, self.referrals)
330 + ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
331 + ldap.set_option(ldap.OPT_TIMEOUT, self.timeout)
332 + if hasattr(ldap, 'TLS_AVAIL') and ldap.TLS_AVAIL:
333 + for option, value in (
334 + (ldap.OPT_X_TLS_CACERTDIR, self.tls_cacertdir),
335 + (ldap.OPT_X_TLS_CACERTFILE, self.tls_cacertfile),
336 + (ldap.OPT_X_TLS_CERTFILE, self.tls_certfile),
337 + (ldap.OPT_X_TLS_KEYFILE, self.tls_keyfile),
338 + (ldap.OPT_X_TLS_REQUIRE_CERT, self.tls_require_cert),
339 + (ldap.OPT_X_TLS, self.start_tls),
340 + #(ldap.OPT_X_TLS_ALLOW, 1),
341 + ):
342 + if value is not None:
343 + ldap.set_option(option, value)
344 +
345 + # connect and bind
346 + logging.debug("Trying to initialize %r." % self.server_uri)
347 + conn = ldap.initialize(self.server_uri)
348 + conn.protocol_version = ldap.VERSION3 # recommended by documentation
349 + logging.debug("Connected to LDAP server %r." % self.server_uri)
350 + if self.start_tls and server.startswith('ldap:'):
351 + logging.debug("Trying to start TLS to %r." % self.server_uri)
352 + try:
353 + conn.start_tls_s()
354 + logging.debug("Using TLS to %r." % self.server_uri)
355 + except (ldap.SERVER_DOWN, ldap.CONNECT_ERROR), err:
356 + logging.warning("Couldn't establish TLS to %r (err: %s)." % (self.server_uri, str(err)))
357 + raise
358 +
359 + if default:
360 + bind_dn = self.bind_dn
361 + bind_pw = self.bind_pw
362 + conn.simple_bind_s(bind_dn.encode(self.coding), bind_pw.encode(self.coding))
363 + logging.debug("Bound with binddn %r" % bind_dn)
364 + if default:
365 + self.ldap=conn
366 + return conn
367 + except ldap.LDAPError, error_message:
368 + logging.error("LDAP error %r" % error_message)
369 + raise
370 +
371 + def search_single(self, base, scope, filter, attribute):
372 + """ Make a search with the default connection
373 + Search for a single attribute, returns an array of single values. """
374 + conn = self.get_connection()
375 + try:
376 + result_id = conn.search(base.encode(self.coding),
377 + scope,
378 + filter.encode(self.coding),
379 + [ attribute ])
380 + result_type, data = conn.result(result_id, self.timeout)
381 + if result_type != ldap.RES_SEARCH_RESULT:
382 + logging.warning("LDAP result : unknown result_type")
383 + return []
384 + if(attribute == 'dn'):
385 + return [x[0] for x in data]
386 + else:
387 + return [x[1][attribute][0] for x in data]
388 + except ldap.LDAPError, error_message:
389 + logging.error("LDAP search error %s" % error_message)
390 + return []
391 +
392
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.