2008-09-12 11:36:04 -05:00
|
|
|
# Authors: Rich Megginson <richm@redhat.com>
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
# Rob Crittenden <rcritten@redhat.com>
|
|
|
|
# John Dennis <jdennis@redhat.com>
|
2008-09-12 11:36:04 -05:00
|
|
|
#
|
|
|
|
# Copyright (C) 2007 Red Hat
|
|
|
|
# see file 'COPYING' for use and warranty information
|
|
|
|
#
|
2010-12-09 06:59:11 -06:00
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
2008-09-12 11:36:04 -05:00
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
2010-12-09 06:59:11 -06:00
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2008-09-12 11:36:04 -05:00
|
|
|
#
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import socket
|
|
|
|
import ldif
|
|
|
|
import re
|
|
|
|
import string
|
|
|
|
import ldap
|
|
|
|
import cStringIO
|
2009-04-28 16:05:39 -05:00
|
|
|
import time
|
2008-09-12 11:36:04 -05:00
|
|
|
import struct
|
|
|
|
import ldap.sasl
|
2011-12-08 05:34:36 -06:00
|
|
|
import ldapurl
|
2012-02-01 06:25:46 -06:00
|
|
|
from ldap.controls import LDAPControl
|
2008-09-12 11:36:04 -05:00
|
|
|
from ldap.ldapobject import SimpleLDAPObject
|
2012-04-18 10:22:35 -05:00
|
|
|
from ipapython import ipautil
|
2009-04-23 07:51:59 -05:00
|
|
|
from ipalib import errors
|
2011-09-30 03:09:55 -05:00
|
|
|
from ipapython.ipautil import format_netloc
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
from ipapython.entity import Entity
|
ticket #1870 - subclass SimpleLDAPObject
We use convenience types (classes) in IPA which make working with LDAP
easier and more robust. It would be really nice if the basic python-ldap
library understood our utility types and could accept them as parameters
to the basic ldap functions and/or the basic ldap functions returned our
utility types.
Normally such a requirement would trivially be handled in an object-
oriented language (which Python is) by subclassing to extend and modify
the functionality. For some reason we didn't do this with the python-ldap
classes.
python-ldap objects are primarily used in two different places in our
code, ipaserver.ipaldap.py for the IPAdmin class and in
ipaserver/plugins/ldap2.py for the ldap2 class's .conn member.
In IPAdmin we use a IPA utility class called Entry to make it easier to
use the results returned by LDAP. The IPAdmin class is derived from
python-ldap.SimpleLDAPObject. But for some reason when we added the
support for the use of the Entry class in SimpleLDAPObject we didn't
subclass SimpleLDAPObject and extend it for use with the Entry class as
would be the normal expected methodology in an object-oriented language,
rather we used an obscure feature of the Python language to override all
methods of the SimpleLDAPObject class by wrapping those class methods in
another function call. The reason why this isn't a good approach is:
* It violates object-oriented methodology.
* Other classes cannot be derived and inherit the customization (because
the method wrapping occurs in a class instance, not within the class
type).
* It's non-obvious and obscure
* It's inefficient.
Here is a summary of what the code was doing:
It iterated over every member of the SimpleLDAPObject class and if it was
callable it wrapped the method. The wrapper function tested the name of
the method being wrapped, if it was one of a handful of methods we wanted
to customize we modified a parameter and called the original method. If
the method wasn't of interest to use we still wrapped the method.
It was inefficient because every non-customized method (the majority)
executed a function call for the wrapper, the wrapper during run-time used
logic to determine if the method was being overridden and then called the
original method. So every call to ldap was doing extra function calls and
logic processing which for the majority of cases produced nothing useful
(and was non-obvious from brief code reading some methods were being
overridden).
Object-orientated languages have support built in for calling the right
method for a given class object that do not involve extra function call
overhead to realize customized class behaviour. Also when programmers look
for customized class behaviour they look for derived classes. They might
also want to utilize the customized class as the base class for their use.
Also the wrapper logic was fragile, it did things like: if the method name
begins with "add" I'll unconditionally modify the first and second
argument. It would be some much cleaner if the "add", "add_s", etc.
methods were overridden in a subclass where the logic could be seen and
where it would apply to only the explicit functions and parameters being
overridden.
Also we would really benefit if there were classes which could be used as
a base class which had specific ldap customization.
At the moment our ldap customization needs are:
1) Support DN objects being passed to ldap operations
2) Support Entry & Entity objects being passed into and returned from
ldap operations.
We want to subclass the ldap SimpleLDAPObject class, that is the base
ldap class with all the ldap methods we're using. IPASimpleLDAPObject
class would subclass SimpleLDAPObject class which knows about DN
objects (and possilby other IPA specific types that are universally
used in IPA). Then IPAEntrySimpleLDAPObject would subclass
IPASimpleLDAPObject which knows about Entry objects.
The reason for the suggested class hierarchy is because DN objects will be
used whenever we talk to LDAP (in the future we may want to add other IPA
specific classes which will always be used). We don't add Entry support to
the the IPASimpleLDAPObject class because Entry objects are (currently)
only used in IPAdmin.
What this patch does is:
* Introduce IPASimpleLDAPObject derived from
SimpleLDAPObject. IPASimpleLDAPObject is DN object aware.
* Introduce IPAEntryLDAPObject derived from
IPASimpleLDAPObject. IPAEntryLDAPObject is Entry object aware.
* Derive IPAdmin from IPAEntryLDAPObject and remove the funky method
wrapping from IPAdmin.
* Code which called add_s() with an Entry or Entity object now calls
addEntry(). addEntry() always existed, it just wasn't always
used. add_s() had been modified to accept Entry or Entity object
(why didn't we just call addEntry()?). The add*() ldap routine in
IPAEntryLDAPObject have been subclassed to accept Entry and Entity
objects, but that should proably be removed in the future and just
use addEntry().
* Replace the call to ldap.initialize() in ldap2.create_connection()
with a class constructor for IPASimpleLDAPObject. The
ldap.initialize() is a convenience function in python-ldap, but it
always returns a SimpleLDAPObject created via the SimpleLDAPObject
constructor, thus ldap.initialize() did not allow subclassing, yet
has no particular ease-of-use advantage thus we better off using the
obvious class constructor mechanism.
* Fix the use of _handle_errors(), it's not necessary to construct an
empty dict to pass to it.
If we follow the standard class derivation pattern for ldap we can make us
of our own ldap utilities in a far easier, cleaner and more efficient
manner.
2011-09-26 16:33:32 -05:00
|
|
|
from ipaserver.plugins.ldap2 import IPASimpleLDAPObject
|
2011-12-08 05:34:36 -06:00
|
|
|
from ipaserver.install import installutils
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
# Global variable to define SASL auth
|
2011-02-25 17:37:45 -06:00
|
|
|
SASL_AUTH = ldap.sasl.sasl({},'GSSAPI')
|
2011-12-08 05:34:36 -06:00
|
|
|
DEFAULT_TIMEOUT = 10
|
2008-09-12 11:36:04 -05:00
|
|
|
|
ticket #1870 - subclass SimpleLDAPObject
We use convenience types (classes) in IPA which make working with LDAP
easier and more robust. It would be really nice if the basic python-ldap
library understood our utility types and could accept them as parameters
to the basic ldap functions and/or the basic ldap functions returned our
utility types.
Normally such a requirement would trivially be handled in an object-
oriented language (which Python is) by subclassing to extend and modify
the functionality. For some reason we didn't do this with the python-ldap
classes.
python-ldap objects are primarily used in two different places in our
code, ipaserver.ipaldap.py for the IPAdmin class and in
ipaserver/plugins/ldap2.py for the ldap2 class's .conn member.
In IPAdmin we use a IPA utility class called Entry to make it easier to
use the results returned by LDAP. The IPAdmin class is derived from
python-ldap.SimpleLDAPObject. But for some reason when we added the
support for the use of the Entry class in SimpleLDAPObject we didn't
subclass SimpleLDAPObject and extend it for use with the Entry class as
would be the normal expected methodology in an object-oriented language,
rather we used an obscure feature of the Python language to override all
methods of the SimpleLDAPObject class by wrapping those class methods in
another function call. The reason why this isn't a good approach is:
* It violates object-oriented methodology.
* Other classes cannot be derived and inherit the customization (because
the method wrapping occurs in a class instance, not within the class
type).
* It's non-obvious and obscure
* It's inefficient.
Here is a summary of what the code was doing:
It iterated over every member of the SimpleLDAPObject class and if it was
callable it wrapped the method. The wrapper function tested the name of
the method being wrapped, if it was one of a handful of methods we wanted
to customize we modified a parameter and called the original method. If
the method wasn't of interest to use we still wrapped the method.
It was inefficient because every non-customized method (the majority)
executed a function call for the wrapper, the wrapper during run-time used
logic to determine if the method was being overridden and then called the
original method. So every call to ldap was doing extra function calls and
logic processing which for the majority of cases produced nothing useful
(and was non-obvious from brief code reading some methods were being
overridden).
Object-orientated languages have support built in for calling the right
method for a given class object that do not involve extra function call
overhead to realize customized class behaviour. Also when programmers look
for customized class behaviour they look for derived classes. They might
also want to utilize the customized class as the base class for their use.
Also the wrapper logic was fragile, it did things like: if the method name
begins with "add" I'll unconditionally modify the first and second
argument. It would be some much cleaner if the "add", "add_s", etc.
methods were overridden in a subclass where the logic could be seen and
where it would apply to only the explicit functions and parameters being
overridden.
Also we would really benefit if there were classes which could be used as
a base class which had specific ldap customization.
At the moment our ldap customization needs are:
1) Support DN objects being passed to ldap operations
2) Support Entry & Entity objects being passed into and returned from
ldap operations.
We want to subclass the ldap SimpleLDAPObject class, that is the base
ldap class with all the ldap methods we're using. IPASimpleLDAPObject
class would subclass SimpleLDAPObject class which knows about DN
objects (and possilby other IPA specific types that are universally
used in IPA). Then IPAEntrySimpleLDAPObject would subclass
IPASimpleLDAPObject which knows about Entry objects.
The reason for the suggested class hierarchy is because DN objects will be
used whenever we talk to LDAP (in the future we may want to add other IPA
specific classes which will always be used). We don't add Entry support to
the the IPASimpleLDAPObject class because Entry objects are (currently)
only used in IPAdmin.
What this patch does is:
* Introduce IPASimpleLDAPObject derived from
SimpleLDAPObject. IPASimpleLDAPObject is DN object aware.
* Introduce IPAEntryLDAPObject derived from
IPASimpleLDAPObject. IPAEntryLDAPObject is Entry object aware.
* Derive IPAdmin from IPAEntryLDAPObject and remove the funky method
wrapping from IPAdmin.
* Code which called add_s() with an Entry or Entity object now calls
addEntry(). addEntry() always existed, it just wasn't always
used. add_s() had been modified to accept Entry or Entity object
(why didn't we just call addEntry()?). The add*() ldap routine in
IPAEntryLDAPObject have been subclassed to accept Entry and Entity
objects, but that should proably be removed in the future and just
use addEntry().
* Replace the call to ldap.initialize() in ldap2.create_connection()
with a class constructor for IPASimpleLDAPObject. The
ldap.initialize() is a convenience function in python-ldap, but it
always returns a SimpleLDAPObject created via the SimpleLDAPObject
constructor, thus ldap.initialize() did not allow subclassing, yet
has no particular ease-of-use advantage thus we better off using the
obvious class constructor mechanism.
* Fix the use of _handle_errors(), it's not necessary to construct an
empty dict to pass to it.
If we follow the standard class derivation pattern for ldap we can make us
of our own ldap utilities in a far easier, cleaner and more efficient
manner.
2011-09-26 16:33:32 -05:00
|
|
|
class IPAEntryLDAPObject(IPASimpleLDAPObject):
|
|
|
|
def __init__(self, *args, **kwds):
|
|
|
|
IPASimpleLDAPObject.__init__(self, *args, **kwds)
|
|
|
|
|
|
|
|
def result(self, msgid=ldap.RES_ANY, all=1, timeout=None):
|
|
|
|
objtype, data = IPASimpleLDAPObject.result(self, msgid, all, timeout)
|
|
|
|
# data is either a 2-tuple or a list of 2-tuples
|
|
|
|
if data:
|
|
|
|
if isinstance(data, tuple):
|
|
|
|
return objtype, Entry(data)
|
|
|
|
elif isinstance(data, list):
|
|
|
|
return objtype, [Entry(x) for x in data]
|
|
|
|
else:
|
|
|
|
raise TypeError, "unknown data type %s returned by result" % type(data)
|
|
|
|
else:
|
|
|
|
return objtype, data
|
|
|
|
|
|
|
|
def add(self, dn, modlist):
|
|
|
|
if isinstance(dn, Entry):
|
|
|
|
return IPASimpleLDAPObject.add(self, dn.dn, dn.toTupleList())
|
|
|
|
else:
|
|
|
|
return IPASimpleLDAPObject.add(self, dn, modlist)
|
|
|
|
|
|
|
|
def add_s(self, dn, modlist):
|
|
|
|
if isinstance(dn, Entry):
|
|
|
|
return IPASimpleLDAPObject.add_s(self, dn.dn, dn.toTupleList())
|
|
|
|
else:
|
|
|
|
return IPASimpleLDAPObject.add_s(self, dn, modlist)
|
|
|
|
|
|
|
|
def add_ext(self, dn, modlist, serverctrls=None, clientctrls=None):
|
|
|
|
if isinstance(dn, Entry):
|
|
|
|
return IPASimpleLDAPObject.add_ext(self, dn.dn, dn.toTupleList(), serverctrls, clientctrls)
|
|
|
|
else:
|
|
|
|
return IPASimpleLDAPObject.add_ext(self, dn, modlist, serverctrls, clientctrls)
|
|
|
|
|
|
|
|
def add_ext_s(self, dn, modlist, serverctrls=None, clientctrls=None):
|
|
|
|
if isinstance(dn, Entry):
|
|
|
|
return IPASimpleLDAPObject.add_ext_s(self, dn.dn, dn.toTupleList(), serverctrls, clientctrls)
|
|
|
|
else:
|
|
|
|
return IPASimpleLDAPObject.add_ext_s(self, dn, modlist, serverctrls, clientctrls)
|
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
class Entry:
|
2008-10-18 00:25:50 -05:00
|
|
|
"""
|
|
|
|
This class represents an LDAP Entry object. An LDAP entry consists of
|
|
|
|
a DN and a list of attributes. Each attribute consists of a name and
|
|
|
|
a list of values. 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
|
2008-10-01 20:00:13 -05:00
|
|
|
"""
|
2008-09-12 11:36:04 -05:00
|
|
|
def __init__(self,entrydata):
|
|
|
|
"""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 = ipautil.CIDict(entrydata[1])
|
|
|
|
elif isinstance(entrydata,str) or isinstance(entrydata,unicode):
|
|
|
|
self.dn = entrydata
|
|
|
|
self.data = ipautil.CIDict()
|
|
|
|
else:
|
|
|
|
self.dn = ''
|
|
|
|
self.data = ipautil.CIDict()
|
|
|
|
|
|
|
|
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 __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"""
|
|
|
|
return self.data.get(name,[None])[0]
|
|
|
|
|
2008-10-18 00:25:50 -05:00
|
|
|
def setValue(self, name, *value):
|
|
|
|
"""
|
|
|
|
Set a value on this entry.
|
|
|
|
|
|
|
|
The 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.
|
|
|
|
"""
|
2008-09-12 11:36:04 -05:00
|
|
|
if isinstance(value[0],list) or isinstance(value[0],tuple):
|
|
|
|
self.data[name] = value[0]
|
|
|
|
else:
|
|
|
|
self.data[name] = value
|
|
|
|
|
|
|
|
setValues = setValue
|
|
|
|
|
2009-01-16 09:24:32 -06:00
|
|
|
def delAttr(self, name):
|
|
|
|
"""
|
|
|
|
Entirely remove an attribute of this entry.
|
|
|
|
"""
|
|
|
|
if self.hasAttr(name):
|
|
|
|
del self.data[name]
|
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
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."""
|
2008-10-04 00:50:59 -05:00
|
|
|
r = []
|
|
|
|
for i in self.data.iteritems():
|
|
|
|
n = ipautil.utf8_encode_values(i[1])
|
|
|
|
r.append((i[0], n))
|
|
|
|
return r
|
2008-09-12 11:36:04 -05:00
|
|
|
|
2008-10-07 03:31:22 -05:00
|
|
|
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 = ipautil.CIDict(self.data)
|
|
|
|
for i in result.keys():
|
|
|
|
result[i] = ipautil.utf8_encode_values(result[i])
|
|
|
|
result['dn'] = self.dn
|
|
|
|
return result
|
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
def __str__(self):
|
|
|
|
"""Convert the Entry to its LDIF representation"""
|
|
|
|
return self.__repr__()
|
|
|
|
|
2008-10-01 20:00:13 -05:00
|
|
|
# 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
|
2008-09-12 11:36:04 -05:00
|
|
|
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,
|
2008-10-01 20:00:13 -05:00
|
|
|
# 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)
|
2008-09-12 11:36:04 -05:00
|
|
|
newdata = {}
|
|
|
|
newdata.update(self.data)
|
|
|
|
ldif.LDIFWriter(sio,Entry.base64_attrs,1000).unparse(self.dn,newdata)
|
|
|
|
return sio.getvalue()
|
|
|
|
|
ticket #1870 - subclass SimpleLDAPObject
We use convenience types (classes) in IPA which make working with LDAP
easier and more robust. It would be really nice if the basic python-ldap
library understood our utility types and could accept them as parameters
to the basic ldap functions and/or the basic ldap functions returned our
utility types.
Normally such a requirement would trivially be handled in an object-
oriented language (which Python is) by subclassing to extend and modify
the functionality. For some reason we didn't do this with the python-ldap
classes.
python-ldap objects are primarily used in two different places in our
code, ipaserver.ipaldap.py for the IPAdmin class and in
ipaserver/plugins/ldap2.py for the ldap2 class's .conn member.
In IPAdmin we use a IPA utility class called Entry to make it easier to
use the results returned by LDAP. The IPAdmin class is derived from
python-ldap.SimpleLDAPObject. But for some reason when we added the
support for the use of the Entry class in SimpleLDAPObject we didn't
subclass SimpleLDAPObject and extend it for use with the Entry class as
would be the normal expected methodology in an object-oriented language,
rather we used an obscure feature of the Python language to override all
methods of the SimpleLDAPObject class by wrapping those class methods in
another function call. The reason why this isn't a good approach is:
* It violates object-oriented methodology.
* Other classes cannot be derived and inherit the customization (because
the method wrapping occurs in a class instance, not within the class
type).
* It's non-obvious and obscure
* It's inefficient.
Here is a summary of what the code was doing:
It iterated over every member of the SimpleLDAPObject class and if it was
callable it wrapped the method. The wrapper function tested the name of
the method being wrapped, if it was one of a handful of methods we wanted
to customize we modified a parameter and called the original method. If
the method wasn't of interest to use we still wrapped the method.
It was inefficient because every non-customized method (the majority)
executed a function call for the wrapper, the wrapper during run-time used
logic to determine if the method was being overridden and then called the
original method. So every call to ldap was doing extra function calls and
logic processing which for the majority of cases produced nothing useful
(and was non-obvious from brief code reading some methods were being
overridden).
Object-orientated languages have support built in for calling the right
method for a given class object that do not involve extra function call
overhead to realize customized class behaviour. Also when programmers look
for customized class behaviour they look for derived classes. They might
also want to utilize the customized class as the base class for their use.
Also the wrapper logic was fragile, it did things like: if the method name
begins with "add" I'll unconditionally modify the first and second
argument. It would be some much cleaner if the "add", "add_s", etc.
methods were overridden in a subclass where the logic could be seen and
where it would apply to only the explicit functions and parameters being
overridden.
Also we would really benefit if there were classes which could be used as
a base class which had specific ldap customization.
At the moment our ldap customization needs are:
1) Support DN objects being passed to ldap operations
2) Support Entry & Entity objects being passed into and returned from
ldap operations.
We want to subclass the ldap SimpleLDAPObject class, that is the base
ldap class with all the ldap methods we're using. IPASimpleLDAPObject
class would subclass SimpleLDAPObject class which knows about DN
objects (and possilby other IPA specific types that are universally
used in IPA). Then IPAEntrySimpleLDAPObject would subclass
IPASimpleLDAPObject which knows about Entry objects.
The reason for the suggested class hierarchy is because DN objects will be
used whenever we talk to LDAP (in the future we may want to add other IPA
specific classes which will always be used). We don't add Entry support to
the the IPASimpleLDAPObject class because Entry objects are (currently)
only used in IPAdmin.
What this patch does is:
* Introduce IPASimpleLDAPObject derived from
SimpleLDAPObject. IPASimpleLDAPObject is DN object aware.
* Introduce IPAEntryLDAPObject derived from
IPASimpleLDAPObject. IPAEntryLDAPObject is Entry object aware.
* Derive IPAdmin from IPAEntryLDAPObject and remove the funky method
wrapping from IPAdmin.
* Code which called add_s() with an Entry or Entity object now calls
addEntry(). addEntry() always existed, it just wasn't always
used. add_s() had been modified to accept Entry or Entity object
(why didn't we just call addEntry()?). The add*() ldap routine in
IPAEntryLDAPObject have been subclassed to accept Entry and Entity
objects, but that should proably be removed in the future and just
use addEntry().
* Replace the call to ldap.initialize() in ldap2.create_connection()
with a class constructor for IPASimpleLDAPObject. The
ldap.initialize() is a convenience function in python-ldap, but it
always returns a SimpleLDAPObject created via the SimpleLDAPObject
constructor, thus ldap.initialize() did not allow subclassing, yet
has no particular ease-of-use advantage thus we better off using the
obvious class constructor mechanism.
* Fix the use of _handle_errors(), it's not necessary to construct an
empty dict to pass to it.
If we follow the standard class derivation pattern for ldap we can make us
of our own ldap utilities in a far easier, cleaner and more efficient
manner.
2011-09-26 16:33:32 -05:00
|
|
|
class IPAdmin(IPAEntryLDAPObject):
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
def __localinit(self):
|
|
|
|
"""If a CA certificate is provided then it is assumed that we are
|
|
|
|
doing SSL client authentication with proxy auth.
|
|
|
|
|
|
|
|
If a CA certificate is not present then it is assumed that we are
|
|
|
|
using a forwarded kerberos ticket for SASL auth. SASL provides
|
|
|
|
its own encryption.
|
|
|
|
"""
|
|
|
|
if self.cacert is not None:
|
ticket #1870 - subclass SimpleLDAPObject
We use convenience types (classes) in IPA which make working with LDAP
easier and more robust. It would be really nice if the basic python-ldap
library understood our utility types and could accept them as parameters
to the basic ldap functions and/or the basic ldap functions returned our
utility types.
Normally such a requirement would trivially be handled in an object-
oriented language (which Python is) by subclassing to extend and modify
the functionality. For some reason we didn't do this with the python-ldap
classes.
python-ldap objects are primarily used in two different places in our
code, ipaserver.ipaldap.py for the IPAdmin class and in
ipaserver/plugins/ldap2.py for the ldap2 class's .conn member.
In IPAdmin we use a IPA utility class called Entry to make it easier to
use the results returned by LDAP. The IPAdmin class is derived from
python-ldap.SimpleLDAPObject. But for some reason when we added the
support for the use of the Entry class in SimpleLDAPObject we didn't
subclass SimpleLDAPObject and extend it for use with the Entry class as
would be the normal expected methodology in an object-oriented language,
rather we used an obscure feature of the Python language to override all
methods of the SimpleLDAPObject class by wrapping those class methods in
another function call. The reason why this isn't a good approach is:
* It violates object-oriented methodology.
* Other classes cannot be derived and inherit the customization (because
the method wrapping occurs in a class instance, not within the class
type).
* It's non-obvious and obscure
* It's inefficient.
Here is a summary of what the code was doing:
It iterated over every member of the SimpleLDAPObject class and if it was
callable it wrapped the method. The wrapper function tested the name of
the method being wrapped, if it was one of a handful of methods we wanted
to customize we modified a parameter and called the original method. If
the method wasn't of interest to use we still wrapped the method.
It was inefficient because every non-customized method (the majority)
executed a function call for the wrapper, the wrapper during run-time used
logic to determine if the method was being overridden and then called the
original method. So every call to ldap was doing extra function calls and
logic processing which for the majority of cases produced nothing useful
(and was non-obvious from brief code reading some methods were being
overridden).
Object-orientated languages have support built in for calling the right
method for a given class object that do not involve extra function call
overhead to realize customized class behaviour. Also when programmers look
for customized class behaviour they look for derived classes. They might
also want to utilize the customized class as the base class for their use.
Also the wrapper logic was fragile, it did things like: if the method name
begins with "add" I'll unconditionally modify the first and second
argument. It would be some much cleaner if the "add", "add_s", etc.
methods were overridden in a subclass where the logic could be seen and
where it would apply to only the explicit functions and parameters being
overridden.
Also we would really benefit if there were classes which could be used as
a base class which had specific ldap customization.
At the moment our ldap customization needs are:
1) Support DN objects being passed to ldap operations
2) Support Entry & Entity objects being passed into and returned from
ldap operations.
We want to subclass the ldap SimpleLDAPObject class, that is the base
ldap class with all the ldap methods we're using. IPASimpleLDAPObject
class would subclass SimpleLDAPObject class which knows about DN
objects (and possilby other IPA specific types that are universally
used in IPA). Then IPAEntrySimpleLDAPObject would subclass
IPASimpleLDAPObject which knows about Entry objects.
The reason for the suggested class hierarchy is because DN objects will be
used whenever we talk to LDAP (in the future we may want to add other IPA
specific classes which will always be used). We don't add Entry support to
the the IPASimpleLDAPObject class because Entry objects are (currently)
only used in IPAdmin.
What this patch does is:
* Introduce IPASimpleLDAPObject derived from
SimpleLDAPObject. IPASimpleLDAPObject is DN object aware.
* Introduce IPAEntryLDAPObject derived from
IPASimpleLDAPObject. IPAEntryLDAPObject is Entry object aware.
* Derive IPAdmin from IPAEntryLDAPObject and remove the funky method
wrapping from IPAdmin.
* Code which called add_s() with an Entry or Entity object now calls
addEntry(). addEntry() always existed, it just wasn't always
used. add_s() had been modified to accept Entry or Entity object
(why didn't we just call addEntry()?). The add*() ldap routine in
IPAEntryLDAPObject have been subclassed to accept Entry and Entity
objects, but that should proably be removed in the future and just
use addEntry().
* Replace the call to ldap.initialize() in ldap2.create_connection()
with a class constructor for IPASimpleLDAPObject. The
ldap.initialize() is a convenience function in python-ldap, but it
always returns a SimpleLDAPObject created via the SimpleLDAPObject
constructor, thus ldap.initialize() did not allow subclassing, yet
has no particular ease-of-use advantage thus we better off using the
obvious class constructor mechanism.
* Fix the use of _handle_errors(), it's not necessary to construct an
empty dict to pass to it.
If we follow the standard class derivation pattern for ldap we can make us
of our own ldap utilities in a far easier, cleaner and more efficient
manner.
2011-09-26 16:33:32 -05:00
|
|
|
IPAEntryLDAPObject.__init__(self,'ldaps://%s' % format_netloc(self.host, self.port))
|
2008-09-12 11:36:04 -05:00
|
|
|
else:
|
2010-05-27 10:58:31 -05:00
|
|
|
if self.ldapi:
|
ticket #1870 - subclass SimpleLDAPObject
We use convenience types (classes) in IPA which make working with LDAP
easier and more robust. It would be really nice if the basic python-ldap
library understood our utility types and could accept them as parameters
to the basic ldap functions and/or the basic ldap functions returned our
utility types.
Normally such a requirement would trivially be handled in an object-
oriented language (which Python is) by subclassing to extend and modify
the functionality. For some reason we didn't do this with the python-ldap
classes.
python-ldap objects are primarily used in two different places in our
code, ipaserver.ipaldap.py for the IPAdmin class and in
ipaserver/plugins/ldap2.py for the ldap2 class's .conn member.
In IPAdmin we use a IPA utility class called Entry to make it easier to
use the results returned by LDAP. The IPAdmin class is derived from
python-ldap.SimpleLDAPObject. But for some reason when we added the
support for the use of the Entry class in SimpleLDAPObject we didn't
subclass SimpleLDAPObject and extend it for use with the Entry class as
would be the normal expected methodology in an object-oriented language,
rather we used an obscure feature of the Python language to override all
methods of the SimpleLDAPObject class by wrapping those class methods in
another function call. The reason why this isn't a good approach is:
* It violates object-oriented methodology.
* Other classes cannot be derived and inherit the customization (because
the method wrapping occurs in a class instance, not within the class
type).
* It's non-obvious and obscure
* It's inefficient.
Here is a summary of what the code was doing:
It iterated over every member of the SimpleLDAPObject class and if it was
callable it wrapped the method. The wrapper function tested the name of
the method being wrapped, if it was one of a handful of methods we wanted
to customize we modified a parameter and called the original method. If
the method wasn't of interest to use we still wrapped the method.
It was inefficient because every non-customized method (the majority)
executed a function call for the wrapper, the wrapper during run-time used
logic to determine if the method was being overridden and then called the
original method. So every call to ldap was doing extra function calls and
logic processing which for the majority of cases produced nothing useful
(and was non-obvious from brief code reading some methods were being
overridden).
Object-orientated languages have support built in for calling the right
method for a given class object that do not involve extra function call
overhead to realize customized class behaviour. Also when programmers look
for customized class behaviour they look for derived classes. They might
also want to utilize the customized class as the base class for their use.
Also the wrapper logic was fragile, it did things like: if the method name
begins with "add" I'll unconditionally modify the first and second
argument. It would be some much cleaner if the "add", "add_s", etc.
methods were overridden in a subclass where the logic could be seen and
where it would apply to only the explicit functions and parameters being
overridden.
Also we would really benefit if there were classes which could be used as
a base class which had specific ldap customization.
At the moment our ldap customization needs are:
1) Support DN objects being passed to ldap operations
2) Support Entry & Entity objects being passed into and returned from
ldap operations.
We want to subclass the ldap SimpleLDAPObject class, that is the base
ldap class with all the ldap methods we're using. IPASimpleLDAPObject
class would subclass SimpleLDAPObject class which knows about DN
objects (and possilby other IPA specific types that are universally
used in IPA). Then IPAEntrySimpleLDAPObject would subclass
IPASimpleLDAPObject which knows about Entry objects.
The reason for the suggested class hierarchy is because DN objects will be
used whenever we talk to LDAP (in the future we may want to add other IPA
specific classes which will always be used). We don't add Entry support to
the the IPASimpleLDAPObject class because Entry objects are (currently)
only used in IPAdmin.
What this patch does is:
* Introduce IPASimpleLDAPObject derived from
SimpleLDAPObject. IPASimpleLDAPObject is DN object aware.
* Introduce IPAEntryLDAPObject derived from
IPASimpleLDAPObject. IPAEntryLDAPObject is Entry object aware.
* Derive IPAdmin from IPAEntryLDAPObject and remove the funky method
wrapping from IPAdmin.
* Code which called add_s() with an Entry or Entity object now calls
addEntry(). addEntry() always existed, it just wasn't always
used. add_s() had been modified to accept Entry or Entity object
(why didn't we just call addEntry()?). The add*() ldap routine in
IPAEntryLDAPObject have been subclassed to accept Entry and Entity
objects, but that should proably be removed in the future and just
use addEntry().
* Replace the call to ldap.initialize() in ldap2.create_connection()
with a class constructor for IPASimpleLDAPObject. The
ldap.initialize() is a convenience function in python-ldap, but it
always returns a SimpleLDAPObject created via the SimpleLDAPObject
constructor, thus ldap.initialize() did not allow subclassing, yet
has no particular ease-of-use advantage thus we better off using the
obvious class constructor mechanism.
* Fix the use of _handle_errors(), it's not necessary to construct an
empty dict to pass to it.
If we follow the standard class derivation pattern for ldap we can make us
of our own ldap utilities in a far easier, cleaner and more efficient
manner.
2011-09-26 16:33:32 -05:00
|
|
|
IPAEntryLDAPObject.__init__(self,'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % "-".join(self.realm.split(".")))
|
2010-05-27 10:58:31 -05:00
|
|
|
else:
|
ticket #1870 - subclass SimpleLDAPObject
We use convenience types (classes) in IPA which make working with LDAP
easier and more robust. It would be really nice if the basic python-ldap
library understood our utility types and could accept them as parameters
to the basic ldap functions and/or the basic ldap functions returned our
utility types.
Normally such a requirement would trivially be handled in an object-
oriented language (which Python is) by subclassing to extend and modify
the functionality. For some reason we didn't do this with the python-ldap
classes.
python-ldap objects are primarily used in two different places in our
code, ipaserver.ipaldap.py for the IPAdmin class and in
ipaserver/plugins/ldap2.py for the ldap2 class's .conn member.
In IPAdmin we use a IPA utility class called Entry to make it easier to
use the results returned by LDAP. The IPAdmin class is derived from
python-ldap.SimpleLDAPObject. But for some reason when we added the
support for the use of the Entry class in SimpleLDAPObject we didn't
subclass SimpleLDAPObject and extend it for use with the Entry class as
would be the normal expected methodology in an object-oriented language,
rather we used an obscure feature of the Python language to override all
methods of the SimpleLDAPObject class by wrapping those class methods in
another function call. The reason why this isn't a good approach is:
* It violates object-oriented methodology.
* Other classes cannot be derived and inherit the customization (because
the method wrapping occurs in a class instance, not within the class
type).
* It's non-obvious and obscure
* It's inefficient.
Here is a summary of what the code was doing:
It iterated over every member of the SimpleLDAPObject class and if it was
callable it wrapped the method. The wrapper function tested the name of
the method being wrapped, if it was one of a handful of methods we wanted
to customize we modified a parameter and called the original method. If
the method wasn't of interest to use we still wrapped the method.
It was inefficient because every non-customized method (the majority)
executed a function call for the wrapper, the wrapper during run-time used
logic to determine if the method was being overridden and then called the
original method. So every call to ldap was doing extra function calls and
logic processing which for the majority of cases produced nothing useful
(and was non-obvious from brief code reading some methods were being
overridden).
Object-orientated languages have support built in for calling the right
method for a given class object that do not involve extra function call
overhead to realize customized class behaviour. Also when programmers look
for customized class behaviour they look for derived classes. They might
also want to utilize the customized class as the base class for their use.
Also the wrapper logic was fragile, it did things like: if the method name
begins with "add" I'll unconditionally modify the first and second
argument. It would be some much cleaner if the "add", "add_s", etc.
methods were overridden in a subclass where the logic could be seen and
where it would apply to only the explicit functions and parameters being
overridden.
Also we would really benefit if there were classes which could be used as
a base class which had specific ldap customization.
At the moment our ldap customization needs are:
1) Support DN objects being passed to ldap operations
2) Support Entry & Entity objects being passed into and returned from
ldap operations.
We want to subclass the ldap SimpleLDAPObject class, that is the base
ldap class with all the ldap methods we're using. IPASimpleLDAPObject
class would subclass SimpleLDAPObject class which knows about DN
objects (and possilby other IPA specific types that are universally
used in IPA). Then IPAEntrySimpleLDAPObject would subclass
IPASimpleLDAPObject which knows about Entry objects.
The reason for the suggested class hierarchy is because DN objects will be
used whenever we talk to LDAP (in the future we may want to add other IPA
specific classes which will always be used). We don't add Entry support to
the the IPASimpleLDAPObject class because Entry objects are (currently)
only used in IPAdmin.
What this patch does is:
* Introduce IPASimpleLDAPObject derived from
SimpleLDAPObject. IPASimpleLDAPObject is DN object aware.
* Introduce IPAEntryLDAPObject derived from
IPASimpleLDAPObject. IPAEntryLDAPObject is Entry object aware.
* Derive IPAdmin from IPAEntryLDAPObject and remove the funky method
wrapping from IPAdmin.
* Code which called add_s() with an Entry or Entity object now calls
addEntry(). addEntry() always existed, it just wasn't always
used. add_s() had been modified to accept Entry or Entity object
(why didn't we just call addEntry()?). The add*() ldap routine in
IPAEntryLDAPObject have been subclassed to accept Entry and Entity
objects, but that should proably be removed in the future and just
use addEntry().
* Replace the call to ldap.initialize() in ldap2.create_connection()
with a class constructor for IPASimpleLDAPObject. The
ldap.initialize() is a convenience function in python-ldap, but it
always returns a SimpleLDAPObject created via the SimpleLDAPObject
constructor, thus ldap.initialize() did not allow subclassing, yet
has no particular ease-of-use advantage thus we better off using the
obvious class constructor mechanism.
* Fix the use of _handle_errors(), it's not necessary to construct an
empty dict to pass to it.
If we follow the standard class derivation pattern for ldap we can make us
of our own ldap utilities in a far easier, cleaner and more efficient
manner.
2011-09-26 16:33:32 -05:00
|
|
|
IPAEntryLDAPObject.__init__(self,'ldap://%s' % format_netloc(self.host, self.port))
|
2008-09-12 11:36:04 -05:00
|
|
|
|
2010-05-27 10:58:31 -05:00
|
|
|
def __init__(self,host='',port=389,cacert=None,bindcert=None,bindkey=None,proxydn=None,debug=None,ldapi=False,realm=None):
|
2008-09-12 11:36:04 -05:00
|
|
|
"""We just set our instance variables and wrap the methods - the real
|
2008-10-01 20:00:13 -05:00
|
|
|
work is done in __localinit. This is separated out this way so
|
|
|
|
that we can call it from places other than instance creation
|
|
|
|
e.g. when we just need to reconnect
|
|
|
|
"""
|
2008-09-12 11:36:04 -05:00
|
|
|
if debug and debug.lower() == "on":
|
|
|
|
ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
|
|
|
|
if cacert is not None:
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,cacert)
|
|
|
|
if bindcert is not None:
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_CERTFILE,bindcert)
|
|
|
|
if bindkey is not None:
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey)
|
|
|
|
|
|
|
|
self.port = port
|
|
|
|
self.host = host
|
|
|
|
self.cacert = cacert
|
|
|
|
self.bindcert = bindcert
|
|
|
|
self.bindkey = bindkey
|
|
|
|
self.proxydn = proxydn
|
2010-05-27 10:58:31 -05:00
|
|
|
self.ldapi = ldapi
|
|
|
|
self.realm = realm
|
2008-09-12 11:36:04 -05:00
|
|
|
self.suffixes = {}
|
2011-05-19 21:30:53 -05:00
|
|
|
self.schema = None
|
2008-09-12 11:36:04 -05:00
|
|
|
self.__localinit()
|
|
|
|
|
2009-04-28 16:05:39 -05:00
|
|
|
def __lateinit(self):
|
|
|
|
"""
|
|
|
|
This is executed after the connection is bound to fill in some useful
|
|
|
|
values.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
ent = self.getEntry('cn=config,cn=ldbm database,cn=plugins,cn=config',
|
|
|
|
ldap.SCOPE_BASE, '(objectclass=*)',
|
|
|
|
[ 'nsslapd-directory' ])
|
|
|
|
|
|
|
|
self.dbdir = os.path.dirname(ent.getValue('nsslapd-directory'))
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e)
|
2009-04-28 16:05:39 -05:00
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
def __str__(self):
|
|
|
|
return self.host + ":" + str(self.port)
|
|
|
|
|
|
|
|
def __get_server_controls(self):
|
|
|
|
"""Create the proxy user server control. The control has the form
|
|
|
|
0x04 = Octet String
|
|
|
|
4|0x80 sets the length of the string length field at 4 bytes
|
|
|
|
the struct() gets us the length in bytes of string self.proxydn
|
|
|
|
self.proxydn is the proxy dn to send"""
|
|
|
|
|
|
|
|
if self.proxydn is not None:
|
|
|
|
proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
|
|
|
|
|
|
|
|
# Create the proxy control
|
|
|
|
sctrl=[]
|
|
|
|
sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
|
|
|
|
else:
|
|
|
|
sctrl=None
|
|
|
|
|
|
|
|
return sctrl
|
|
|
|
|
2009-04-20 12:58:26 -05:00
|
|
|
def __handle_errors(self, e, **kw):
|
|
|
|
"""
|
|
|
|
Centralize error handling in one place.
|
|
|
|
|
|
|
|
e is the error to be raised
|
|
|
|
**kw is an exception-specific list of options
|
|
|
|
"""
|
|
|
|
if not isinstance(e,ldap.TIMEOUT):
|
|
|
|
desc = e.args[0]['desc'].strip()
|
|
|
|
info = e.args[0].get('info','').strip()
|
|
|
|
else:
|
|
|
|
desc = ''
|
|
|
|
info = ''
|
|
|
|
|
|
|
|
try:
|
|
|
|
# re-raise the error so we can handle it
|
|
|
|
raise e
|
|
|
|
except ldap.NO_SUCH_OBJECT, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = kw.get('arg_desc', "entry not found")
|
|
|
|
raise errors.NotFound(reason=arg_desc)
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.ALREADY_EXISTS, e:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.DuplicateEntry()
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.CONSTRAINT_VIOLATION, e:
|
|
|
|
# This error gets thrown by the uniqueness plugin
|
2011-11-01 07:58:05 -05:00
|
|
|
if info.startswith('Another entry with the same attribute value already exists'):
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.DuplicateEntry()
|
2009-04-20 12:58:26 -05:00
|
|
|
else:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.DatabaseError(desc=desc,info=info)
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.INSUFFICIENT_ACCESS, e:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.ACIError(info=info)
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.NO_SUCH_ATTRIBUTE:
|
|
|
|
# this is raised when a 'delete' attribute isn't found.
|
|
|
|
# it indicates the previous attribute was removed by another
|
|
|
|
# update, making the oldentry stale.
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.MidairCollision()
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.ADMINLIMIT_EXCEEDED, e:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.LimitsExceeded()
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.SIZELIMIT_EXCEEDED, e:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.LimitsExceeded()
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.TIMELIMIT_EXCEEDED, e:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.LimitsExceeded()
|
2009-04-20 12:58:26 -05:00
|
|
|
except ldap.LDAPError, e:
|
2009-04-23 07:51:59 -05:00
|
|
|
raise errors.DatabaseError(desc=desc,info=info)
|
2009-04-20 12:58:26 -05:00
|
|
|
|
2011-12-08 05:34:36 -06:00
|
|
|
def __wait_for_connection(self, timeout):
|
|
|
|
lurl = ldapurl.LDAPUrl(self._uri)
|
|
|
|
if lurl.urlscheme == 'ldapi':
|
|
|
|
installutils.wait_for_open_socket(lurl.hostport, timeout)
|
|
|
|
else:
|
|
|
|
(host,port) = lurl.hostport.split(':')
|
|
|
|
installutils.wait_for_open_ports(host, int(port), timeout)
|
|
|
|
|
|
|
|
def __bind_with_wait(self, bind_func, timeout, *args, **kwargs):
|
|
|
|
try:
|
|
|
|
bind_func(*args, **kwargs)
|
|
|
|
except (ldap.CONNECT_ERROR, ldap.SERVER_DOWN), e:
|
2012-01-26 15:32:29 -06:00
|
|
|
if not timeout or 'TLS' in e.args[0].get('info', ''):
|
|
|
|
# No connection to continue on if we have a TLS failure
|
|
|
|
# https://bugzilla.redhat.com/show_bug.cgi?id=784989
|
2011-12-08 05:34:36 -06:00
|
|
|
raise e
|
|
|
|
try:
|
|
|
|
self.__wait_for_connection(timeout)
|
|
|
|
except:
|
|
|
|
raise e
|
|
|
|
bind_func(*args, **kwargs)
|
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
def toLDAPURL(self):
|
2011-09-30 03:09:55 -05:00
|
|
|
return "ldap://%s/" % format_netloc(self.host, self.port)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
def set_proxydn(self, proxydn):
|
|
|
|
self.proxydn = proxydn
|
|
|
|
|
|
|
|
def set_krbccache(self, krbccache, principal):
|
2009-04-20 12:58:26 -05:00
|
|
|
try:
|
|
|
|
if krbccache is not None:
|
|
|
|
os.environ["KRB5CCNAME"] = krbccache
|
2011-02-25 17:37:45 -06:00
|
|
|
self.sasl_interactive_bind_s("", SASL_AUTH)
|
2009-04-20 12:58:26 -05:00
|
|
|
self.principal = principal
|
|
|
|
self.proxydn = None
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
2011-12-08 05:34:36 -06:00
|
|
|
def do_simple_bind(self, binddn="cn=directory manager", bindpw="", timeout=DEFAULT_TIMEOUT):
|
2008-09-12 11:36:04 -05:00
|
|
|
self.binddn = binddn
|
|
|
|
self.bindpwd = bindpw
|
2011-12-08 05:34:36 -06:00
|
|
|
self.__bind_with_wait(self.simple_bind_s, timeout, binddn, bindpw)
|
2009-04-28 16:05:39 -05:00
|
|
|
self.__lateinit()
|
2008-09-12 11:36:04 -05:00
|
|
|
|
2011-12-08 05:34:36 -06:00
|
|
|
def do_sasl_gssapi_bind(self, timeout=DEFAULT_TIMEOUT):
|
|
|
|
self.__bind_with_wait(self.sasl_interactive_bind_s, timeout, '', SASL_AUTH)
|
2011-02-25 17:37:45 -06:00
|
|
|
self.__lateinit()
|
|
|
|
|
2011-12-08 05:34:36 -06:00
|
|
|
def do_external_bind(self, user_name=None, timeout=DEFAULT_TIMEOUT):
|
2010-05-27 10:58:31 -05:00
|
|
|
auth_tokens = ldap.sasl.external(user_name)
|
2011-12-08 05:34:36 -06:00
|
|
|
self.__bind_with_wait(self.sasl_interactive_bind_s, timeout, '', auth_tokens)
|
2010-05-27 10:58:31 -05:00
|
|
|
self.__lateinit()
|
|
|
|
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
def getEntry(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
|
2008-09-12 11:36:04 -05:00
|
|
|
"""This wraps the search function. It is common to just get one entry"""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
|
|
|
|
try:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
res = self.search(base, scope, filterstr, attrlist, attrsonly)
|
2008-09-12 11:36:04 -05:00
|
|
|
objtype, obj = self.result(res)
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'base="%s", scope=%s, filterstr="%s"' % (base, scope, filterstr)
|
|
|
|
self.__handle_errors(e, arg_desc=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
if not obj:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'base="%s", scope=%s, filterstr="%s"' % (base, scope, filterstr)
|
|
|
|
raise errors.NotFound(reason=arg_desc)
|
2008-10-18 00:25:50 -05:00
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
elif isinstance(obj,Entry):
|
|
|
|
return obj
|
|
|
|
else: # assume list/tuple
|
|
|
|
return obj[0]
|
|
|
|
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
def getList(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
|
2008-09-12 11:36:04 -05:00
|
|
|
"""This wraps the search function to find multiple entries."""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
|
|
|
|
try:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
res = self.search(base, scope, filterstr, attrlist, attrsonly)
|
2008-09-12 11:36:04 -05:00
|
|
|
objtype, obj = self.result(res)
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'base="%s", scope=%s, filterstr="%s"' % (base, scope, filterstr)
|
|
|
|
self.__handle_errors(e, arg_desc=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
if not obj:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'base="%s", scope=%s, filterstr="%s"' % (base, scope, filterstr)
|
|
|
|
raise errors.NotFound(reason=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
entries = []
|
|
|
|
for s in obj:
|
|
|
|
entries.append(s)
|
|
|
|
|
|
|
|
return entries
|
|
|
|
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
def getListAsync(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0,
|
|
|
|
serverctrls=None, clientctrls=None, timeout=-1, sizelimit=0):
|
2008-09-12 11:36:04 -05:00
|
|
|
"""This version performs an asynchronous search, to allow
|
|
|
|
results even if we hit a limit.
|
|
|
|
|
|
|
|
It returns a list: counter followed by the results.
|
|
|
|
If the results are truncated, counter will be set to -1.
|
|
|
|
"""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
|
|
|
|
entries = []
|
|
|
|
partial = 0
|
|
|
|
|
|
|
|
try:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
msgid = self.search_ext(base, scope, filterstr, attrlist, attrsonly,
|
|
|
|
serverctrls, clientctrls, timeout, sizelimit)
|
2008-09-12 11:36:04 -05:00
|
|
|
objtype, result_list = self.result(msgid, 0)
|
|
|
|
while result_list:
|
|
|
|
for result in result_list:
|
|
|
|
entries.append(result)
|
|
|
|
objtype, result_list = self.result(msgid, 0)
|
|
|
|
except (ldap.ADMINLIMIT_EXCEEDED, ldap.SIZELIMIT_EXCEEDED,
|
|
|
|
ldap.TIMELIMIT_EXCEEDED), e:
|
|
|
|
partial = 1
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'base="%s", scope=%s, filterstr="%s", timeout=%s, sizelimit=%s' % \
|
|
|
|
(base, scope, filterstr, timeout, sizelimit)
|
|
|
|
self.__handle_errors(e, arg_desc=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
if not entries:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'base="%s", scope=%s, filterstr="%s"' % (base, scope, filterstr)
|
|
|
|
raise errors.NotFound(reason=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
if partial == 1:
|
|
|
|
counter = -1
|
|
|
|
else:
|
|
|
|
counter = len(entries)
|
|
|
|
|
|
|
|
return [counter] + entries
|
|
|
|
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
def addEntry(self, entry):
|
2008-09-12 11:36:04 -05:00
|
|
|
"""This wraps the add function. It assumes that the entry is already
|
|
|
|
populated with all of the desired objectclasses and attributes"""
|
|
|
|
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
if not isinstance(entry, (Entry, Entity)):
|
|
|
|
raise TypeError('addEntry expected an Entry or Entity object, got %s instead' % entry.__class__)
|
|
|
|
|
2008-09-12 11:36:04 -05:00
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
|
|
|
|
try:
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.add_s(entry.dn, entry.toTupleList())
|
2008-09-12 11:36:04 -05:00
|
|
|
except ldap.LDAPError, e:
|
2012-05-11 09:59:56 -05:00
|
|
|
arg_desc = 'entry=%s' % (entry.toTupleList())
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e, arg_desc=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
return True
|
|
|
|
|
|
|
|
def updateRDN(self, dn, newrdn):
|
|
|
|
"""Wrap the modrdn function."""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
|
|
|
|
if dn == newrdn:
|
|
|
|
# no need to report an error
|
|
|
|
return True
|
|
|
|
|
|
|
|
try:
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
self.modrdn_s(dn, newrdn, delold=1)
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e)
|
2008-09-12 11:36:04 -05:00
|
|
|
return True
|
|
|
|
|
|
|
|
def updateEntry(self,dn,oldentry,newentry):
|
|
|
|
"""This wraps the mod function. It assumes that the entry is already
|
|
|
|
populated with all of the desired objectclasses and attributes"""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
|
|
|
|
modlist = self.generateModList(oldentry, newentry)
|
|
|
|
|
|
|
|
if len(modlist) == 0:
|
2008-10-13 13:58:08 -05:00
|
|
|
raise errors.EmptyModlist
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
try:
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
self.modify_s(dn, modlist)
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e)
|
2008-09-12 11:36:04 -05:00
|
|
|
return True
|
|
|
|
|
|
|
|
def generateModList(self, old_entry, new_entry):
|
|
|
|
"""A mod list generator that computes more precise modification lists
|
2011-05-19 21:30:53 -05:00
|
|
|
than the python-ldap version. For single-value attributes always
|
|
|
|
use a REPLACE operation, otherwise use ADD/DEL.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Some attributes, like those in cn=config, need to be replaced
|
|
|
|
# not deleted/added.
|
2012-03-22 16:19:01 -05:00
|
|
|
FORCE_REPLACE_ON_UPDATE_ATTRS = ('nsslapd-ssl-check-hostname', 'nsslapd-lookthroughlimit', 'nsslapd-idlistscanlimit', 'nsslapd-anonlimitsdn', 'nsslapd-minssf-exclude-rootdse')
|
2008-09-12 11:36:04 -05:00
|
|
|
modlist = []
|
|
|
|
|
|
|
|
old_entry = ipautil.CIDict(old_entry)
|
|
|
|
new_entry = ipautil.CIDict(new_entry)
|
|
|
|
|
|
|
|
keys = set(map(string.lower, old_entry.keys()))
|
|
|
|
keys.update(map(string.lower, new_entry.keys()))
|
|
|
|
|
|
|
|
for key in keys:
|
|
|
|
new_values = new_entry.get(key, [])
|
|
|
|
if not(isinstance(new_values,list) or isinstance(new_values,tuple)):
|
|
|
|
new_values = [new_values]
|
|
|
|
new_values = filter(lambda value:value!=None, new_values)
|
|
|
|
|
|
|
|
old_values = old_entry.get(key, [])
|
|
|
|
if not(isinstance(old_values,list) or isinstance(old_values,tuple)):
|
|
|
|
old_values = [old_values]
|
|
|
|
old_values = filter(lambda value:value!=None, old_values)
|
|
|
|
|
2012-02-01 12:10:09 -06:00
|
|
|
# We used to convert to sets and use difference to calculate
|
|
|
|
# the changes but this did not preserve order which is important
|
|
|
|
# particularly for schema
|
|
|
|
adds = [x for x in new_values if x not in old_values]
|
|
|
|
removes = [x for x in old_values if x not in new_values]
|
2008-09-12 11:36:04 -05:00
|
|
|
|
2011-05-19 21:30:53 -05:00
|
|
|
if len(adds) == 0 and len(removes) == 0:
|
|
|
|
continue
|
|
|
|
|
|
|
|
is_single_value = self.get_single_value(key)
|
|
|
|
force_replace = False
|
|
|
|
if key in FORCE_REPLACE_ON_UPDATE_ATTRS or is_single_value:
|
|
|
|
force_replace = True
|
|
|
|
|
2011-04-05 15:28:59 -05:00
|
|
|
# You can't remove schema online. An add will automatically
|
|
|
|
# replace any existing schema.
|
|
|
|
if old_entry.get('dn') == 'cn=schema':
|
|
|
|
if len(adds) > 0:
|
|
|
|
modlist.append((ldap.MOD_ADD, key, adds))
|
|
|
|
else:
|
2011-05-19 21:30:53 -05:00
|
|
|
if adds:
|
|
|
|
if force_replace:
|
|
|
|
modlist.append((ldap.MOD_REPLACE, key, adds))
|
|
|
|
else:
|
|
|
|
modlist.append((ldap.MOD_ADD, key, adds))
|
|
|
|
if removes:
|
|
|
|
if not force_replace:
|
|
|
|
modlist.append((ldap.MOD_DELETE, key, removes))
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
return modlist
|
|
|
|
|
|
|
|
def inactivateEntry(self,dn,has_key):
|
|
|
|
"""Rather than deleting entries we mark them as inactive.
|
|
|
|
has_key defines whether the entry already has nsAccountlock
|
|
|
|
set so we can determine which type of mod operation to run."""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
modlist=[]
|
|
|
|
|
|
|
|
if has_key:
|
|
|
|
operation = ldap.MOD_REPLACE
|
|
|
|
else:
|
|
|
|
operation = ldap.MOD_ADD
|
|
|
|
|
2011-07-07 10:58:18 -05:00
|
|
|
modlist.append((operation, "nsAccountlock", "TRUE"))
|
2008-09-12 11:36:04 -05:00
|
|
|
|
|
|
|
try:
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
self.modify_s(dn, modlist)
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e)
|
2008-09-12 11:36:04 -05:00
|
|
|
return True
|
|
|
|
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
def deleteEntry(self, dn):
|
2008-09-12 11:36:04 -05:00
|
|
|
"""This wraps the delete function. Use with caution."""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
|
|
|
|
try:
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.delete_s(dn)
|
2008-09-12 11:36:04 -05:00
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
arg_desc = 'dn=%s' % (dn)
|
|
|
|
self.__handle_errors(e, arg_desc=arg_desc)
|
2008-09-12 11:36:04 -05:00
|
|
|
return True
|
|
|
|
|
|
|
|
def modifyPassword(self,dn,oldpass,newpass):
|
|
|
|
"""Set the user password using RFC 3062, LDAP Password Modify Extended
|
2008-10-18 00:25:50 -05:00
|
|
|
Operation. This ends up calling the IPA password slapi plugin
|
2008-09-12 11:36:04 -05:00
|
|
|
handler so the Kerberos password gets set properly.
|
|
|
|
|
|
|
|
oldpass is not mandatory
|
|
|
|
"""
|
|
|
|
|
|
|
|
sctrl = self.__get_server_controls()
|
|
|
|
|
|
|
|
try:
|
|
|
|
if sctrl is not None:
|
|
|
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
|
|
|
self.passwd_s(dn, oldpass, newpass)
|
|
|
|
except ldap.LDAPError, e:
|
Ticket #1879 - IPAdmin undefined anonymous parameter lists
The IPAdmin class in ipaserver/ipaldap.py has methods with anonymous
undefined parameter lists.
For example:
def getList(self,*args):
In Python syntax this means you can call getList with any positional
parameter list you want.
This is bad because:
1) It's not true, *args gets passed to an ldap function with a well
defined parameter list, so you really do have to call it with a
defined parameter list. *args will let you pass anything, but once it
gets passed to the ldap function it will blow up if the parameters do
not match (what parameters are those you're wondering? see item 2).
2) The programmer does not know what the valid parameters are unless
they are defined in the formal parameter list.
3) Without a formal parameter list automatic documentation generators
cannot produce API documentation (see item 2)
4) The Python interpreter cannot validate the parameters being passed
because there is no formal parameter list. Note, Python does not
validate the type of parameters, but it does validate the correct
number of postitional parameters are passed and only defined keyword
parameters are passed. Bypassing the language support facilities leads
to programming errors.
5) Without a formal parameter list program checkers such as pylint
cannot validate the program which leads to progamming errors.
6) Without a formal parameter list which includes default keyword
parameters it's not possible to use keyword arguments nor to know what
their default values are (see item 2). One is forced to pass a keyword
argument as a positional argument, plus you must then pass every
keyword argument between the end of the positional argument list and
keyword arg of interest even of the other keyword arguments are not of
interest. This also demands you know what the default value of the
intermediate keyword arguments are (see item 2) and hope they don't
change.
Also the *args anonymous tuple get passed into the error handling code
so it can report what the called values were. But because the tuple is
anonymous the error handler cannot not describe what it was passed. In
addition the error handling code makes assumptions about the possible
contents of the anonymous tuple based on current practice instead of
actual defined values. Things like "if the number of items in the
tuple is 2 or less then the first tuple item must be a dn
(Distinguished Name)" or "if the number of items in the tuple is
greater than 2 then the 3rd item must be an ldap search filter". These
are constructs which are not robust and will fail at some point in the
future.
This patch also fixes the use of IPAdmin.addEntry(). It was sometimes
being called with (dn, modlist), sometimes a Entry object, or
sometimes a Entity object. Now it's always called with either a Entry
or Entity object and IPAdmin.addEntry() validates the type of the
parameter passed.
2011-09-26 09:39:15 -05:00
|
|
|
self.__handle_errors(e)
|
2008-09-12 11:36:04 -05:00
|
|
|
return True
|
|
|
|
|
2009-04-28 16:05:39 -05:00
|
|
|
def waitForEntry(self, dn, timeout=7200, attr='', quiet=True):
|
|
|
|
scope = ldap.SCOPE_BASE
|
|
|
|
filter = "(objectclass=*)"
|
|
|
|
attrlist = []
|
|
|
|
if attr:
|
|
|
|
filter = "(%s=*)" % attr
|
|
|
|
attrlist.append(attr)
|
|
|
|
timeout += int(time.time())
|
|
|
|
|
|
|
|
if isinstance(dn,Entry):
|
|
|
|
dn = dn.dn
|
|
|
|
|
|
|
|
# wait for entry and/or attr to show up
|
|
|
|
if not quiet:
|
|
|
|
sys.stdout.write("Waiting for %s %s:%s " % (self,dn,attr))
|
|
|
|
sys.stdout.flush()
|
|
|
|
entry = None
|
|
|
|
while not entry and int(time.time()) < timeout:
|
|
|
|
try:
|
|
|
|
entry = self.getEntry(dn, scope, filter, attrlist)
|
|
|
|
except ldap.NO_SUCH_OBJECT:
|
|
|
|
pass # no entry yet
|
|
|
|
except ldap.LDAPError, e: # badness
|
|
|
|
print "\nError reading entry", dn, e
|
|
|
|
break
|
|
|
|
if not entry:
|
|
|
|
if not quiet:
|
|
|
|
sys.stdout.write(".")
|
|
|
|
sys.stdout.flush()
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
if not entry and int(time.time()) > timeout:
|
|
|
|
print "\nwaitForEntry timeout for %s for %s" % (self,dn)
|
|
|
|
elif entry and not quiet:
|
|
|
|
print "\nThe waited for entry is:", entry
|
|
|
|
elif not entry:
|
|
|
|
print "\nError: could not read entry %s from %s" % (dn,self)
|
|
|
|
|
|
|
|
return entry
|
|
|
|
|
2011-04-21 15:43:10 -05:00
|
|
|
def checkTask(self, dn, dowait=False, verbose=False):
|
|
|
|
"""check task status - task is complete when the nsTaskExitCode attr
|
|
|
|
is set return a 2 tuple (true/false,code) first is false if task is
|
|
|
|
running, true if done - if true, second is the exit code - if dowait
|
|
|
|
is True, this function will block until the task is complete
|
|
|
|
"""
|
|
|
|
attrlist = ['nsTaskLog', 'nsTaskStatus', 'nsTaskExitCode', 'nsTaskCurrentItem', 'nsTaskTotalItems']
|
|
|
|
done = False
|
|
|
|
exitCode = 0
|
|
|
|
while not done:
|
|
|
|
try:
|
|
|
|
entry = self.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)", attrlist)
|
|
|
|
except errors.NotFound:
|
|
|
|
break
|
|
|
|
if verbose:
|
|
|
|
print entry
|
|
|
|
if entry.nsTaskExitCode:
|
|
|
|
exitCode = int(entry.nsTaskExitCode)
|
|
|
|
done = True
|
|
|
|
if dowait: time.sleep(1)
|
|
|
|
else: break
|
|
|
|
return (done, exitCode)
|
|
|
|
|
2011-05-19 21:30:53 -05:00
|
|
|
def get_schema(self):
|
|
|
|
"""
|
|
|
|
Retrieve cn=schema and convert it into a python-ldap schema
|
|
|
|
object.
|
|
|
|
"""
|
|
|
|
if self.schema:
|
|
|
|
return self.schema
|
|
|
|
schema = self.getEntry('cn=schema', ldap.SCOPE_BASE,
|
|
|
|
'(objectclass=*)', ['attributetypes', 'objectclasses'])
|
|
|
|
schema = schema.toDict()
|
|
|
|
self.schema = ldap.schema.SubSchema(schema)
|
|
|
|
return self.schema
|
|
|
|
|
|
|
|
def get_single_value(self, attr):
|
|
|
|
"""
|
|
|
|
Check the schema to see if the attribute is single-valued.
|
|
|
|
|
|
|
|
If the attribute is in the schema then returns True/False
|
|
|
|
|
|
|
|
If there is a problem loading the schema or the attribute is
|
|
|
|
not in the schema return None
|
|
|
|
"""
|
|
|
|
if not self.schema:
|
|
|
|
self.get_schema()
|
|
|
|
obj = self.schema.get_obj(ldap.schema.AttributeType, attr)
|
|
|
|
return obj and obj.single_value
|
|
|
|
|
2010-12-10 08:48:06 -06:00
|
|
|
def get_dns_sorted_by_length(self, entries, reverse=False):
|
|
|
|
"""
|
|
|
|
Sorts a list of entries [(dn, entry_attrs)] based on their DN.
|
|
|
|
Entries within the same node are not sorted in any meaningful way.
|
|
|
|
If Reverse is set to True, leaf entries are returned first. This is
|
|
|
|
useful to perform recursive deletes where you need to delete entries
|
|
|
|
starting from the leafs and go up to delete nodes only when all its
|
|
|
|
leafs are removed.
|
|
|
|
|
|
|
|
Returns a "sorted" dict keyed by dn lengths and corresponding list
|
|
|
|
of DNs.
|
|
|
|
{'1': [dn1, dn2, dn3], '2': [dn4, dn5], ..}
|
|
|
|
"""
|
|
|
|
|
|
|
|
res = dict()
|
|
|
|
|
|
|
|
for e in entries:
|
|
|
|
sdn = ldap.dn.str2dn(e.dn)
|
|
|
|
l = len(sdn)
|
|
|
|
if not l in res:
|
|
|
|
res[l] = []
|
|
|
|
res[l].append(e.dn)
|
|
|
|
|
|
|
|
keys = res.keys()
|
|
|
|
keys.sort(reverse=reverse)
|
|
|
|
|
|
|
|
return map(res.get, keys)
|