python-mylib/LdapServer.py

285 lines
11 KiB
Python
Raw Normal View History

2013-06-07 12:13:03 +02:00
#!/usr/bin/python
2019-06-13 19:15:13 +02:00
import copy
import datetime
import dateutil.parser
import dateutil.tz
2013-06-07 12:13:03 +02:00
import ldap
import ldap.modlist as modlist
import logging
2019-06-13 19:15:13 +02:00
import pytz
2013-06-07 12:13:03 +02:00
class LdapServer(object):
uri = None
dn = None
pwd = None
v2 = None
con = 0
2018-12-12 17:11:23 +01:00
def __init__(self,uri,dn=None,pwd=None,v2=None,raiseOnError=False, logger=False):
2013-06-07 12:13:03 +02:00
self.uri = uri
self.dn = dn
self.pwd = pwd
self.raiseOnError = raiseOnError
2013-06-07 12:13:03 +02:00
if v2:
self.v2=True
2018-12-12 17:11:23 +01:00
if logger:
self.logger = logger
else:
self.logger = logging.getLogger()
2013-06-07 12:13:03 +02:00
def _error(self,error,level=logging.WARNING):
if self.raiseOnError:
2018-02-13 15:30:38 +01:00
raise LdapServerException(error)
else:
2018-12-12 17:11:23 +01:00
self.logger.log(level,error)
2013-06-07 12:13:03 +02:00
def connect(self):
if self.con == 0:
try:
con = ldap.initialize(self.uri)
if self.v2:
con.protocol_version = ldap.VERSION2
else:
con.protocol_version = ldap.VERSION3
if self.dn:
con.simple_bind_s(self.dn,self.pwd)
self.con = con
return True
2013-06-07 12:13:03 +02:00
except ldap.LDAPError, e:
self._error('LdapServer - Error connecting and binding to LDAP server : %s' % e,logging.CRITICAL)
return False
return True
2013-06-07 12:13:03 +02:00
def get_scope(self, scope):
if scope == 'base':
return ldap.SCOPE_BASE
elif scope == 'one':
return ldap.SCOPE_ONELEVEL
elif scope == 'sub':
return ldap.SCOPE_SUBTREE
raise Exception("Unknown LDAP scope '%s'" % scope)
def search(self, basedn, filterstr, attrs, sizelimit=0, scope='sub'):
res_id = self.con.search(basedn, self.get_scope(scope), filterstr, attrs)
2013-06-07 12:13:03 +02:00
ret = {}
c=0
while 1:
res_type, res_data = self.con.result(res_id,0)
if res_data == [] or sizelimit!=0 and c>sizelimit:
break
else:
if res_type == ldap.RES_SEARCH_ENTRY:
ret[res_data[0][0]]=res_data[0][1]
c=c+1
return ret
def add_object(self,dn,attrs):
ldif = modlist.addModlist(attrs)
try:
2018-12-12 17:11:23 +01:00
self.logger.debug("LdapServer - Add %s" % dn)
2013-06-07 12:13:03 +02:00
self.con.add_s(dn,ldif)
return True
except ldap.LDAPError, e:
2018-02-13 15:30:38 +01:00
self._error("LdapServer - Error adding %s : %s" % (dn,e), logging.error)
2013-06-07 12:13:03 +02:00
return False
def update_object(self, dn, old, new, ignore_attrs=[]):
ldif = modlist.modifyModlist(old, new, ignore_attr_types=ignore_attrs)
2013-06-07 12:13:03 +02:00
if ldif == []:
return True
try:
self.con.modify_s(dn,ldif)
return True
except ldap.LDAPError, e:
2018-02-13 15:30:38 +01:00
self._error("LdapServer - Error updating %s : %s\nOld : %s\nNew : %s" % (dn, e, old, new), logging.error)
2013-06-07 12:13:03 +02:00
return False
def update_need(self, old, new, ignore_attrs=[]):
ldif = modlist.modifyModlist(old, new, ignore_attr_types=ignore_attrs)
if ldif == []:
return False
return True
2015-04-01 17:08:26 +02:00
def rename_object(self,dn,new_rdn):
try:
2018-12-12 17:11:23 +01:00
self.logger.debug("LdapServer - Rename %s in %s" % (dn,new_rdn))
2015-04-01 17:08:26 +02:00
self.con.rename_s(dn,new_rdn)
return True
except ldap.LDAPError, e:
2018-02-13 15:30:38 +01:00
self._error("LdapServer - Error renaming %s in %s : %s" % (dn,new_rdn,e), logging.error)
2015-04-01 17:08:26 +02:00
return False
2013-06-07 12:13:03 +02:00
def drop_object(self,dn):
try:
2018-12-12 17:11:23 +01:00
self.logger.debug("LdapServer - Delete %s" % dn)
2013-06-07 12:13:03 +02:00
self.con.delete_s(dn)
return True
except ldap.LDAPError, e:
2018-02-13 15:30:38 +01:00
self._error("LdapServer - Error deleting %s : %s" % (dn,e), logging.error)
2013-06-07 12:13:03 +02:00
return False
def get_dn(self,obj):
return obj[0][0]
def get_attr(self,obj,attr,all=None,default=None):
if attr not in obj:
for k in obj:
if k.lower() == attr.lower():
attr = k
break
2013-06-07 12:13:03 +02:00
if all is not None:
if attr in obj:
return obj[attr]
else:
return default or []
2013-06-07 12:13:03 +02:00
else:
if attr in obj:
return obj[attr][0]
else:
return default
class LdapServerException(BaseException):
def __init__(self,msg):
BaseException.__init__(self, msg)
2019-06-13 19:15:13 +02:00
#
# Helpers
#
def parse_datetime(value, to_timezone=None, default_timezone=None):
2019-07-25 14:05:37 +02:00
"""
Convert LDAP date string to datetime.datetime object
:param value: The LDAP date string to convert
:param to_timezone: If specified, the return datetime will be converted to this
specific timezone (optional, default : timezone of the LDAP date string)
:param default_timezone: The timezone used if LDAP date string does not specified
the timezone (optional, default : server local timezone)
"""
assert to_timezone is None or isinstance(to_timezone, datetime.tzinfo) or isinstance(to_timezone, str), 'to_timezone must be None, a datetime.tzinfo object or a string (not %s)' % type(to_timezone)
assert default_timezone is None or isinstance(default_timezone, datetime.tzinfo) or isinstance(default_timezone, pytz.tzinfo.DstTzInfo) or isinstance(default_timezone, str), 'default_timezone parameter must be None, a string, a pytz.tzinfo.DstTzInfo or a datetime.tzinfo object (not %s)' % type(default_timezone)
2019-06-13 19:15:13 +02:00
date = dateutil.parser.parse(value, dayfirst=False)
if not date.tzinfo:
if not default_timezone:
default_timezone = pytz.utc
elif isinstance(default_timezone, str):
default_timezone = pytz.timezone(default_timezone)
if isinstance(default_timezone, pytz.tzinfo.DstTzInfo):
date = default_timezone.localize(date)
elif isinstance(default_timezone, datetime.tzinfo):
2019-06-13 19:15:13 +02:00
date = date.replace(tzinfo=default_timezone)
else:
raise Exception("It's not supposed to happen!")
if to_timezone:
if isinstance(to_timezone, str):
to_timezone = pytz.timezone(to_timezone)
return date.astimezone(to_timezone)
2019-06-13 19:15:13 +02:00
return date
def parse_date(value, to_timezone=None, default_timezone=None):
2019-07-25 14:05:37 +02:00
"""
Convert LDAP date string to datetime.date object
:param value: The LDAP date string to convert
:param to_timezone: If specified, the return datetime will be converted to this
specific timezone (optional, default : timezone of the LDAP date string)
:param default_timezone: The timezone used if LDAP date string does not specified
the timezone (optional, default : server local timezone)
"""
return parse_datetime(value, to_timezone, default_timezone).date()
2019-06-13 19:15:13 +02:00
def format_datetime(value, from_timezone=None, to_timezone=None):
2019-07-25 14:05:37 +02:00
"""
Convert datetime.datetime object to LDAP date string
:param value: The datetime.datetime object to convert
:param from_timezone: The timezone used if datetime.datetime object is naive (no tzinfo)
(optional, default : server local timezone)
:param to_timezone: The timezone used in LDAP (optional, default : UTC)
"""
2019-06-13 19:15:13 +02:00
assert isinstance(value, datetime.datetime), 'First parameter must be an datetime.datetime object (not %s)' % type(value)
assert from_timezone is None or isinstance(from_timezone, datetime.tzinfo) or isinstance(from_timezone, pytz.tzinfo.DstTzInfo) or isinstance(from_timezone, str), 'from_timezone parameter must be None, a string, a pytz.tzinfo.DstTzInfo or a datetime.tzinfo object (not %s)' % type(from_timezone)
assert to_timezone is None or isinstance(to_timezone, datetime.tzinfo) or isinstance(to_timezone, str), 'to_timezone must be None, a datetime.tzinfo object or a string (not %s)' % type(to_timezone)
2019-06-13 19:15:13 +02:00
if not value.tzinfo:
if not from_timezone:
from_timezone = dateutil.tz.tzlocal()
elif isinstance(from_timezone, str):
from_timezone = pytz.timezone(from_timezone)
if isinstance(from_timezone, pytz.tzinfo.DstTzInfo):
from_value = from_timezone.localize(value)
elif isinstance(from_timezone, datetime.tzinfo):
from_value = value.replace(tzinfo=from_timezone)
else:
raise Exception("It's not supposed to happen!")
else:
from_value = copy.deepcopy(value)
if not to_timezone:
to_timezone = pytz.utc
elif isinstance(to_timezone, str):
to_timezone = pytz.timezone(to_timezone)
2019-06-13 19:15:13 +02:00
to_value = from_value.astimezone(to_timezone)
datestring = to_value.strftime('%Y%m%d%H%M%S%z')
2019-06-13 19:15:13 +02:00
if datestring.endswith('+0000'):
datestring = datestring.replace('+0000', 'Z')
return datestring
def format_date(value, from_timezone=None, to_timezone=None):
2019-07-25 14:05:37 +02:00
"""
Convert datetime.date object to LDAP date string
:param value: The datetime.date object to convert
:param from_timezone: The timezone used if datetime.datetime object is naive (no tzinfo)
(optional, default : server local timezone)
:param to_timezone: The timezone used in LDAP (optional, default : UTC)
"""
2019-06-13 19:15:13 +02:00
assert isinstance(value, datetime.date), 'First parameter must be an datetime.date object (not %s)' % type(value)
return format_datetime(datetime.datetime.combine(value, datetime.datetime.min.time()), from_timezone, to_timezone)
#
# Tests
#
if __name__ == '__main__':
now = datetime.datetime.now()
print "Now = %s" % now
datestring_now = format_datetime(now)
print "format_datetime : %s" % datestring_now
print "format_datetime (from_timezone=utc) : %s" % format_datetime(now.replace(tzinfo=None), from_timezone=pytz.utc)
print "format_datetime (from_timezone=local) : %s" % format_datetime(now.replace(tzinfo=None), from_timezone=dateutil.tz.tzlocal())
print "format_datetime (from_timezone=Paris) : %s" % format_datetime(now.replace(tzinfo=None), from_timezone='Europe/Paris')
print "format_datetime (to_timezone=utc) : %s" % format_datetime(now, to_timezone=pytz.utc)
print "format_datetime (to_timezone=local) : %s" % format_datetime(now, to_timezone=dateutil.tz.tzlocal())
print "format_datetime (to_timezone=Tokyo) : %s" % format_datetime(now, to_timezone='Asia/Tokyo')
print "format_date : %s" % format_date(now)
print "format_date (from_timezone=utc) : %s" % format_date(now.replace(tzinfo=None), from_timezone=pytz.utc)
print "format_date (from_timezone=local) : %s" % format_date(now.replace(tzinfo=None), from_timezone=dateutil.tz.tzlocal())
print "format_date (from_timezone=Paris) : %s" % format_date(now.replace(tzinfo=None), from_timezone='Europe/Paris')
print "format_date (to_timezone=utc) : %s" % format_date(now, to_timezone=pytz.utc)
print "format_date (to_timezone=local) : %s" % format_date(now, to_timezone=dateutil.tz.tzlocal())
print "format_date (to_timezone=Tokyo) : %s" % format_date(now, to_timezone='Asia/Tokyo')
print "parse_datetime : %s" % parse_datetime(datestring_now)
print "parse_datetime (default_timezone=utc) : %s" % parse_datetime(datestring_now[0:-1], default_timezone=pytz.utc)
print "parse_datetime (default_timezone=local) : %s" % parse_datetime(datestring_now[0:-1], default_timezone=dateutil.tz.tzlocal())
print "parse_datetime (default_timezone=Paris) : %s" % parse_datetime(datestring_now[0:-1], default_timezone='Europe/Paris')
print "parse_datetime (to_timezone=utc) : %s" % parse_datetime(datestring_now, to_timezone=pytz.utc)
print "parse_datetime (to_timezone=local) : %s" % parse_datetime(datestring_now, to_timezone=dateutil.tz.tzlocal())
print "parse_datetime (to_timezone=Tokyo) : %s" % parse_datetime(datestring_now, to_timezone='Asia/Tokyo')
print "parse_date : %s" % parse_date(datestring_now)
print "parse_date (default_timezone=utc) : %s" % parse_date(datestring_now[0:-1], default_timezone=pytz.utc)
print "parse_date (default_timezone=local) : %s" % parse_date(datestring_now[0:-1], default_timezone=dateutil.tz.tzlocal())
print "parse_date (default_timezone=Paris) : %s" % parse_date(datestring_now[0:-1], default_timezone='Europe/Paris')
print "parse_date (to_timezone=utc) : %s" % parse_date(datestring_now, to_timezone=pytz.utc)
print "parse_date (to_timezone=local) : %s" % parse_date(datestring_now, to_timezone=dateutil.tz.tzlocal())
print "parse_date (to_timezone=Tokyo) : %s" % parse_date(datestring_now, to_timezone='Asia/Tokyo')