#535 closed enhancement (fixed)
Group management and LDAP
Reported by: | gvincent | Owned by: | anonymous |
---|---|---|---|
Priority: | normal | Milestone: | 0.9.1 |
Component: | general | Version: | devel |
Severity: | normal | Keywords: | LDAP ACL group |
Cc: | maze@…, techref@…, danbeck-trac@…, myroslav@…, leho@… | Branch: | |
Release Notes: | |||
API Changes: | |||
Internal Changes: |
Description
I haven't found any ticket about it, but I've got a problem with installing properly Trac actually : I would like to connect users via LDAP (more than 2000 users), for an Intranet. My Only problem is that actually the configuration of each new user must be done by hand, and this isn't a valuable solution for my use of Trac.
Is there any plan to implement group management in Trac ACL (and especially by LDAP ;) ?
Attachments (0)
Change History (26)
comment:1 by , 21 years ago
comment:2 by , 21 years ago
Milestone: | 0.8 → 2.0 |
---|
comment:3 by , 21 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
As you might've realized already, this is not a Trac issue.
The best way to accomplish this is indeed to use Apache's LDAP authentication like dju' hinted at.
For Apache LDAP documentation see:
- http://httpd.apache.org/docs-2.0/mod/mod_auth_ldap.html
- http://httpd.apache.org/docs-2.0/mod/mod_ldap.html
For further questions, emailing the MailingList might help you get in touch with other users who've dealt with Trac and LDAP.
comment:4 by , 21 years ago
Milestone: | 2.0 → 0.8 |
---|---|
Resolution: | wontfix |
Status: | closed → reopened |
reopening
http://lists.edgewall.com/archive/trac/msg00349.html I guess the issue here is authenticating groups not useres.
comment:5 by , 20 years ago
Keywords: | acces removed |
---|---|
Milestone: | 0.8 |
Severity: | major → enhancement |
Correct URL is http://lists.edgewall.com/archive/trac/2004-June/000349.html
Anyway, this is an enhancement request, and I don't think it's going to happen for 0.8 (unless someone steps up and provides a good solution.)
comment:6 by , 20 years ago
Version: | 0.7.1 → devel |
---|
Here is a basic patch to support LDAP group within Trac
MODIFIED/CREATED FILES
scripts/tracd
modified version to support LDAP authentication. It's mainly for demonstration purposes.
Be careful: I did not took time to write a secure authentication: LDAP authentication here is based on 'Basic' HTTP authentication. In other words, user password is sent to the server with no encryption: password is Base64 encoded, that is, plain text. You should not use this over an
insecure network, etc. 8')
On my server, I do not use tracd, but Apache2 with mod_ldap and trac. I've updated tracd if you want to give a try with LDAP without the Apache2 / Ldap setup overhead.
trac/core.py
I wrote LDAP authentication so that it can easily adapted to an existing LDAP installation. Therefore, all LDAP parameters are optionally defined in the trac.ini
config file. In order to acces .ini file, perm.py
needs to get a reference on the 'Environment
' instance. I've modified core.py
so that it instanciates a PermissionCache
instance
with the current environment.
trac/db_default.py
I've added the default LDAP value to this file, so that trad-admin creates a new trac.ini file with default LDAP parameters. You need to edit trac.ini file so that it matches your LDAP server configuration
trac/perm.py
PermissionCache
has been modified so that it optionally support LDAP (if and only if ldap_auth
is set to 'true
' in trac.ini
). If LDAP is enabled, PermissionCache
retrieved the LDAP configuration parameters, and instanciates a Ldap connection with the server
trac/ldapgrp.py
Finally, the LDAP authentication class. It performs authentication (it binds the LDAP connection with the supplied user/password of the remote user), and retrieves the list of groups the user belongs to.
NOTES:
- Patch is based on Trac revision r915 (four days old). It won't work with official Trac release (0.7.1), as
perm.py
has been slighty changed.
- Please note that I'm quite a newbie with Python, so there are probably a lot of improvements to be done. Security should be improved to (in order to use challenge mechanism instead of plain text password, etc.) I do not really care on this point since all access is done through HTTPS on our server, and LDAP server is on the same host. If you care about security issues, you should definitely fix up this code.
- I kept the '
@
' sign to distinguish real username from group name. Since 0.7.1, Trac developers have introduced groupnames (which are not different from real username). I prefer to keep the usual syntax for groups; however, if you want to follow Trac rules, it should not be difficult to remove the '@
' stuff
PATCH:
Tested with Linux 2.4.22
, Open LDAP 2.1.30
Index: scripts/tracd =================================================================== --- scripts/tracd (revision 915) +++ scripts/tracd (working copy) @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: iso8859-1 -*- # # Copyright (C) 2003, 2004 Edgewall Software @@ -35,6 +35,7 @@ import urllib import urllib2 import mimetypes +import base64 import SocketServer import BaseHTTPServer @@ -42,6 +43,7 @@ import trac.core import trac.util +import trac.ldapgrp from trac import Session from trac.Href import Href from trac import auth, siteconfig @@ -129,7 +131,40 @@ self.active_nonces.remove(auth['nonce']) return auth['username'] +class LdapAuth: + def __init__(self, hostport, basedn, realm): + host, port = hostport.split(':', 2) + self.host = host + if None != port: + self.port = int(port) + else: + self.port = 389 + self.realm = realm + self.basedn = basedn + def send_auth_request(self, req, stale='false'): + req.send_response(401) + req.send_header('WWW-Authenticate', + 'Basic realm="%s"' % (self.realm)) + req.end_headers() + + def do_auth(self, req): + if not 'Authorization' in req.headers or \ + req.headers['Authorization'][:5] != 'Basic': + self.send_auth_request(req) + return None + auth64 = urllib2.parse_http_list(req.headers['Authorization'][6:])[0] + auth = base64.decodestring(auth64) + username, passwd = auth.split(':', 2) + lc = trac.ldapgrp.Connection(host=self.host, port=self.port, basedn=self.basedn) + rc = lc.open(username,passwd) + lc.close() + if not rc: + self.send_auth_request(req) + return None + return username + + class TracHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): pass @@ -260,6 +295,7 @@ print 'usage: %s [options] <projenv> [projenv] ...' % sys.argv[0] print '\nOptions:\n' print '-a --auth [project],[htdigest_file],[realm]' + print '-l --ldapauth [project],[ldaphost:port],[realm],[basedn]' print '-p --port [port]\t\tPort number to use (default: 80)' print '-b --hostname [hostname]\tIP to bind to (default: \'\')' print @@ -272,8 +308,8 @@ auths = {} projects = {} try: - opts, args = getopt.getopt(sys.argv[1:], "a:p:b:", - ["auth=", "port=", "hostname="]) + opts, args = getopt.getopt(sys.argv[1:], "a:l:p:b", + ["auth=", "ldapauth=", "port=", "hostname="]) except getopt.GetoptError: usage() for o, a in opts: @@ -283,6 +319,12 @@ usage() p, h, r = info auths[p] = DigestAuth(h, r) + if o in ("-l", "--ldapauth"): + info = a.split(',', 3) + if len(info) != 4: + usage() + p, h, r, b = info + auths[p] = LdapAuth(h,b,r) if o in ("-p", "--port"): port = int(a) elif o in ("-b", "--hostname"): Index: trac/core.py =================================================================== --- trac/core.py (revision 915) +++ trac/core.py (working copy) @@ -162,7 +162,7 @@ module.req = req module._name = mode module.db = db - module.perm = perm.PermissionCache(module.db, req.authname) + module.perm = perm.PermissionCache(module.db, module.env, req.authname) module.perm.add_to_hdf(req.hdf) module.authzperm = None Index: trac/db_default.py =================================================================== --- trac/db_default.py (revision 915) +++ trac/db_default.py (working copy) @@ -445,5 +445,13 @@ ('notification', 'smtp_from', 'trac@localhost'), ('notification', 'smtp_replyto', 'trac@localhost'), ('timeline', 'changeset_show_files', 'false'), - ('timeline', 'changeset_files_count', 3)) + ('timeline', 'changeset_files_count', 3), + ('ldapauth', 'ldap_auth', 'false'), + ('ldapauth', 'ldap_host', 'localhost'), + ('ldapauth', 'ldap_port', 389), + ('ldapauth', 'ldap_basedn', 'dc=example,dc=com'), + ('ldapauth', 'ldap_groupname', 'groupofnames'), + ('ldapauth', 'ldap_groupuid', 'cn'), + ('ldapauth', 'ldap_groupmember', 'member'), + ('ldapauth', 'ldap_uid', 'uid')) Index: trac/ldapgrp.py =================================================================== --- trac/ldapgrp.py (revision 0) +++ trac/ldapgrp.py (revision 0) @@ -0,0 +1,98 @@ +# -*- coding: iso8859-1 -*- +# +# Copyright (C) 2003, 2004 Edgewall Software +# Copyright (C) 2003, 2004 Jonas Borgström <jonas@edgewall.com> +# +# Trac is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# Trac is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# Author: Emmanuel Blot <emmanuel.blot@free.fr> + +import ldap +import re + +class Connection: + def __init__(self, **ldap): + self.host = 'localhost' + self.port = 389 + self.basedn = '' + self.groupname = 'groupofnames' + self.groupuid = 'cn' + self.groupmember = 'member' + self.uid = 'uid' + if ldap.has_key('host'): self.host = ldap['host'] + if ldap.has_key('port'): self.port = ldap['port'] + if ldap.has_key('basedn'): self.basedn = ldap['basedn'] + if ldap.has_key('groupname'): self.groupname = ldap['groupname'] + if ldap.has_key('groupuid'): self.groupuid = ldap['groupuid'] + if ldap.has_key('groupmember'): self.groupmember = ldap['groupmember'] + if ldap.has_key('uid'): self.uid = ldap['uid'] + + def basedn(self): + return self.basedn; + + def open(self, uid=None, password=None): + try: + self.__ds = ldap.open(self.host, self.port) + self.__ds.protocol_version = ldap.VERSION3 + if uid is not None: + if ( not uid.startswith('%s=' % self.uid) ): + uid = '%s=%s' % (self.uid, uid) + self.__ds.simple_bind_s(uid + ',' + self.basedn, password) + self.__login = uid + return True + except ldap.LDAPError, e: + return False + + def close(self): + self.__ds.unbind_s() + self.__login = '' + + def isOwner(self, uid): + return self.__login == uid + + def search(self, filter, attributes=None): + try: + sr = self.__ds.search_s(self.basedn, ldap.SCOPE_SUBTREE, filter, attributes) + return sr + + except ldap.LDAPError, e: + return False; + + def compare(self, dn, attribute, value): + try: + cr = self.__ds.compare_s(dn + "," + self.basedn, attribute, value) + return cr + + except ldap.LDAPError, e: + return False + + def enumerate_groups(self): + attributes = ['dn'] + sr = self.search('objectclass=' + self.groupname, attributes) + groups = ['none'] + if sr: + for (dn, attrs) in sr: + regex = re.compile('^(\w+)=(\w+)') + m = regex.search(dn) + if m: + groups.append(m.group(2)) + return groups + + def is_in_group(self, uid, group): + dn = self.groupuid + "=" + group + value = self.uid + "=" + uid + "," + self.basedn + cr = self.compare(dn, self.groupmember, value) + return cr + Index: trac/perm.py =================================================================== --- trac/perm.py (revision 915) +++ trac/perm.py (working copy) @@ -104,7 +104,7 @@ 'authenticated': Permissions granted to this user will apply to any authenticated (logged in with HTTP_AUTH) user. """ - def __init__(self, db, username): + def __init__(self, db, env, username): self.perm_cache = {} cursor = db.cursor() cursor.execute ("SELECT username, action FROM permission") @@ -114,6 +114,26 @@ users = ['anonymous'] if username != 'anonymous': users += [username, 'authenticated'] + + useldap = False + if env.get_config('ldapauth', 'ldap_auth', '') == 'true': + useldap = True + import ldapgrp + if useldap and (username[0] !='@'): + lc = ldapgrp.Connection(host=env.get_config('ldapauth', 'ldap_host', 'localhost'), + port=int(env.get_config('ldapauth', 'ldap_port', '389')), + basedn=env.get_config('ldapauth', 'ldap_basedn', ''), + groupname=env.get_config('ldapauth', 'ldap_groupname', 'groupofnames'), + groupuid=env.get_config('ldapauth', 'ldap_groupuid', 'cn'), + groupmember=env.get_config('ldapauth', 'ldap_groupmember', 'member'), + uid=env.get_config('ldapauth', 'ldap_uid', 'uid')) + if lc.open(): + groups = lc.enumerate_groups() + for group in groups: + if lc.is_in_group(username, group): + users.append('@' + group) + lc.close() + while 1: num_users = len(users) num_perms = len(perms)
comment:7 by , 20 years ago
Updated code for 0.9 pre (1366) is available from here: http://anciens.enib.fr/trac/public/wiki/TracLdap
comment:8 by , 20 years ago
Are there any plans to merge this into the trunk? I'm trying to stay updated with that branch, and I'm no python expert, so handling them myself isn't possible. I humbly beg for this. :)
comment:9 by , 20 years ago
Cc: | added |
---|
comment:10 by , 20 years ago
Cc: | added |
---|
comment:11 by , 19 years ago
Cc: | added |
---|
I'm also interested in a deeper integration of LDAP (or Active Directory) into Trac. All relevant user information (email address, full name) is available there.
comment:12 by , 19 years ago
I've been working on it. I hope to propose a working solution by the end of august or september '05
comment:14 by , 19 years ago
Cc: | added |
---|
comment:15 by , 19 years ago
Cc: | added; removed |
---|
comment:16 by , 19 years ago
I put a very basic implementation for LDAP group here:
http://anciens.enib.fr/trac/public/wiki/TracLdap
comment:17 by , 19 years ago
LdapPlugin, featuring some kind of LDAP ACLs and group management, is now available on Trac Hacks.
comment:18 by , 19 years ago
Cc: | added |
---|
comment:19 by , 19 years ago
Milestone: | → 0.9.1 |
---|---|
Resolution: | → fixed |
Status: | reopened → closed |
Summary: | group management and LDAP → Group management and LDAP |
I'm closing this bug, as LDAP extension is not a piece of the core Trac engine. To request new features, report bugs or participate in the LDAP plugin development, please follow the http://trac-hacks.swapoff.org link.
Closing the bug as 'fixed', as the requested features are now available for 0.9.1 within the current implementation of the LDAP plugin.
comment:20 by , 19 years ago
Cc: | removed |
---|
comment:21 by , 18 years ago
Cc: | removed |
---|
follow-ups: 24 25 comment:22 by , 18 years ago
I think having built-in LDAP authentication for Standalone 'tracd' was a good idea.
comment:23 by , 13 years ago
Cc: | added |
---|
comment:24 by , 13 years ago
Owner: | changed from | to
---|
Replying to anonymous:
I think having built-in LDAP authentication for Standalone 'tracd' was a good idea.
comment:25 by , 13 years ago
Replying to anonymous:
I think having built-in LDAP authentication for Standalone 'tracd' was a good idea.
How does the standalone server use LDAP? I could not find any documentation for this. Also, would the authentication be secure as the documentation says that standalone server uses clear text and does not have https support.
comment:26 by , 13 years ago
there is no LDAP support built into tracd. currently the closest you are going to get is th:LdapLugin. i'm about to retry getting started on #2456 which probably encompasses this as well.
i believe you have to use Apache directives to do this, like <Location /trac/ticket/> […] Require group group1 </Location>.