2015-04-15 21:13:14 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
|
|
|
#
|
|
|
|
# The MIT License (MIT)
|
|
|
|
# LdapCherry
|
|
|
|
# Copyright (c) 2014 Carpentier Pierre-Francois
|
|
|
|
|
2015-05-20 17:13:18 +02:00
|
|
|
import cherrypy
|
|
|
|
import ldap
|
2015-05-26 00:33:36 +02:00
|
|
|
import ldap.modlist as modlist
|
2015-05-20 17:13:18 +02:00
|
|
|
import logging
|
2015-05-20 14:21:43 +02:00
|
|
|
import ldapcherry.backend
|
2015-05-26 00:33:36 +02:00
|
|
|
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}
|
|
|
|
|
2015-05-20 14:21:43 +02:00
|
|
|
|
2015-04-15 21:13:14 +02:00
|
|
|
class Backend(ldapcherry.backend.Backend):
|
|
|
|
|
2015-05-22 10:27:46 +02:00
|
|
|
def __init__(self, config, logger, name, attrslist):
|
2015-05-20 17:13:18 +02:00
|
|
|
self.config = config
|
2015-05-21 08:52:06 +02:00
|
|
|
self._logger = logger
|
2015-05-21 19:55:11 +02:00
|
|
|
self.backend_name = name
|
2015-05-21 21:40:13 +02:00
|
|
|
self.binddn = self.get_param('binddn')
|
2015-05-22 01:16:53 +02:00
|
|
|
self.bindpassword = self.get_param('password')
|
2015-05-21 21:40:13 +02:00
|
|
|
self.ca = self.get_param('ca', False)
|
|
|
|
self.checkcert = self.get_param('checkcert', 'on')
|
|
|
|
self.starttls = self.get_param('starttls', 'off')
|
|
|
|
self.uri = self.get_param('uri')
|
2015-05-22 20:05:24 +02:00
|
|
|
self.timeout = self.get_param('timeout', 1)
|
2015-05-22 01:16:53 +02:00
|
|
|
self.userdn = self.get_param('userdn')
|
|
|
|
self.groupdn = self.get_param('groupdn')
|
2015-05-21 21:40:13 +02:00
|
|
|
self.user_filter_tmpl = self.get_param('user_filter_tmpl')
|
2015-05-25 18:52:14 +02:00
|
|
|
self.group_filter_tmpl = self.get_param('group_filter_tmpl')
|
|
|
|
self.search_filter_tmpl = self.get_param('search_filter_tmpl')
|
2015-05-26 00:33:36 +02:00
|
|
|
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))
|
|
|
|
|
2015-05-22 20:05:24 +02:00
|
|
|
self.attrlist = []
|
|
|
|
for a in attrslist:
|
2015-05-26 00:33:36 +02:00
|
|
|
self.attrlist.append(self._str(a))
|
|
|
|
|
|
|
|
def _str(self, s):
|
2015-05-22 20:05:24 +02:00
|
|
|
try:
|
2015-05-26 00:33:36 +02:00
|
|
|
return str(s)
|
2015-05-22 20:05:24 +02:00
|
|
|
except UnicodeEncodeError:
|
2015-05-26 00:33:36 +02:00
|
|
|
return unicode(s).encode('unicode_escape')
|
2015-05-20 17:13:18 +02:00
|
|
|
|
|
|
|
def auth(self, username, password):
|
|
|
|
|
2015-05-26 22:50:42 +02:00
|
|
|
binddn = self._get_user(username, False)
|
2015-05-24 15:11:49 +02:00
|
|
|
if not binddn is None:
|
2015-05-20 17:13:18 +02:00
|
|
|
ldap_client = self._connect()
|
|
|
|
try:
|
|
|
|
ldap_client.simple_bind_s(binddn, password)
|
|
|
|
except ldap.INVALID_CREDENTIALS:
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
return False
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def add_to_group(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def set_attrs(self, attrs):
|
|
|
|
pass
|
|
|
|
|
2015-05-24 15:20:17 +02:00
|
|
|
def rm_from_group(self,username):
|
2015-05-20 17:13:18 +02:00
|
|
|
pass
|
|
|
|
|
2015-05-26 00:33:36 +02:00
|
|
|
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
|
2015-05-20 17:13:18 +02:00
|
|
|
|
|
|
|
def del_user(self, username):
|
2015-05-26 00:33:36 +02:00
|
|
|
ldap_client = self._bind()
|
2015-05-26 22:50:42 +02:00
|
|
|
dn = self._get_user(username, False)
|
2015-05-26 00:33:36 +02:00
|
|
|
if not dn is None:
|
|
|
|
ldap_client.delete_s(dn)
|
|
|
|
else:
|
|
|
|
raise DelUserDontExists(username)
|
2015-05-20 17:13:18 +02:00
|
|
|
|
2015-05-25 22:17:17 +02:00
|
|
|
def _bind(self):
|
2015-05-25 19:30:41 +02:00
|
|
|
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 + "'",
|
|
|
|
)
|
2015-05-25 19:52:54 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-05-25 19:30:41 +02:00
|
|
|
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",
|
|
|
|
)
|
2015-05-25 19:52:54 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-05-25 19:30:41 +02:00
|
|
|
raise e
|
2015-05-25 22:17:17 +02:00
|
|
|
return ldap_client
|
|
|
|
|
2015-05-25 19:30:41 +02:00
|
|
|
|
2015-05-25 22:17:17 +02:00
|
|
|
def _search(self, searchfilter, attrs):
|
|
|
|
ldap_client = self._bind()
|
2015-05-25 19:30:41 +02:00
|
|
|
try:
|
|
|
|
r = ldap_client.search_s(self.userdn,
|
|
|
|
ldap.SCOPE_SUBTREE,
|
2015-05-25 19:52:54 +02:00
|
|
|
searchfilter,
|
|
|
|
attrlist=attrs
|
2015-05-25 19:30:41 +02:00
|
|
|
)
|
|
|
|
except ldap.FILTER_ERROR as e:
|
2015-05-25 19:52:54 +02:00
|
|
|
self._logger(
|
|
|
|
logging.ERROR,
|
|
|
|
"Bad search filter, check '" + self.backend_name + ".*_filter_tmpl' params",
|
|
|
|
)
|
|
|
|
ldap_client.unbind_s()
|
2015-05-25 19:30:41 +02:00
|
|
|
raise e
|
2015-05-25 19:52:54 +02:00
|
|
|
|
|
|
|
ldap_client.unbind_s()
|
2015-05-25 19:30:41 +02:00
|
|
|
return r
|
2015-05-24 17:32:03 +02:00
|
|
|
|
2015-05-25 19:52:54 +02:00
|
|
|
|
|
|
|
def search(self, searchstring):
|
|
|
|
|
|
|
|
searchfilter = self.search_filter_tmpl % {
|
|
|
|
'searchstring': searchstring
|
|
|
|
}
|
|
|
|
|
|
|
|
return self._search(searchfilter, None)
|
|
|
|
|
2015-05-26 22:50:42 +02:00
|
|
|
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):
|
2015-05-22 20:05:24 +02:00
|
|
|
if attrs:
|
|
|
|
a = self.attrlist
|
|
|
|
else:
|
|
|
|
a = None
|
2015-05-20 17:13:18 +02:00
|
|
|
|
2015-05-22 01:16:53 +02:00
|
|
|
user_filter = self.user_filter_tmpl % {
|
|
|
|
'username': username
|
|
|
|
}
|
2015-05-20 17:13:18 +02:00
|
|
|
|
2015-05-25 19:52:54 +02:00
|
|
|
r = self._search(user_filter, a)
|
|
|
|
|
2015-05-20 17:13:18 +02:00
|
|
|
if len(r) == 0:
|
2015-05-24 15:11:49 +02:00
|
|
|
return None
|
2015-05-20 17:13:18 +02:00
|
|
|
|
2015-05-22 20:05:24 +02:00
|
|
|
if attrs:
|
|
|
|
dn_entry = r[0]
|
|
|
|
else:
|
|
|
|
dn_entry = r[0][0]
|
2015-05-20 17:13:18 +02:00
|
|
|
return dn_entry
|
|
|
|
|
|
|
|
def _connect(self):
|
2015-05-21 19:55:11 +02:00
|
|
|
ldap_client = ldap.initialize(self.uri)
|
2015-05-20 17:13:18 +02:00
|
|
|
ldap_client.set_option(ldap.OPT_REFERRALS, 0)
|
2015-05-22 20:05:24 +02:00
|
|
|
ldap_client.set_option(ldap.OPT_TIMEOUT, self.timeout)
|
2015-05-21 19:55:11 +02:00
|
|
|
if self.starttls == 'on':
|
2015-05-20 17:13:18 +02:00
|
|
|
ldap.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
2015-05-22 01:16:53 +02:00
|
|
|
else:
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_DEMAND, False)
|
|
|
|
if self.ca and self.checkcert == 'on':
|
2015-05-21 19:55:11 +02:00
|
|
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca)
|
2015-05-22 01:16:53 +02:00
|
|
|
#else:
|
|
|
|
# ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '')
|
2015-05-21 19:55:11 +02:00
|
|
|
if self.checkcert == 'off':
|
2015-05-20 17:13:18 +02:00
|
|
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
|
|
|
|
else:
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_DEMAND)
|
2015-05-21 21:40:13 +02:00
|
|
|
if self.starttls == 'on':
|
2015-05-20 17:13:18 +02:00
|
|
|
try:
|
|
|
|
ldap_client.start_tls_s()
|
2015-05-22 01:48:27 +02:00
|
|
|
except ldap.OPERATIONS_ERROR as e:
|
2015-05-20 17:13:18 +02:00
|
|
|
self._logger(
|
|
|
|
logging.ERROR,
|
2015-05-21 19:55:11 +02:00
|
|
|
"cannot use starttls with ldaps:// uri (uri: " + self.uri + ")",
|
2015-05-20 17:13:18 +02:00
|
|
|
)
|
2015-05-22 01:48:27 +02:00
|
|
|
raise e
|
|
|
|
#raise cherrypy.HTTPError("500", "Configuration Error, contact administrator")
|
2015-05-20 17:13:18 +02:00
|
|
|
return ldap_client
|