rpcclient.py must call XML-RPC functions with all arguments. Removed encode_args and decode_args. They were the source of most of the argument pain. Now opts is alwyas appended to the end of the arguments so MUST be the last argument in any server-side function (can be None) Allow the User object to handle unicode data Small fixes to command-line tools to be friendlier Broke out get_user() into get_user_by_dn() and get_user_by_uid() Need to request more than just 'nsAccountLock' attribute when trying to see if a user is already marked deleted. If it is blank the record coming back is empty. Add 'uid' to the list to guarantee something coming back (dn is handled specially) Added user_container attribute to get_user_* and add_user so the caller can specify where in the tree the user will be searched for/added. Added global default value for user_container
154 lines
5.9 KiB
Python
154 lines
5.9 KiB
Python
import ldap
|
|
import ldif
|
|
import re
|
|
import cStringIO
|
|
|
|
def utf8_encode_value(value):
|
|
if isinstance(value,unicode):
|
|
return value.encode('utf-8')
|
|
return value
|
|
|
|
def utf8_encode_values(values):
|
|
if isinstance(values,list) or isinstance(values,tuple):
|
|
return map(utf8_encode_value, values)
|
|
else:
|
|
return utf8_encode_value(values)
|
|
|
|
class User:
|
|
"""This class represents an IPA user. An LDAP entry consists of a DN
|
|
and a list of attributes. Each attribute consists of a name and a list of
|
|
values. For the time being I will maintain this.
|
|
|
|
In python-ldap, entries are returned as a list of 2-tuples.
|
|
Instance variables:
|
|
dn - string - the string DN of the entry
|
|
data - cidict - case insensitive dict of the attributes and values
|
|
orig_data - cidict - case insentiive dict of the original attributes and values"""
|
|
|
|
def __init__(self,entrydata=None):
|
|
"""data is the raw data returned from the python-ldap result method,
|
|
which is a search result entry or a reference or None.
|
|
If creating a new empty entry, data is the string DN."""
|
|
if entrydata:
|
|
if isinstance(entrydata,tuple):
|
|
self.dn = entrydata[0]
|
|
self.data = ldap.cidict.cidict(entrydata[1])
|
|
elif isinstance(entrydata,str) or isinstance(entrydata,unicode):
|
|
self.dn = entrydata
|
|
self.data = ldap.cidict.cidict()
|
|
elif isinstance(entrydata,dict):
|
|
self.dn = entrydata['dn']
|
|
del entrydata['dn']
|
|
self.data = ldap.cidict.cidict(entrydata)
|
|
else:
|
|
self.dn = ''
|
|
self.data = ldap.cidict.cidict()
|
|
|
|
self.orig_data = dict(self.data)
|
|
|
|
def __nonzero__(self):
|
|
"""This allows us to do tests like if entry: returns false if there is no data,
|
|
true otherwise"""
|
|
return self.data != None and len(self.data) > 0
|
|
|
|
def hasAttr(self,name):
|
|
"""Return True if this entry has an attribute named name, False otherwise"""
|
|
return self.data and self.data.has_key(name)
|
|
|
|
def __setattr__(self,name,value):
|
|
"""One should use setValue() or setValues() to set values except for
|
|
dn and data which are special."""
|
|
if name != 'dn' and name != 'data' and name != 'orig_data':
|
|
raise KeyError, 'use setValue() or setValues()'
|
|
else:
|
|
self.__dict__[name] = value
|
|
|
|
def __getattr__(self,name):
|
|
"""If name is the name of an LDAP attribute, return the first value for that
|
|
attribute - equivalent to getValue - this allows the use of
|
|
entry.cn
|
|
instead of
|
|
entry.getValue('cn')
|
|
This also allows us to return None if an attribute is not found rather than
|
|
throwing an exception"""
|
|
return self.getValue(name)
|
|
|
|
def getValues(self,name):
|
|
"""Get the list (array) of values for the attribute named name"""
|
|
return self.data.get(name)
|
|
|
|
def getValue(self,name):
|
|
"""Get the first value for the attribute named name"""
|
|
value = self.data.get(name,[None])
|
|
if isinstance(value,list) or isinstance(value,tuple):
|
|
return value[0]
|
|
else:
|
|
return value
|
|
|
|
def setValue(self,name,*value):
|
|
"""Value passed in may be a single value, several values, or a single sequence.
|
|
For example:
|
|
ent.setValue('name', 'value')
|
|
ent.setValue('name', 'value1', 'value2', ..., 'valueN')
|
|
ent.setValue('name', ['value1', 'value2', ..., 'valueN'])
|
|
ent.setValue('name', ('value1', 'value2', ..., 'valueN'))
|
|
Since *value is a tuple, we may have to extract a list or tuple from that
|
|
tuple as in the last two examples above"""
|
|
if (len(value) < 1):
|
|
return
|
|
if (len(value) == 1):
|
|
self.data[name] = utf8_encode_values(value[0])
|
|
else:
|
|
self.data[name] = utf8_encode_values(value)
|
|
|
|
setValues = setValue
|
|
|
|
def toTupleList(self):
|
|
"""Convert the attrs and values to a list of 2-tuples. The first element
|
|
of the tuple is the attribute name. The second element is either a
|
|
single value or a list of values."""
|
|
return self.data.items()
|
|
|
|
def toDict(self):
|
|
"""Convert the attrs and values to a dict. The dict is keyed on the
|
|
attribute name. The value is either single value or a list of values."""
|
|
result = {}
|
|
for k in self.data.keys():
|
|
result[k] = self.data[k]
|
|
result['dn'] = self.dn
|
|
return result
|
|
|
|
def attrList(self):
|
|
"""Return a list of all attributes in the entry"""
|
|
return self.data.keys()
|
|
|
|
def origDataDict(self):
|
|
"""Returns a dict of the original values of the user. Used for updates."""
|
|
result = {}
|
|
for k in self.orig_data.keys():
|
|
result[k] = self.orig_data[k]
|
|
result['dn'] = self.dn
|
|
return result
|
|
|
|
# def __str__(self):
|
|
# """Convert the Entry to its LDIF representation"""
|
|
# return self.__repr__()
|
|
#
|
|
# # the ldif class base64 encodes some attrs which I would rather see in raw form - to
|
|
# # encode specific attrs as base64, add them to the list below
|
|
# ldif.safe_string_re = re.compile('^$')
|
|
# base64_attrs = ['nsstate', 'krbprincipalkey', 'krbExtraData']
|
|
#
|
|
# def __repr__(self):
|
|
# """Convert the Entry to its LDIF representation"""
|
|
# sio = cStringIO.StringIO()
|
|
# # what's all this then? the unparse method will currently only accept
|
|
# # a list or a dict, not a class derived from them. self.data is a
|
|
# # cidict, so unparse barfs on it. I've filed a bug against python-ldap,
|
|
# # but in the meantime, we have to convert to a plain old dict for printing
|
|
# # I also don't want to see wrapping, so set the line width really high (1000)
|
|
# newdata = {}
|
|
# newdata.update(self.data)
|
|
# ldif.LDIFWriter(sio,User.base64_attrs,1000).unparse(self.dn,newdata)
|
|
# return sio.getvalue()
|