From 13cca11f5143b142aebb2fab8a6a07657541acee Mon Sep 17 00:00:00 2001 From: kakwa Date: Sun, 24 May 2015 17:32:03 +0200 Subject: [PATCH 01/22] adding skeleton of method search user --- ldapcherry/backend/backendLdap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index b7e411f..19575a9 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -64,6 +64,9 @@ class Backend(ldapcherry.backend.Backend): def del_user(self, username): pass + def search(self, search_string): + pass + def get_user(self, username, attrs=True): if attrs: a = self.attrlist From 6af8628d5dcb36ae06c01f2308a8c4791b8de0c7 Mon Sep 17 00:00:00 2001 From: kakwa Date: Mon, 25 May 2015 18:52:14 +0200 Subject: [PATCH 02/22] adding search template --- conf/ldapcherry.ini | 3 ++- ldapcherry/backend/backendLdap.py | 2 ++ tests/cfg/ldapcherry.ini | 1 + tests/test_BackendLdap.py | 23 ++++++++++++----------- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/conf/ldapcherry.ini b/conf/ldapcherry.ini index b2a00f9..3a8de08 100644 --- a/conf/ldapcherry.ini +++ b/conf/ldapcherry.ini @@ -76,7 +76,8 @@ ldap.ca = '/etc/dnscherry/TEST-cacert.pem' ldap.starttls = 'on' ldap.checkcert = 'off' ldap.user_filter_tmpl = '(uid=%(username)s)' -ldap.group_filter_tmpl = '(member=%(userdn)s)' +ldap.group_filter_tmpl = '(member=%(username)s)' +ldap.search_filter_tmpl = '&(uid=%(searchstring)s*)(sn=%(searchstring)s*)' ldap.timeout = 1 diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 19575a9..5bc928e 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -26,6 +26,8 @@ class Backend(ldapcherry.backend.Backend): self.userdn = self.get_param('userdn') self.groupdn = self.get_param('groupdn') self.user_filter_tmpl = self.get_param('user_filter_tmpl') + self.group_filter_tmpl = self.get_param('group_filter_tmpl') + self.search_filter_tmpl = self.get_param('search_filter_tmpl') self.attrlist = [] for a in attrslist: try: diff --git a/tests/cfg/ldapcherry.ini b/tests/cfg/ldapcherry.ini index f157b26..d7f1b4d 100644 --- a/tests/cfg/ldapcherry.ini +++ b/tests/cfg/ldapcherry.ini @@ -77,6 +77,7 @@ ldap.starttls = 'on' ldap.checkcert = 'off' ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(userdn)s)' +ldap.search_filter_tmpl = '&(uid=%(searchstring)s*)(sn=%(searchstring)s*)' ldap.timeout = 1 ad.module = 'ldapcherry.backend.backendSamba4' diff --git a/tests/test_BackendLdap.py b/tests/test_BackendLdap.py index f85c9e5..426a58e 100644 --- a/tests/test_BackendLdap.py +++ b/tests/test_BackendLdap.py @@ -14,17 +14,18 @@ import cherrypy from ldap import SERVER_DOWN cfg = { -'module' : 'ldapcherry.backend.ldap', -'groupdn' : 'ou=group,dc=example,dc=org', -'userdn' : 'ou=People,dc=example,dc=org', -'binddn' : 'cn=dnscherry,dc=example,dc=org', -'password' : 'password', -'uri' : 'ldap://ldap.ldapcherry.org:390', -'ca' : './tests/test_env/etc/ldapcherry/TEST-cacert.pem', -'starttls' : 'off', -'checkcert' : 'off', -'user_filter_tmpl' : '(uid=%(username)s)', -'group_filter_tmpl' : '(member=%(userdn)s)', +'module' : 'ldapcherry.backend.ldap', +'groupdn' : 'ou=group,dc=example,dc=org', +'userdn' : 'ou=People,dc=example,dc=org', +'binddn' : 'cn=dnscherry,dc=example,dc=org', +'password' : 'password', +'uri' : 'ldap://ldap.ldapcherry.org:390', +'ca' : './tests/test_env/etc/ldapcherry/TEST-cacert.pem', +'starttls' : 'off', +'checkcert' : 'off', +'user_filter_tmpl' : '(uid=%(username)s)', +'group_filter_tmpl' : '(member=%(userdn)s)', +'search_filter_tmpl' : '&(uid=%(searchstring)s*)(sn=%(searchstring)s*)', } cherrypy.log.error = syslog_error From 7a7d6f5f6f06623461246f511c9c718b4853fb98 Mon Sep 17 00:00:00 2001 From: kakwa Date: Mon, 25 May 2015 19:30:41 +0200 Subject: [PATCH 03/22] implementing search users * adding search * adding unit tests --- conf/ldapcherry.ini | 2 +- ldapcherry/backend/backendLdap.py | 37 ++++++++++++++++++++++++++-- tests/cfg/ldapcherry.ini | 2 +- tests/test_BackendLdap.py | 8 +++++- tests/test_env/deploy.sh | 2 +- tests/test_env/etc/ldap/content.ldif | 12 +++++++++ 6 files changed, 57 insertions(+), 6 deletions(-) diff --git a/conf/ldapcherry.ini b/conf/ldapcherry.ini index 3a8de08..b412497 100644 --- a/conf/ldapcherry.ini +++ b/conf/ldapcherry.ini @@ -77,7 +77,7 @@ ldap.starttls = 'on' ldap.checkcert = 'off' ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(username)s)' -ldap.search_filter_tmpl = '&(uid=%(searchstring)s*)(sn=%(searchstring)s*)' +ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' ldap.timeout = 1 diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 5bc928e..2874fcc 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -66,8 +66,41 @@ class Backend(ldapcherry.backend.Backend): def del_user(self, username): pass - def search(self, search_string): - pass + def search(self, searchstring): + ldap_client = self._connect() + try: + ldap_client.simple_bind_s(self.binddn, self.bindpassword) + except ldap.INVALID_CREDENTIALS as e: + self._logger( + logging.ERROR, + "Configuration error, wrong credentials, unable to connect to ldap with '" + self.binddn + "'", + ) + #raise cherrypy.HTTPError("500", "Configuration Error, contact administrator") + raise e + except ldap.SERVER_DOWN as e: + self._logger( + logging.ERROR, + "Unable to contact ldap server '" + self.uri + "', check 'auth.ldap.uri' and ssl/tls configuration", + ) + raise e + + user_filter = self.search_filter_tmpl % { + 'searchstring': searchstring + } + print user_filter + try: + r = ldap_client.search_s(self.userdn, + ldap.SCOPE_SUBTREE, + user_filter, + attrlist=None + ) + except ldap.FILTER_ERROR as e: + #self._logger( + # logging.ERROR, + # "Bad search filter, check '" + self.backend_name + ".search_filter_tmpl'", + # ) + raise e + return r def get_user(self, username, attrs=True): if attrs: diff --git a/tests/cfg/ldapcherry.ini b/tests/cfg/ldapcherry.ini index d7f1b4d..b15b8f5 100644 --- a/tests/cfg/ldapcherry.ini +++ b/tests/cfg/ldapcherry.ini @@ -77,7 +77,7 @@ ldap.starttls = 'on' ldap.checkcert = 'off' ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(userdn)s)' -ldap.search_filter_tmpl = '&(uid=%(searchstring)s*)(sn=%(searchstring)s*)' +ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' ldap.timeout = 1 ad.module = 'ldapcherry.backend.backendSamba4' diff --git a/tests/test_BackendLdap.py b/tests/test_BackendLdap.py index 426a58e..0fc4c72 100644 --- a/tests/test_BackendLdap.py +++ b/tests/test_BackendLdap.py @@ -25,7 +25,7 @@ cfg = { 'checkcert' : 'off', 'user_filter_tmpl' : '(uid=%(username)s)', 'group_filter_tmpl' : '(member=%(userdn)s)', -'search_filter_tmpl' : '&(uid=%(searchstring)s*)(sn=%(searchstring)s*)', +'search_filter_tmpl' : '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))', } cherrypy.log.error = syslog_error @@ -114,3 +114,9 @@ class TestError(object): ret = inv.get_user('jwatson') expected = ('cn=John Watson,ou=People,dc=example,dc=org', {'uid': ['jwatson'], 'cn': ['John Watson'], 'sn': ['watson']}) assert ret == expected + + def testSearchtUser(self): + inv = Backend(cfg, cherrypy.log, 'ldap', attr) + ret = inv.search('smith') + expected = [('cn=Sheri Smith,ou=People,dc=example,dc=org', {'uid': ['ssmith'], 'objectClass': ['inetOrgPerson'], 'carLicense': ['HERCAR 125'], 'sn': ['smith'], 'mail': ['s.smith@example.com', 'ssmith@example.com', 'sheri.smith@example.com'], 'homePhone': ['555-111-2225'], 'cn': ['Sheri Smith']}), ('cn=John Smith,ou=People,dc=example,dc=org', {'uid': ['jsmith'], 'objectClass': ['inetOrgPerson'], 'carLicense': ['HISCAR 125'], 'sn': ['Smith'], 'mail': ['j.smith@example.com', 'jsmith@example.com', 'jsmith.smith@example.com'], 'homePhone': ['555-111-2225'], 'cn': ['John Smith']})] + assert ret == expected diff --git a/tests/test_env/deploy.sh b/tests/test_env/deploy.sh index 25afd32..7cfe77f 100755 --- a/tests/test_env/deploy.sh +++ b/tests/test_env/deploy.sh @@ -17,7 +17,7 @@ sudo sed -i "s%tools.staticdir.dir.*%tools.staticdir.dir = '`pwd`/resources/stat chown -R openldap:openldap /etc/ldap/ rm /etc/ldap/slapd.d/cn\=config/*mdb* /etc/init.d/slapd restart -ldapadd -H ldap://localhost:390 -x -D "cn=admin,dc=example,dc=org" -f /etc/ldap/content.ldif -w password +ldapadd -c -H ldap://localhost:390 -x -D "cn=admin,dc=example,dc=org" -f /etc/ldap/content.ldif -w password sed -i "s/\(127.0.0.1.*\)/\1 ldap.ldapcherry.org ad.ldapcherry.org/" /etc/hosts diff --git a/tests/test_env/etc/ldap/content.ldif b/tests/test_env/etc/ldap/content.ldif index 5108ed1..98a0682 100644 --- a/tests/test_env/etc/ldap/content.ldif +++ b/tests/test_env/etc/ldap/content.ldif @@ -32,6 +32,18 @@ mail: s.smith@example.com mail: ssmith@example.com mail: sheri.smith@example.com +dn: cn=John Smith,ou=people,dc=example,dc=org +objectclass: inetOrgPerson +cn: John Smith +sn: Smith +uid: jsmith +userpassword: passwordsmith +carlicense: HISCAR 125 +homephone: 555-111-2225 +mail: j.smith@example.com +mail: jsmith@example.com +mail: jsmith.smith@example.com + dn: cn=John Watson,ou=people,dc=example,dc=org objectclass: inetOrgPerson cn: John Watson From 8da0b7c533ff3fc27aed49a5a8c9886e7a33f084 Mon Sep 17 00:00:00 2001 From: kakwa Date: Mon, 25 May 2015 19:52:54 +0200 Subject: [PATCH 04/22] code factoring --- ldapcherry/backend/backendLdap.py | 58 ++++++++++++------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 2874fcc..b9a8579 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -66,7 +66,7 @@ class Backend(ldapcherry.backend.Backend): def del_user(self, username): pass - def search(self, searchstring): + def _search(self, searchfilter, attrs): ldap_client = self._connect() try: ldap_client.simple_bind_s(self.binddn, self.bindpassword) @@ -75,69 +75,57 @@ class Backend(ldapcherry.backend.Backend): logging.ERROR, "Configuration error, wrong credentials, unable to connect to ldap with '" + self.binddn + "'", ) - #raise cherrypy.HTTPError("500", "Configuration Error, contact administrator") + ldap_client.unbind_s() raise e except ldap.SERVER_DOWN as e: self._logger( logging.ERROR, "Unable to contact ldap server '" + self.uri + "', check 'auth.ldap.uri' and ssl/tls configuration", ) + ldap_client.unbind_s() raise e - user_filter = self.search_filter_tmpl % { - 'searchstring': searchstring - } - print user_filter try: r = ldap_client.search_s(self.userdn, ldap.SCOPE_SUBTREE, - user_filter, - attrlist=None + searchfilter, + attrlist=attrs ) except ldap.FILTER_ERROR as e: - #self._logger( - # logging.ERROR, - # "Bad search filter, check '" + self.backend_name + ".search_filter_tmpl'", - # ) + self._logger( + logging.ERROR, + "Bad search filter, check '" + self.backend_name + ".*_filter_tmpl' params", + ) + ldap_client.unbind_s() raise e + + ldap_client.unbind_s() return r + + def search(self, searchstring): + + searchfilter = self.search_filter_tmpl % { + 'searchstring': searchstring + } + + return self._search(searchfilter, None) + def get_user(self, username, attrs=True): if attrs: a = self.attrlist else: a = None - ldap_client = self._connect() - try: - ldap_client.simple_bind_s(self.binddn, self.bindpassword) - except ldap.INVALID_CREDENTIALS as e: - self._logger( - logging.ERROR, - "Configuration error, wrong credentials, unable to connect to ldap with '" + self.binddn + "'", - ) - #raise cherrypy.HTTPError("500", "Configuration Error, contact administrator") - raise e - except ldap.SERVER_DOWN as e: - self._logger( - logging.ERROR, - "Unable to contact ldap server '" + self.uri + "', check 'auth.ldap.uri' and ssl/tls configuration", - ) - raise e user_filter = self.user_filter_tmpl % { 'username': username } - r = ldap_client.search_s(self.userdn, - ldap.SCOPE_SUBTREE, - user_filter, - attrlist=a - ) + r = self._search(user_filter, a) + if len(r) == 0: - ldap_client.unbind_s() return None - ldap_client.unbind_s() if attrs: dn_entry = r[0] else: From 4d29706eaf6cac36156812938dfe6d713db86dc3 Mon Sep 17 00:00:00 2001 From: kakwa Date: Mon, 25 May 2015 19:55:18 +0200 Subject: [PATCH 05/22] better code example --- misc/debug_ldapbackend.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/misc/debug_ldapbackend.py b/misc/debug_ldapbackend.py index 047beea..ec09b83 100644 --- a/misc/debug_ldapbackend.py +++ b/misc/debug_ldapbackend.py @@ -14,17 +14,19 @@ import cherrypy from ldap import SERVER_DOWN cfg = { -'module' : 'ldapcherry.backend.ldap', -'groupdn' : 'ou=group,dc=example,dc=org', -'userdn' : 'ou=People,dc=example,dc=org', -'binddn' : 'cn=dnscherry,dc=example,dc=org', -'password' : 'password', -'uri' : 'ldap://ldap.ldapcherry.org:390', -'ca' : './tests/test_env/etc/ldapcherry/TEST-cacert.pem', -'starttls' : 'off', -'checkcert' : 'off', -'user_filter_tmpl' : '(uid=%(username)s)', -'group_filter_tmpl' : '(member=%(userdn)s)', +'module' : 'ldapcherry.backend.ldap', +'groupdn' : 'ou=group,dc=example,dc=org', +'userdn' : 'ou=People,dc=example,dc=org', +'binddn' : 'cn=dnscherry,dc=example,dc=org', +'password' : 'password', +'uri' : 'ldap://ldap.ldapcherry.org:390', +'ca' : './tests/test_env/etc/ldapcherry/TEST-cacert.pem', +'starttls' : 'off', +'checkcert' : 'off', +'user_filter_tmpl' : '(uid=%(username)s)', +'group_filter_tmpl' : '(member=%(userdn)s)', +'search_filter_tmpl' : '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))', + } cherrypy.log.error = syslog_error @@ -32,3 +34,4 @@ attr = ['shéll', 'cn', 'uid', 'uidNumber', 'gidNumber', 'home', 'userPassword', inv = Backend(cfg, cherrypy.log, 'ldap', attr) print inv.get_user('jwatson') +print inv.search('smit') From 16c757bdd46ba20c5b86899e668426c7d385ce0d Mon Sep 17 00:00:00 2001 From: kakwa Date: Mon, 25 May 2015 22:17:17 +0200 Subject: [PATCH 06/22] factoring binding to the ldap --- ldapcherry/backend/backendLdap.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index b9a8579..5c17f92 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -66,7 +66,7 @@ class Backend(ldapcherry.backend.Backend): def del_user(self, username): pass - def _search(self, searchfilter, attrs): + def _bind(self): ldap_client = self._connect() try: ldap_client.simple_bind_s(self.binddn, self.bindpassword) @@ -84,7 +84,11 @@ class Backend(ldapcherry.backend.Backend): ) ldap_client.unbind_s() raise e + return ldap_client + + def _search(self, searchfilter, attrs): + ldap_client = self._bind() try: r = ldap_client.search_s(self.userdn, ldap.SCOPE_SUBTREE, From 0f6e0c7cabc9c4cc3a20af500b730f2c051e7a89 Mon Sep 17 00:00:00 2001 From: kakwa Date: Mon, 25 May 2015 22:53:34 +0200 Subject: [PATCH 07/22] adding objectclasses options --- conf/ldapcherry.ini | 1 + ldapcherry/backend/backendLdap.py | 1 + tests/cfg/ldapcherry.ini | 1 + tests/test_BackendLdap.py | 1 + 4 files changed, 4 insertions(+) diff --git a/conf/ldapcherry.ini b/conf/ldapcherry.ini index b412497..4415cf5 100644 --- a/conf/ldapcherry.ini +++ b/conf/ldapcherry.ini @@ -78,6 +78,7 @@ ldap.checkcert = 'off' ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(username)s)' ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' +ldap.objectclasses = 'top, person, organizationalPerson, user' ldap.timeout = 1 diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 5c17f92..94a89a4 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -28,6 +28,7 @@ class Backend(ldapcherry.backend.Backend): self.user_filter_tmpl = self.get_param('user_filter_tmpl') self.group_filter_tmpl = self.get_param('group_filter_tmpl') self.search_filter_tmpl = self.get_param('search_filter_tmpl') + self.objectclasses = self.get_param('objectclasses') self.attrlist = [] for a in attrslist: try: diff --git a/tests/cfg/ldapcherry.ini b/tests/cfg/ldapcherry.ini index b15b8f5..18ff3d7 100644 --- a/tests/cfg/ldapcherry.ini +++ b/tests/cfg/ldapcherry.ini @@ -78,6 +78,7 @@ ldap.checkcert = 'off' ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(userdn)s)' ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' +ldap.objectclasses = 'top, person, organizationalPerson, user' ldap.timeout = 1 ad.module = 'ldapcherry.backend.backendSamba4' diff --git a/tests/test_BackendLdap.py b/tests/test_BackendLdap.py index 0fc4c72..7333b96 100644 --- a/tests/test_BackendLdap.py +++ b/tests/test_BackendLdap.py @@ -26,6 +26,7 @@ cfg = { 'user_filter_tmpl' : '(uid=%(username)s)', 'group_filter_tmpl' : '(member=%(userdn)s)', 'search_filter_tmpl' : '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))', +'objectclasses' : 'top, person, organizationalPerson, user', } cherrypy.log.error = syslog_error From a1c7a529d81a47ac073b97ef604970f988e2fc90 Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 00:33:36 +0200 Subject: [PATCH 08/22] adding methods add_user and del_user * adding add_user * adding del_user * adding unit tests * adding configuration parameters for adding users --- conf/ldapcherry.ini | 3 +- ldapcherry/backend/backendLdap.py | 64 +++++++++++++++-- tests/cfg/ldapcherry.ini | 1 + tests/test_BackendLdap.py | 70 +++++++++++++++++-- .../slapd.d/cn=config/olcDatabase={1}hdb.ldif | 2 +- 5 files changed, 125 insertions(+), 15 deletions(-) diff --git a/conf/ldapcherry.ini b/conf/ldapcherry.ini index 4415cf5..4962c55 100644 --- a/conf/ldapcherry.ini +++ b/conf/ldapcherry.ini @@ -68,7 +68,7 @@ roles.file = '/etc/ldapcherry/roles.yml' ldap.module = 'ldapcherry.backend.backendLdap' ldap.groupdn = 'ou=group,dc=example,dc=com' -ldap.userdn = 'ou=group,dc=example,dc=com' +ldap.userdn = 'ou=people,dc=example,dc=com' ldap.binddn = 'cn=ldapcherry,dc=example,dc=com' ldap.password = 'password' ldap.uri = 'ldaps://ldap.ldapcherry.org' @@ -79,6 +79,7 @@ ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(username)s)' ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' ldap.objectclasses = 'top, person, organizationalPerson, user' +ldap.dn_user_attr = 'uid' ldap.timeout = 1 diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 94a89a4..1130166 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -7,8 +7,16 @@ import cherrypy import ldap +import ldap.modlist as modlist import logging import ldapcherry.backend +import re + +class DelUserDontExists(Exception): + def __init__(self, user): + self.user = user + self.log = "cannot remove user, user <%(user)s> does not exist" % { 'user' : user} + class Backend(ldapcherry.backend.Backend): @@ -28,14 +36,20 @@ class Backend(ldapcherry.backend.Backend): self.user_filter_tmpl = self.get_param('user_filter_tmpl') self.group_filter_tmpl = self.get_param('group_filter_tmpl') self.search_filter_tmpl = self.get_param('search_filter_tmpl') - self.objectclasses = self.get_param('objectclasses') + self.dn_user_attr = self.get_param('dn_user_attr') + self.objectclasses = [] + for o in re.split('\W+', self.get_param('objectclasses')): + self.objectclasses.append(self._str(o)) + self.attrlist = [] for a in attrslist: + self.attrlist.append(self._str(a)) + + def _str(self, s): try: - self.attrlist.append(str(a)) + return str(s) except UnicodeEncodeError: - tmp = unicode(a).encode('unicode_escape') - self.attrlist.append(tmp) + return unicode(s).encode('unicode_escape') def auth(self, username, password): @@ -61,11 +75,47 @@ class Backend(ldapcherry.backend.Backend): def rm_from_group(self,username): pass - def add_user(self, username): - pass + def add_user(self, attrs): + ldap_client = self._bind() + attrs_str = {} + for a in attrs: + attrs_str[self._str(a)] = self._str(attrs[a]) + attrs_str['objectClass'] = self.objectclasses + dn = self.dn_user_attr + '=' + attrs[self.dn_user_attr] + ',' + self.userdn + ldif = modlist.addModlist(attrs_str) + try: + ldap_client.add_s(dn,ldif) + except ldap.OBJECT_CLASS_VIOLATION as e: + info = e[0]['info'] + desc = e[0]['desc'] + self._logger( + logging.ERROR, + "Configuration error, " + desc + ", " + info, + ) + raise e + except ldap.INSUFFICIENT_ACCESS as e: + info = e[0]['info'] + desc = e[0]['desc'] + self._logger( + logging.ERROR, + "Access error, " + desc + ", " + info, + ) + raise e + except ldap.ALREADY_EXISTS as e: + desc = e[0]['desc'] + self._logger( + logging.ERROR, + "adding user failed, " + desc, + ) + raise e def del_user(self, username): - pass + ldap_client = self._bind() + dn = self.get_user(username, False) + if not dn is None: + ldap_client.delete_s(dn) + else: + raise DelUserDontExists(username) def _bind(self): ldap_client = self._connect() diff --git a/tests/cfg/ldapcherry.ini b/tests/cfg/ldapcherry.ini index 18ff3d7..e9d3adc 100644 --- a/tests/cfg/ldapcherry.ini +++ b/tests/cfg/ldapcherry.ini @@ -79,6 +79,7 @@ ldap.user_filter_tmpl = '(uid=%(username)s)' ldap.group_filter_tmpl = '(member=%(userdn)s)' ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' ldap.objectclasses = 'top, person, organizationalPerson, user' +ldap.dn_user_attr = 'uid' ldap.timeout = 1 ad.module = 'ldapcherry.backend.backendSamba4' diff --git a/tests/test_BackendLdap.py b/tests/test_BackendLdap.py index 7333b96..df22b77 100644 --- a/tests/test_BackendLdap.py +++ b/tests/test_BackendLdap.py @@ -8,10 +8,10 @@ import pytest import sys from sets import Set from ldapcherry.backend.backendLdap import Backend -from ldapcherry import syslog_error from ldapcherry.exceptions import * import cherrypy -from ldap import SERVER_DOWN +import logging +import ldap cfg = { 'module' : 'ldapcherry.backend.ldap', @@ -26,9 +26,14 @@ cfg = { 'user_filter_tmpl' : '(uid=%(username)s)', 'group_filter_tmpl' : '(member=%(userdn)s)', 'search_filter_tmpl' : '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))', -'objectclasses' : 'top, person, organizationalPerson, user', +'objectclasses' : 'top, person, organizationalPerson, simpleSecurityObject, posixAccount', +'dn_user_attr' : 'uid', } +def syslog_error(msg='', context='', + severity=logging.INFO, traceback=False): + pass + cherrypy.log.error = syslog_error attr = ['shéll', 'shell', 'cn', 'uid', 'uidNumber', 'gidNumber', 'home', 'userPassword', 'givenName', 'email', 'sn'] @@ -61,7 +66,7 @@ class TestError(object): ldapc = inv._connect() try: ldapc.simple_bind_s(inv.binddn, inv.bindpassword) - except SERVER_DOWN as e: + except ldap.SERVER_DOWN as e: return else: raise AssertionError("expected an exception") @@ -75,7 +80,7 @@ class TestError(object): ldapc = inv._connect() try: ldapc.simple_bind_s(inv.binddn, inv.bindpassword) - except SERVER_DOWN as e: + except ldap.SERVER_DOWN as e: assert e[0]['info'] == 'TLS: hostname does not match CN in peer certificate' # def testConnectSSLNoCheck(self): @@ -116,8 +121,61 @@ class TestError(object): expected = ('cn=John Watson,ou=People,dc=example,dc=org', {'uid': ['jwatson'], 'cn': ['John Watson'], 'sn': ['watson']}) assert ret == expected - def testSearchtUser(self): + def testSearchUser(self): inv = Backend(cfg, cherrypy.log, 'ldap', attr) ret = inv.search('smith') expected = [('cn=Sheri Smith,ou=People,dc=example,dc=org', {'uid': ['ssmith'], 'objectClass': ['inetOrgPerson'], 'carLicense': ['HERCAR 125'], 'sn': ['smith'], 'mail': ['s.smith@example.com', 'ssmith@example.com', 'sheri.smith@example.com'], 'homePhone': ['555-111-2225'], 'cn': ['Sheri Smith']}), ('cn=John Smith,ou=People,dc=example,dc=org', {'uid': ['jsmith'], 'objectClass': ['inetOrgPerson'], 'carLicense': ['HISCAR 125'], 'sn': ['Smith'], 'mail': ['j.smith@example.com', 'jsmith@example.com', 'jsmith.smith@example.com'], 'homePhone': ['555-111-2225'], 'cn': ['John Smith']})] assert ret == expected + + def testAddUser(self): + inv = Backend(cfg, cherrypy.log, 'ldap', attr) + user = { + 'uid': 'test', + 'sn': 'test', + 'cn': 'test', + 'userPassword': 'test', + 'uidNumber': '42', + 'gidNumber': '42', + 'homeDirectory': '/home/test/' + } + inv.add_user(user) + inv.del_user('test') + + def testAddUserDuplicate(self): + inv = Backend(cfg, cherrypy.log, 'ldap', attr) + user = { + 'uid': 'test', + 'sn': 'test', + 'cn': 'test', + 'uidNumber': '42', + 'userPassword': 'test', + 'gidNumber': '42', + 'homeDirectory': '/home/test/' + } + try: + inv.add_user(user) + inv.add_user(user) + except ldap.ALREADY_EXISTS: + inv.del_user('test') + return + else: + inv.del_user('test') + raise AssertionError("expected an exception") + + def testAddUserMissingMustAttribute(self): + inv = Backend(cfg, cherrypy.log, 'ldap', attr) + user = { + 'uid': 'test', + 'sn': 'test', + 'cn': 'test', + 'userPassword': 'test', + 'gidNumber': '42', + 'homeDirectory': '/home/test/' + } + try: + inv.add_user(user) + except ldap.OBJECT_CLASS_VIOLATION: + return + else: + inv.del_user('test') + raise AssertionError("expected an exception") diff --git a/tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={1}hdb.ldif b/tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={1}hdb.ldif index e35ae5b..f767ece 100644 --- a/tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={1}hdb.ldif +++ b/tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={1}hdb.ldif @@ -7,7 +7,7 @@ olcSuffix: dc=example,dc=org olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymou s auth by dn="cn=admin,dc=example,dc=org" write by * none olcAccess: {1}to dn.base="" by * read -olcAccess: {2}to * by self write by dn="cn=admin,dc=example,dc=org" write by * read +olcAccess: {2}to * by self write by dn="cn=dnscherry,dc=example,dc=org" write by * read olcLastMod: TRUE olcRootDN: cn=admin,dc=example,dc=org olcRootPW: {SSHA}Fp+rSxe5eFsj0DGITJts4DwdSDFDZG9P From 03b703aea216f5acb787c5b79777b0211178b133 Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 00:34:56 +0200 Subject: [PATCH 09/22] adding misc --- misc/debug_ldapbackend.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/misc/debug_ldapbackend.py b/misc/debug_ldapbackend.py index ec09b83..ce5eb00 100644 --- a/misc/debug_ldapbackend.py +++ b/misc/debug_ldapbackend.py @@ -11,6 +11,7 @@ from ldapcherry.backend.backendLdap import Backend from ldapcherry import syslog_error from ldapcherry.exceptions import * import cherrypy +import logging from ldap import SERVER_DOWN cfg = { @@ -26,12 +27,31 @@ cfg = { 'user_filter_tmpl' : '(uid=%(username)s)', 'group_filter_tmpl' : '(member=%(userdn)s)', 'search_filter_tmpl' : '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))', - +'objectclasses' : 'top, person, organizationalPerson, simpleSecurityObject, posixAccount', +'dn_user_attr' : 'uid', } +def syslog_error(msg='', context='', + severity=logging.INFO, traceback=False): + pass + cherrypy.log.error = syslog_error attr = ['shéll', 'cn', 'uid', 'uidNumber', 'gidNumber', 'home', 'userPassword', 'givenName', 'email', 'sn'] +cherrypy.log.error = syslog_error + inv = Backend(cfg, cherrypy.log, 'ldap', attr) print inv.get_user('jwatson') -print inv.search('smit') +print inv.get_user('test') +#print inv.search('smit') +user = { +'uid': 'test', +'sn': 'test', +'cn': 'test', +'userPassword': 'test', +'uidNumber': '42', +'gidNumber': '42', +'homeDirectory': '/home/test/' +} +inv.add_user(user) +inv.del_user('test') From cb463b2a9d5b2cdff47d80dddf34ba5749994899 Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 08:50:31 +0200 Subject: [PATCH 10/22] include ldapcherry.backend in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 296caf4..e8159b0 100755 --- a/setup.py +++ b/setup.py @@ -87,7 +87,7 @@ setup( version = '0.0.1', author = 'Pierre-Francois Carpentier', author_email = 'carpentier.pf@gmail.com', - packages = ['ldapcherry'], + packages = ['ldapcherry', 'ldapcherry.backend'], data_files = resources_files, scripts = ['scripts/ldapcherryd'], url = 'https://github.com/kakwa/ldapcherry', From 91bc6bb18e778ce386e0221e7bafe0fb4fbf334c Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 22:50:42 +0200 Subject: [PATCH 11/22] reimplement get_user in ldap backend --- ldapcherry/backend/backendLdap.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 1130166..b081455 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -53,7 +53,7 @@ class Backend(ldapcherry.backend.Backend): def auth(self, username, password): - binddn = self.get_user(username, False) + binddn = self._get_user(username, False) if not binddn is None: ldap_client = self._connect() try: @@ -111,7 +111,7 @@ class Backend(ldapcherry.backend.Backend): def del_user(self, username): ldap_client = self._bind() - dn = self.get_user(username, False) + dn = self._get_user(username, False) if not dn is None: ldap_client.delete_s(dn) else: @@ -166,7 +166,18 @@ class Backend(ldapcherry.backend.Backend): return self._search(searchfilter, None) - def get_user(self, username, attrs=True): + def get_user(self, username): + ret = {} + attrs_tmp = self._get_user(username)[1] + for attr in attrs_tmp: + value_tmp = attrs_tmp[attr] + if len(value_tmp) == 1: + ret[attr] = value_tmp[0] + else: + ret[attr] = value_tmp + return ret + + def _get_user(self, username, attrs=True): if attrs: a = self.attrlist else: From c0939050152a922a9b21fee4579f33cdde0e1eff Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 22:51:29 +0200 Subject: [PATCH 12/22] fix test on ldap backend --- tests/test_BackendLdap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_BackendLdap.py b/tests/test_BackendLdap.py index df22b77..05bc282 100644 --- a/tests/test_BackendLdap.py +++ b/tests/test_BackendLdap.py @@ -118,7 +118,7 @@ class TestError(object): def testGetUser(self): inv = Backend(cfg, cherrypy.log, 'ldap', attr) ret = inv.get_user('jwatson') - expected = ('cn=John Watson,ou=People,dc=example,dc=org', {'uid': ['jwatson'], 'cn': ['John Watson'], 'sn': ['watson']}) + expected = {'uid': 'jwatson', 'cn': 'John Watson', 'sn': 'watson'} assert ret == expected def testSearchUser(self): From 9d314b6778d7e821c9a4ea96567ee71b68cb2f05 Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 22:51:57 +0200 Subject: [PATCH 13/22] implement authentification --- ldapcherry/__init__.py | 69 ++++++++++++++++++++++++++++++++++++---- ldapcherry/exceptions.py | 10 ++++++ 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/ldapcherry/__init__.py b/ldapcherry/__init__.py index a263ec7..06161b7 100644 --- a/ldapcherry/__init__.py +++ b/ldapcherry/__init__.py @@ -76,6 +76,21 @@ class LdapCherry(object): else: raise MissingParameter(section, key) + + def _get_groups(self, username): + ret = {} + for b in self.backends: + ret[b] = self.backends[b].get_groups(username) + return ret + + def _get_roles(self, username): + groups = self._get_groups(username) + return self.roles.get_roles(groups) + + def _is_admin(self, username): + roles = self._get_roles(username) + return self.roles.is_admin(roles['roles']) + def _check_backends(self): backends = self.backends_params.keys() for b in self.roles.get_backends(): @@ -114,6 +129,27 @@ class LdapCherry(object): except: raise BackendModuleInitFail(module) + + def _init_auth(self, config): + self.auth_mode = self._get_param('auth', 'auth.mode', config) + if self.auth_mode in ['and', 'or', 'none']: + pass + elif self.auth_mode == 'custom': + # load custom auth module + auth_module = self._get_param('auth', 'auth.module', config) + auth = __import__(auth_module, globals(), locals(), ['Auth'], -1) + self.auth = auth.Auth(config['auth'], cherrypy.log) + else: + raise WrongParamValue('auth.mode', 'auth', ['and', 'or', 'none', 'custom']) + + self.roles_file = self._get_param('roles', 'roles.file', config) + cherrypy.log.error( + msg = "loading roles file <%(file)s>" % { 'file': self.roles_file }, + severity = logging.DEBUG + ) + self.roles = Roles(self.roles_file) + + def _set_access_log(self, config, level): access_handler = self._get_param('global', 'log.access_handler', config, 'syslog') @@ -201,6 +237,27 @@ class LdapCherry(object): else: return logging.INFO + def _auth(self, user, password): + if self.auth_mode == 'none': + return {'connected': True, 'isadmin': True} + elif self.auth_mode == 'and': + ret1 = True + for b in self.backends: + ret1 = self.backends[b].auth(user, password) and ret1 + elif self.auth_mode == 'or': + ret1 = False + for b in self.backends: + ret1 = self.backends[b].auth(user, password) or ret1 + elif self.auth_mode == 'custom': + ret1 = self.auth.auth(user, password) + else: + raise Exception() + if not ret1: + return {'connected': False, 'isadmin': False} + else: + isadmin = self._is_admin(user) + return {'connected': True, 'isadmin': isadmin} + def reload(self, config = None): """ load/reload the configuration """ @@ -229,18 +286,14 @@ class LdapCherry(object): self.temp_login = self.temp_lookup.get_template('login.tmpl') - self.roles_file = self._get_param('roles', 'roles.file', config) - cherrypy.log.error( - msg = "loading roles file <%(file)s>" % { 'file': self.roles_file }, - severity = logging.DEBUG - ) - self.roles = Roles(self.roles_file) + self._init_auth(config) self.attributes_file = self._get_param('attributes', 'attributes.file', config) cherrypy.log.error( msg = "loading attributes file <%(file)s>" % { 'file': self.attributes_file }, severity = logging.DEBUG ) + self.attributes = Attributes(self.attributes_file) cherrypy.log.error( @@ -318,7 +371,8 @@ class LdapCherry(object): def login(self, login, password): """login page """ - if self.auth.check_credentials(login, password): + auth = self._auth(login, password) + if auth['connected']: message = "login success for user '%(user)s'" % { 'user': login } @@ -327,6 +381,7 @@ class LdapCherry(object): severity = logging.INFO ) cherrypy.session[SESSION_KEY] = cherrypy.request.login = login + cherrypy.session['isadmin'] = auth['isadmin'] raise cherrypy.HTTPRedirect("/") else: message = "login failed for user '%(user)s'" % { diff --git a/ldapcherry/exceptions.py b/ldapcherry/exceptions.py index bd612e6..27d62b4 100644 --- a/ldapcherry/exceptions.py +++ b/ldapcherry/exceptions.py @@ -5,6 +5,8 @@ # LdapCherry # Copyright (c) 2014 Carpentier Pierre-Francois +import string + class MissingParameter(Exception): def __init__(self, section, key): self.section = section @@ -69,6 +71,14 @@ class BackendModuleInitFail(Exception): self.module = module self.log = "fail to init module <%(module)s>" % {'module': module} +class WrongParamValue(Exception): + def __init__(self, param, section, possible_values): + self.possible_values = possible_values + self.section = section + self.param = param + possible_values_str = string.join(possible_values, ', ') + self.log = "wrong value for param <%(param)s> in section <%(section)s>, possible values are [%(values)s]" % {'param': param, 'section': section, 'values': possible_values_str} + class WrongAttributeType(Exception): def __init__(self, key, section, ymlfile): self.key = key From 746c2a9978884ad022bff275840078266d6a8aed Mon Sep 17 00:00:00 2001 From: kakwa Date: Tue, 26 May 2015 23:12:33 +0200 Subject: [PATCH 14/22] fix logging in ldap backend --- ldapcherry/backend/backendLdap.py | 33 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index b081455..5335642 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -72,9 +72,12 @@ class Backend(ldapcherry.backend.Backend): def set_attrs(self, attrs): pass - def rm_from_group(self,username): + def rm_from_group(self, username): pass + def get_groups(self, username): + return [] + def add_user(self, attrs): ldap_client = self._bind() attrs_str = {} @@ -89,23 +92,23 @@ class Backend(ldapcherry.backend.Backend): info = e[0]['info'] desc = e[0]['desc'] self._logger( - logging.ERROR, - "Configuration error, " + desc + ", " + info, + severity = logging.ERROR, + msg = "Configuration error, " + desc + ", " + info, ) raise e except ldap.INSUFFICIENT_ACCESS as e: info = e[0]['info'] desc = e[0]['desc'] self._logger( - logging.ERROR, - "Access error, " + desc + ", " + info, + severity = logging.ERROR, + msg = "Access error, " + desc + ", " + info, ) raise e except ldap.ALREADY_EXISTS as e: desc = e[0]['desc'] self._logger( - logging.ERROR, - "adding user failed, " + desc, + severity = logging.ERROR, + msg = "adding user failed, " + desc, ) raise e @@ -123,15 +126,15 @@ class Backend(ldapcherry.backend.Backend): ldap_client.simple_bind_s(self.binddn, self.bindpassword) except ldap.INVALID_CREDENTIALS as e: self._logger( - logging.ERROR, - "Configuration error, wrong credentials, unable to connect to ldap with '" + self.binddn + "'", + severity = logging.ERROR, + msg = "Configuration error, wrong credentials, unable to connect to ldap with '" + self.binddn + "'", ) ldap_client.unbind_s() raise e except ldap.SERVER_DOWN as e: self._logger( - logging.ERROR, - "Unable to contact ldap server '" + self.uri + "', check 'auth.ldap.uri' and ssl/tls configuration", + severity = logging.ERROR, + msg = "Unable to contact ldap server '" + self.uri + "', check 'auth.ldap.uri' and ssl/tls configuration", ) ldap_client.unbind_s() raise e @@ -148,8 +151,8 @@ class Backend(ldapcherry.backend.Backend): ) except ldap.FILTER_ERROR as e: self._logger( - logging.ERROR, - "Bad search filter, check '" + self.backend_name + ".*_filter_tmpl' params", + severity = logging.ERROR, + msg = "Bad search filter, check '" + self.backend_name + ".*_filter_tmpl' params", ) ldap_client.unbind_s() raise e @@ -219,8 +222,8 @@ class Backend(ldapcherry.backend.Backend): ldap_client.start_tls_s() except ldap.OPERATIONS_ERROR as e: self._logger( - logging.ERROR, - "cannot use starttls with ldaps:// uri (uri: " + self.uri + ")", + severity = logging.ERROR, + msg = "cannot use starttls with ldaps:// uri (uri: " + self.uri + ")", ) raise e #raise cherrypy.HTTPError("500", "Configuration Error, contact administrator") From dc7f07ab38e92c4669d0bda58dc3592e4d56d8a1 Mon Sep 17 00:00:00 2001 From: kakwa Date: Wed, 27 May 2015 21:48:41 +0200 Subject: [PATCH 15/22] implement get_groups --- ldapcherry/backend/backendLdap.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 5335642..75d73d9 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -76,7 +76,18 @@ class Backend(ldapcherry.backend.Backend): pass def get_groups(self, username): - return [] + userdn = self._get_user(username, False) + + searchfilter = self.group_filter_tmpl % { + 'userdn': userdn, + 'username': username + } + + groups = self._search(searchfilter, None, self.groupdn) + ret = [] + for entry in groups: + ret.append(entry[0]) + return ret def add_user(self, attrs): ldap_client = self._bind() @@ -141,10 +152,10 @@ class Backend(ldapcherry.backend.Backend): return ldap_client - def _search(self, searchfilter, attrs): + def _search(self, searchfilter, attrs, basedn): ldap_client = self._bind() try: - r = ldap_client.search_s(self.userdn, + r = ldap_client.search_s(basedn, ldap.SCOPE_SUBTREE, searchfilter, attrlist=attrs @@ -167,7 +178,7 @@ class Backend(ldapcherry.backend.Backend): 'searchstring': searchstring } - return self._search(searchfilter, None) + return self._search(searchfilter, None, self.userdn) def get_user(self, username): ret = {} @@ -190,7 +201,7 @@ class Backend(ldapcherry.backend.Backend): 'username': username } - r = self._search(user_filter, a) + r = self._search(user_filter, a, self.userdn) if len(r) == 0: return None From 231b591d6a376869abe10701c4df75319e408708 Mon Sep 17 00:00:00 2001 From: kakwa Date: Wed, 27 May 2015 21:56:55 +0200 Subject: [PATCH 16/22] add unit test for get_groups --- tests/test_BackendLdap.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_BackendLdap.py b/tests/test_BackendLdap.py index 05bc282..4543976 100644 --- a/tests/test_BackendLdap.py +++ b/tests/test_BackendLdap.py @@ -15,7 +15,7 @@ import ldap cfg = { 'module' : 'ldapcherry.backend.ldap', -'groupdn' : 'ou=group,dc=example,dc=org', +'groupdn' : 'ou=groups,dc=example,dc=org', 'userdn' : 'ou=People,dc=example,dc=org', 'binddn' : 'cn=dnscherry,dc=example,dc=org', 'password' : 'password', @@ -121,6 +121,12 @@ class TestError(object): expected = {'uid': 'jwatson', 'cn': 'John Watson', 'sn': 'watson'} assert ret == expected + def testGetUser(self): + inv = Backend(cfg, cherrypy.log, 'ldap', attr) + ret = inv.get_groups('jwatson') + expected = ['cn=itpeople,ou=Groups,dc=example,dc=org'] + assert ret == expected + def testSearchUser(self): inv = Backend(cfg, cherrypy.log, 'ldap', attr) ret = inv.search('smith') From 380085fdfcd59896e1655a555a98f4229afce064 Mon Sep 17 00:00:00 2001 From: kakwa Date: Wed, 27 May 2015 21:59:29 +0200 Subject: [PATCH 17/22] fix configuration --- conf/ldapcherry.ini | 2 +- tests/cfg/ldapcherry.ini | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/ldapcherry.ini b/conf/ldapcherry.ini index 4962c55..ebc1da7 100644 --- a/conf/ldapcherry.ini +++ b/conf/ldapcherry.ini @@ -67,7 +67,7 @@ roles.file = '/etc/ldapcherry/roles.yml' [backends] ldap.module = 'ldapcherry.backend.backendLdap' -ldap.groupdn = 'ou=group,dc=example,dc=com' +ldap.groupdn = 'ou=groups,dc=example,dc=com' ldap.userdn = 'ou=people,dc=example,dc=com' ldap.binddn = 'cn=ldapcherry,dc=example,dc=com' ldap.password = 'password' diff --git a/tests/cfg/ldapcherry.ini b/tests/cfg/ldapcherry.ini index e9d3adc..5cc168c 100644 --- a/tests/cfg/ldapcherry.ini +++ b/tests/cfg/ldapcherry.ini @@ -67,8 +67,8 @@ roles.file = './tests/cfg/roles.yml' [backends] ldap.module = 'ldapcherry.backend.backendLdap' -ldap.groupdn = 'ou=group,dc=example,dc=com' -ldap.userdn = 'ou=group,dc=example,dc=com' +ldap.groupdn = 'ou=groups,dc=example,dc=com' +ldap.userdn = 'ou=people,dc=example,dc=com' ldap.binddn = 'cn=dnscherry,dc=example,dc=org' ldap.password = 'password' ldap.uri = 'ldaps://ldap.ldapcherry.org' From e559f695895fcd7751bb89e08d7bbd4f3dda39fb Mon Sep 17 00:00:00 2001 From: kakwa Date: Wed, 27 May 2015 21:59:54 +0200 Subject: [PATCH 18/22] add exception handling --- ldapcherry/backend/backendLdap.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ldapcherry/backend/backendLdap.py b/ldapcherry/backend/backendLdap.py index 75d73d9..2dc9de2 100644 --- a/ldapcherry/backend/backendLdap.py +++ b/ldapcherry/backend/backendLdap.py @@ -167,6 +167,16 @@ class Backend(ldapcherry.backend.Backend): ) ldap_client.unbind_s() raise e + except ldap.NO_SUCH_OBJECT as e: + self._logger( + severity = logging.ERROR, + msg = "Search DN '" + basedn \ + + "' doesn't exist, check '" \ + + self.backend_name + ".userdn' or '" \ + + self.backend_name + ".groupdn'", + ) + ldap_client.unbind_s() + raise e ldap_client.unbind_s() return r From 55e71960f7f31ed0b478e2e6dea28b459a03a0f3 Mon Sep 17 00:00:00 2001 From: kakwa Date: Wed, 27 May 2015 22:00:15 +0200 Subject: [PATCH 19/22] better example for ldap backend --- misc/debug_ldapbackend.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/misc/debug_ldapbackend.py b/misc/debug_ldapbackend.py index ce5eb00..5cc9f1a 100644 --- a/misc/debug_ldapbackend.py +++ b/misc/debug_ldapbackend.py @@ -16,7 +16,7 @@ from ldap import SERVER_DOWN cfg = { 'module' : 'ldapcherry.backend.ldap', -'groupdn' : 'ou=group,dc=example,dc=org', +'groupdn' : 'ou=Groups,dc=example,dc=org', 'userdn' : 'ou=People,dc=example,dc=org', 'binddn' : 'cn=dnscherry,dc=example,dc=org', 'password' : 'password', @@ -42,8 +42,8 @@ cherrypy.log.error = syslog_error inv = Backend(cfg, cherrypy.log, 'ldap', attr) print inv.get_user('jwatson') -print inv.get_user('test') -#print inv.search('smit') +print inv.get_groups('jwatson') +print inv.search('smit') user = { 'uid': 'test', 'sn': 'test', @@ -54,4 +54,6 @@ user = { 'homeDirectory': '/home/test/' } inv.add_user(user) +print inv.get_user('test') +print inv.get_groups('test') inv.del_user('test') From bbb4ec41172f4cf4b70dca89fdac307dcafdacf4 Mon Sep 17 00:00:00 2001 From: kakwa Date: Thu, 28 May 2015 00:17:59 +0200 Subject: [PATCH 20/22] adding a check to avoid removing previous configuration in setup.py --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e8159b0..565b047 100755 --- a/setup.py +++ b/setup.py @@ -73,10 +73,13 @@ def get_list_files(basedir, targetdir): return_list.append((os.path.join(targetdir, subpath), files_list)) return return_list +# add static files and templates in the list of thing to deploy resources_files = get_list_files('resources', os.path.join(datarootdir, 'share', 'ldapcherry')) -resources_files.append(( +# add the configuration files if they don't exist +if not os.path.exists(os.path.join(sysconfdir, 'ldapcherry')): + resources_files.append(( os.path.join(sysconfdir, 'ldapcherry'), [ 'conf/ldapcherry.ini', 'conf/attributes.yml', 'conf/roles.yml'] )) From 0ae234383a46b588105191f296ae2732650ba83f Mon Sep 17 00:00:00 2001 From: kakwa Date: Thu, 28 May 2015 00:57:15 +0200 Subject: [PATCH 21/22] implementing access controle methods --- ldapcherry/__init__.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/ldapcherry/__init__.py b/ldapcherry/__init__.py index 06161b7..6ba7946 100644 --- a/ldapcherry/__init__.py +++ b/ldapcherry/__init__.py @@ -361,6 +361,21 @@ class LdapCherry(object): message = 'Example warning' return render_error(alert, message) + def _check_auth(self, must_admin): + if not 'connected' in cherrypy.session or not cherrypy.session['connected']: + raise cherrypy.HTTPRedirect("/signin") + if cherrypy.session['connected'] and \ + not cherrypy.session['isadmin']: + if must_admin: + raise cherrypy.HTTPError("403 Forbidden", "You are not allowed to access this resource.") + else: + return + if cherrypy.session['connected'] and \ + cherrypy.session['isadmin']: + return + else: + raise cherrypy.HTTPRedirect("/signin") + @cherrypy.expose def signin(self): """simple signin page @@ -372,6 +387,9 @@ class LdapCherry(object): """login page """ auth = self._auth(login, password) + cherrypy.session['isadmin'] = auth['isadmin'] + cherrypy.session['connected'] = auth['connected'] + if auth['connected']: message = "login success for user '%(user)s'" % { 'user': login @@ -381,7 +399,6 @@ class LdapCherry(object): severity = logging.INFO ) cherrypy.session[SESSION_KEY] = cherrypy.request.login = login - cherrypy.session['isadmin'] = auth['isadmin'] raise cherrypy.HTTPRedirect("/") else: message = "login failed for user '%(user)s'" % { @@ -405,36 +422,41 @@ class LdapCherry(object): msg = message, severity = logging.INFO ) - raise cherrypy.HTTPRedirect("/signin") @cherrypy.expose def index(self, **params): """main page rendering """ + self._check_auth(must_admin=False) pass @cherrypy.expose def searchuser(self): """ search user page """ + self._check_auth(must_admin=True) pass @cherrypy.expose def adduser(self): """ add user page """ + self._check_auth(must_admin=True) pass @cherrypy.expose def removeuser(self): """ remove user page """ + self._check_auth(must_admin=True) pass @cherrypy.expose def modifyuser(self): """ modify user page """ + self._check_auth(must_admin=True) pass @cherrypy.expose def modifyself(self): """ self modify user page """ + self._check_auth(must_admin=False) pass From 3bb36d1a7453f77d760cc7646410a630e8272633 Mon Sep 17 00:00:00 2001 From: kakwa Date: Thu, 28 May 2015 01:23:22 +0200 Subject: [PATCH 22/22] implementing logout --- ldapcherry/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ldapcherry/__init__.py b/ldapcherry/__init__.py index 6ba7946..a104048 100644 --- a/ldapcherry/__init__.py +++ b/ldapcherry/__init__.py @@ -414,9 +414,14 @@ class LdapCherry(object): def logout(self): """ logout page """ - user = self.auth.end_session() + sess = cherrypy.session + username = sess.get(SESSION_KEY, None) + sess[SESSION_KEY] = None + if username: + cherrypy.request.login = None + message = "user '%(user)s' logout" % { - 'user': user + 'user': username } cherrypy.log.error( msg = message,