Attachment 'ldap_group4.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	Mon Aug 03 11:13:21 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 +                conn = 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 = conn.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	Mon Aug 03 11:13:21 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 +        search_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 search_filter and not search_filter.startswith('('):
 206 +            search_filter = '(%s)' % search_filter
 207 +        self.filter = search_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	Mon Aug 03 11:13:21 2009 +0200
 260 @@ -0,0 +1,133 @@
 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, ldap_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 +                                    ldap_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 +                # data is an array of pair with dn as the first element
 386 +                return [x[0] for x in data]
 387 +            else:
 388 +                # the 2nd elt is a hash of attributes containing an array of values
 389 +                return [x[1][attribute][0] for x in data]
 390 +        except ldap.LDAPError, error_message:
 391 +            logging.error("LDAP search error %s" % error_message)
 392 +        return []
 393 +
 394 

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.
  • [get | view] (2009-07-29 18:30:46, 9.9 KB) [[attachment:ldap_group2.patch]]
  • [get | view] (2009-07-30 14:21:47, 17.9 KB) [[attachment:ldap_group3.patch]]
  • [get | view] (2009-08-03 09:47:19, 18.0 KB) [[attachment:ldap_group4.patch]]
  • [get | view] (2009-07-29 13:42:43, 9.6 KB) [[attachment:ldap_groups.patch]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.