[Freeipa-devel] [PATCHES] Add new set of base classes for IPA plugins using LDAP.

Rob Crittenden rcritten at redhat.com
Mon Jun 8 15:05:25 UTC 2009


Pavel Zůna wrote:
> Rob Crittenden wrote:
>> Pavel Zuna wrote:
>>> Dear freeipa-devel,
>>> I know we agreed that we're going to post one patch per email, but it 
>>> this case I think it's justified. Patches 0001 through 0005 are 
>>> prerequisites for the new base classes. Before describing the 
>>> individual patches, let me explain a little bit of what I had in mind 
>>> when implementing this addition.
>>>
>>> For the last few months, I've been mostly working the new LDAP 
>>> backend and IPA plugins that use it (i.e. almost all IPA plugins). I 
>>> tried to make the new backend more powerful and flexible. 
>>> Unfortunately, to take full advantage of the new potential, old 
>>> plugins had to be rewritten. As there were several inconsistencies 
>>> across them (output, naming and sometimes even behavior) and about 
>>> half of them were still using old features from IPAv1, I thought that 
>>> some re-factoring couldn't hurt after all.
>>>
>>> After porting a few plugins to the new backend, I realized that most 
>>> of them were doing pretty much the same thing - manipulating LDAP 
>>> entries. The only difference was that some plugins were generating 
>>> attributes in specific ways based on user input. So, I figured that 
>>> most of the generic work could be done in one place and that's when I 
>>> started working on these new base classes.
>>>
>>> Let's take a quick look at them:
>>>
>>> LDAPObject
>>>  - extends frontend.Object
>>>  - represents a type of LDAP entry
>>>    (or sub-entry if a parent_key=True parameter is present)
>>>
>>> The following methods are to be used with LDAPObject, they are named 
>>> after the CRUD methods they extend with an 'LDAP' prefix. They take 1 
>>> argument (the primary key) in the case their LDAPObject is an entry 
>>> and 2 arguments (the parent and primary key, in that order) if their 
>>> LDAPObject is a subentry:
>>>
>>> LDAPCreate
>>> LDAPRetrieve
>>> LDAPUpdate
>>> LDAPDelete
>>>
>>> Then there's LDAPSearch, which is a bit special. It takes only the 
>>> optional criteria argument if its LDAPObject is an entry. If its 
>>> LDAPObject is a sub-entry then it requires the parent key as the 
>>> first argument. The search logic works as follows:
>>>
>>> 1) If there is no parameters (except for the parent key in the case 
>>> of sub-entries), all entries of the given type are returned.
>>>
>>> 2) If there is only the criteria parameter, all entries of the given 
>>> type with one or more of their default attributes containing the 
>>> value of criteria are returned.
>>>
>>> 3) If there is an options specified, all entries of the given type 
>>> with the corresponding attribute EQUAL to the value specified are 
>>> returned. If more than one options is specified this way, only 
>>> entries matching ALL the conditions are returned.
>>>
>>> 2) and 3) can be combined, so for example, we can make a search for 
>>> entries who have a specific attribute equal to 'foo' and some of 
>>> their other attributes containing 'bar'.
>>>
>>> All of these base classes implement a 'callback' method, that is 
>>> called just before invoking python-ldap through the backend. 
>>> Subclasses can use it to execute their specific code after all the 
>>> boring stuff has been done for them.
>>>
>>> In the future I'm planning to write more base classes for other uses 
>>> cases such as adding/removing values from multivalued attributes.
>>>
>>>
>>> Patches descriptions:
>>> ---------------------
>>>
>>> 0001: Make it possible for subclasses of Command and co. to use their 
>>> own __public__.
>>>
>>> The problem was with PluginProxy only taking the base class' 
>>> __public__ into consideration.
>>>
>>>
>>> 0002: Add parent_key to keyword arguments of Param base class.
>>>
>>>
>>> 0003: Add support for parent key in Object base class.
>>>
>>>
>>> 0004: Make CRUD base classes take parent key parameters into 
>>> consideration when
>>> generating args.
>>>
>>>
>>> 0005: Add delete_tree method to ldap2 allowing recursive deletes of 
>>> whole LDAP branches.
>>>
>>> Some plugins were doing this "manually", so I figured it might as 
>>> well be provided by the LDAP API.
>>>
>>>
>>> 0006: Add new set of base classes for plugins using LDAP.
>>>
>>>
>>> To see the new bases classes in action, take a look at the next patch 
>>> I'm about to send to freeipa-devel.
>>
>> A few comments.
>>
>> I'd like Jason to comment on patch 0001 and 0002. It looks ok to me 
>> but he may balk at changing __public__.
>>
>> In LDAPObject you use a variable named keys. It isn't immediately 
>> clear what this is for (made clear after reading get_dn). If I 
>> understand parent_key and keys you are providing a general way of 
>> constructing the DN of the record, right? I'm not sure that parent_key 
>> and keys are very descriptive. It seems like primary_key is the rdn, 
>> parent_key is the next value in the DN and keys can contain either 
>> primary_key or both primary_key and parent_key. This is particularly 
>> confusing if you aren't using LDAPObject as a base class. Why would 
>> you want to return parent_key instead of primary_key when getting the 
>> arguments?
> I was trying to accomplish 2 things by introducing parent_key:
> 1) provide a generic way of constructing the DN
> 2) generate different arguments for CRUD methods of sub-entries
> 
> Some plugins have to deal with entries and their direct sub-entries (DNS 
> plugin: zone->resource, automount plugin: map->key). When accessing a 
> sub-entry, we need to know its RDN (primary_key), but also its parent 
> RDN (parent_key). parent_key is never returned instead of primary_key. 
> If present, parent_key is returned first followed by primary_key.

I think Jason picked primary_key as the main operating attribute both 
because of his background in SQL and his goal to make the framework not 
IPA-specific. At some point we may well extract the framework into a 
package that ships separately. At this early stage though it is simpler 
to have everything together.

I pondered on a better name before and punted, I couldn't think of 
anything either :-(

I'm wondering if we need a parent_primary_key variable though. Wordy as 
it is it does convey more clearly what it's for. Or it could be that as 
long as it is well-documented then it won't be a big deal.

Further, if you move it into the LDAPObject extension we can call it 
parent_rdn which is more descriptive.

> 
> You're right that it can be confusing when not using LDAPObject as a 
> base class, I'll change the CRUD base classes back and override 
> get_args() in LDAP* methods. 'parent_key' and 'keys' might not be the 
> most descriptive names, but I couldn't think of anything better - any 
> suggestions?
> 
>> I like the idea of a callback but is this limited by only returning 
>> the dn? What kinds of things do you envision this being used for? 
>> (e.g. what it if was smart enough to add in extra attributes the user 
>> didn't or couldn't ask for?).
> Actually they are smarter than they look. :) Since parameters in python 
> are passed by reference (well not exactly, it's actually pass by value, 
> but names are the values and names are like references...) we can modify 
> their contents in the callback. Unfortunately, this only applies to 
> mutable types, so DN/filter (immutable strings) have to be returned.

Ok, that's a good point. I think we'll need to document that this can 
happen so that installing some plugin later on doesn't cause unexpected 
side-effects because some expected keyword value is removed by the new 
plugin.

> 
>> BTW this sort of callback was always envisioned, I'm glad you're 
>> adding it. We had thought about having a pre- and post- callback so 
>> you could manage the data going in and coming out. What do you think?
> Sounds like a good idea.
> 
>> You define backend_name in LDAPObject but I don't see it used 
>> anywhere. What did you have in mind for this?
> backend_name is a forgotten feature I found in the depths of ipalib 
> code. When the Object instance is being initialized, its 'backend' 
> attribute is set to api.Backend.<backend_name>.

Ok, yeah we never could think what other backend there might be for us.

> 
>> If this is going to essentially be the base class for all LDAP-based 
>> plugins I think I'd like to see some more inline documentation so the 
>> output of epydoc will be helpful.
> I'll add it to my TODO list. I was thinking about writing a general 
> HOWTO for plugin authors anyway.

Sounds great.

> 
>> I wonder if delete_tree() isn't a bit dangerous. What if someone 
>> manages to pass in dc=example,dc=com?
> I can see your point. I'll remove delete_tree and rewrite LDAPDelete to 
> delete sub-entries "manually".

Ok.

> 
>> I think that ldap.passwd_s() will blow up if you pass in None as the 
>> old password.
> Probably, old pass is supposed to default to '' not None...

Right. You can try it but I seem to recall bad things happening either 
in python 2.4 or 2.5 or both when None was passed in.

> 
>> You made some changes to the modlist generator. Are you sure that this:
>>
>>     modlist.append((_ldap.MOD_DELETE, k, None))
>>
>> will work with python-ldap found on older systems (e.g. RHEL 5)?
> To be honest, I don't know. python-ldap docs say nothing about older 
> versions, but after some googling, I found code dating 2003 that was 
> using this.

Alright, this just looks really strange :-)

> 
>> I guess the basegroup class could be re-based to work on this and be 
>> even simpler :-)
> Yeah, most plugins could be re-based on this and would become a lot 
> simpler. :)
> 
>> Something that this will need at some point is a pointer to where in 
>> cn=ipaconfig the list of default objectclasses can be found. We may 
>> not end up defining this for every single object type but I think for 
>> many we will (users and groups at least). It would be nice if there 
>> was a way to just pass in an attribute name where this list can be found.
> Ok, that should be pretty straight forward.
> 
>> rob
> 
> I'm going to fix everything discussed in this e-mail and if it gets the 
> green light from Jason, we might finally be ready to make the LDAP 
> backend switch (at least for plugins).
> 
> Pavel

Have you run the tests against this backend? They may need to be tuned a 
bit I suppose but in general they should be able to identify problem areas.

rob

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 3245 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20090608/9d5ed838/attachment.bin>


More information about the Freeipa-devel mailing list