Roundup Wiki

The following example is used to login to Roundup via an LDAP authentication process. User profiles are also stored in Roundup database, to minimize code modifications. Also :

In your 'extensions' directory, create a file 'ldap.py' with::

   1     import ldap
   2     from roundup.cgi.actions import LoginAction
   3     from roundup.i18n import _
   4 
   5     class LdapLoginAction(LoginAction):
   6 
   7         ldap_attrs = (
   8             ( 'realname', ['cn'] ),
   9             ( 'username', ['uid'] ),
  10             )
  11         ldap_server = 'ldaps://example.com'
  12         ldap_base = 'dc=example, dc=com'
  13         email_suffix = '@example.com'
  14 
  15         def verifyLocalPassword(self, password):
  16         ''' Verify the password that the user has supplied '''
  17         stored = self.db.user.get(self.client.userid, 'password')
  18         if password == stored:
  19             return 1
  20         if not password and not stored:
  21             return 1
  22         return 0
  23 
  24         def local_login (self, password):
  25         ''' Local authentication '''
  26         # make sure the user exists
  27         try:
  28             self.client.userid = self.db.user.lookup(self.client.user)
  29         except KeyError:
  30             self.client.error_message.append(_('Unknown user "%s"')%self.client.user)
  31             return 0
  32         # verify the password
  33         if not self.verifyLocalPassword(password):
  34             self.client.error_message.append(_('Invalid password'))
  35             return 0
  36         return 1
  37 
  38         def ldap_login (self, password):
  39         ''' Authentication via LDAP '''
  40         # connect to LDAP host
  41         ldapcn = ldap.initialize(self.ldap_server)
  42         # make sure that user exists
  43         try:
  44             ldaps = ldapcn.search_s(self.ldap_base, ldap.SCOPE_SUBTREE,'uid=%s'%self.client.user)
  45             self.ldapdn,self.attrs = ldaps[0][0],ldaps[0][1]
  46         except:
  47             name = self.client.user
  48             self.client.error_message.append (_('Unknown LDAP account "%(name)s"')%locals())
  49             return 0
  50         # verify the password
  51         try:
  52             ldapcn.bind_s (self.ldapdn, password)
  53         except:
  54             self.client.error_message.append (_('Invalid password !'))
  55             return 0
  56         return 1
  57 
  58         def verifyLogin(self, username, password):
  59         # try to login throught LDAP or with local account
  60         ldap_ok = None
  61         if not self.local_login(password):
  62             ldap_ok = self.ldap_login(password)
  63             if not ldap_ok:
  64                 self.client.make_user_anonymous ()
  65                 return
  66         self.client.error_message = []
  67         # reload user profile, or create it automatically if missing
  68         try:
  69             self.client.userid = self.db.user.lookup(self.client.user)
  70         except:
  71             if ldap_ok:
  72                 props = {}
  73                 for user_attr,ldap_attr in self.ldap_attrs:
  74                     props[user_attr] = ' '.join([self.attrs.get (attr,['',''])[0] for attr in ldap_attr])
  75                 props['address'] =  self.attrs['uid'][0]+self.email_suffix
  76                 self.journaltag = 'admin'
  77                 cl = self.db.user
  78                 props['roles'] = self.db.config.NEW_WEB_USER_ROLES
  79                 self.userid = cl.create (**props)
  80                 self.db.commit ()
  81                 self.client.userid = self.db.user.lookup(self.client.user)
  82             else:
  83                 self.client.make_user_anonymous()
  84                 self.client.error_message.append(_("No account created without LDAP account"))
  85                 return
  86 
  87 
  88     def init(instance):
  89         instance.registerAction('login', LdapLoginAction)

As said before, you could easily forbid actions such as allowing authentication throught local Roundup profile, or auto-creation of user's profile. It's also possible, via LDAP, to check that the logged in user is member of a given group, or has a given attribute with a given value. The only thing which have to be done here is complete the "ldap_login" method, and return 0 and an error message when invalid conditions are encountered.

**From: Denis Shaposhnikov**

**Date: Sat, 5 Jan 2008 21:37:56 +0300**

I want to share my expirience with configuring Roundup to use LDAP for authentication. I've found that the LDAPLogin (see above) is not exactly what I need. It creates new Roundup user after successfull LDAP authentication but use empty password. And it checks local password first. So, after first LDAP authentication anyone can login with empty password.

OK, I've done some modifications and you can find 'ldaplogin.py' below. Just configure it and put to 'extenstions' directory. This module can do local authentication too but it disabled by default, see above for the reason. My module do the next steps for authentication:

1. Try to bind with user's DN (without search).

2. Check that user is a member of configured group.

3. If no Roundup's user in the database, create it.

Here the 'ldaplogin.py' module::

   1     """Authentication an user via LDAP."""
   2 
   3     import ldap
   4     from roundup import password as PW
   5     from roundup.cgi import exceptions
   6     from roundup.cgi.actions import LoginAction
   7     from roundup.i18n import _
   8 
   9     class LDAPLoginAction(LoginAction):
  10         """Do LDAP authentication.
  11 
  12         Do LDAP (and may be local) authentication and create new roundup
  13         user if it is not exists. If property `ldap_req_group` have any
  14         value the user should be a memeber of that LDAP group.
  15 
  16         Set `use_local_auth` if you want to use local authentication and
  17         LDAP next. If not, it authenticates by LDAP only.
  18         """
  19 
  20         ldap_attrs = (
  21             ('address', 'mail'),
  22             ('organisation', 'o'),
  23             ('realname', 'cn'),
  24             ('phone', 'telephoneNumber'),
  25             ('username', 'uid'))
  26         ldap_dn = 'uid=%s,ou=people,dc=example,dc=com'
  27         ldap_group_attr = 'memberUid'
  28         ldap_req_group = 'cn=Roundup Users,ou=groups,dc=example,dc=com'
  29         ldap_server = 'ldap://127.0.0.1/'
  30 
  31         use_local_auth = False              # LDAP only
  32 
  33         def local_login (self, password):
  34             """Try local authentication."""
  35             # make sure the user exists
  36             try:
  37                 self.client.userid = self.db.user.lookup(self.client.user)
  38             except KeyError:
  39                 self.client.error_message.append(
  40                     _('Unknown user "%s"') % self.client.user)
  41                 return 0
  42 
  43             # verify the password
  44             if not self.verifyPassword(self.client.userid, password):
  45                 self.client.error_message.append(_('Invalid password'))
  46                 return 0
  47 
  48             # Determine whether the user has permission to log in.
  49             # Base behaviour is to check the user has "Web Access".
  50             if not self.hasPermission("Web Access"):
  51                 raise exceptions.LoginError, self._(
  52                     "You do not have permission to login")
  53 
  54             return 1
  55 
  56         def ldap_login(self, password):
  57             """Try LDAP authentication."""
  58             ldapconn = ldap.initialize(self.ldap_server)
  59             # verify the password
  60             try:
  61                 ldapconn.bind_s(self.ldap_dn % self.client.user, password)
  62             except:
  63                 self.client.error_message.append (_('Invalid password'))
  64                 return 0
  65 
  66             if self.ldap_req_group:
  67                 # verify the group membership
  68                 member = None
  69                 try:
  70                     member = ldapconn.compare_s(
  71                         self.ldap_req_group, self.ldap_group_attr, self.client.user)
  72                 except:
  73                     member = None
  74                 if not member:
  75                     self.client.error_message.append (
  76                         _("You do not have permission to login"))
  77                     return 0
  78 
  79             return 1
  80 
  81         def verifyLogin(self, username, password):
  82             # try to login throught LDAP or with local account
  83             ldap_ok = None
  84             if self.use_local_auth:
  85                 if not self.local_login(password):
  86                     ldap_ok = self.ldap_login(password)
  87                     if not ldap_ok:
  88                         raise exceptions.LoginError
  89             else:
  90                 ldap_ok = self.ldap_login(password)
  91                 if not ldap_ok:
  92                     raise exceptions.LoginError
  93             self.client.error_message = []
  94             # reload user profile, or create it automatically if missing
  95             try:
  96                 self.client.userid = self.db.user.lookup(self.client.user)
  97             except:
  98                 if ldap_ok:
  99                     ldapconn = ldap.initialize(self.ldap_server)
 100                     ldaps = ldapconn.search_s(
 101                         self.ldap_dn % self.client.user, ldap.SCOPE_BASE)
 102                     attrs = ldaps[0][1]
 103                     props = {}
 104                     for user_attr,ldap_attr in self.ldap_attrs:
 105                         props[user_attr] = attrs.get(ldap_attr,('',))[0]
 106                     props['password'] = PW.Password(password)
 107                     self.journaltag = 'admin'
 108                     cl = self.db.user
 109                     props['roles'] = self.db.config.NEW_WEB_USER_ROLES
 110                     self.userid = cl.create(**props)
 111                     self.db.commit()
 112                     self.client.userid = self.db.user.lookup(self.client.user)
 113                 else:
 114                     raise exceptions.LoginError, _(
 115                         "No account created without LDAP account")
 116 
 117     def init(instance):
 118         instance.registerAction('login', LDAPLoginAction)