Hooks: modifying ObjectStore behavior
python-ldap-faker provides a hook system to allow you to arbitrarily modify
behavior of ObjectStore. Primarily this is provided so that you can
emulate the behavior of the various LDAP implementations (Redhat Directory
Server, Active Directory, openldap, etc.).
You can also use hooks in your test code to produce behavior that may not be
available out of the box from python-ldap-faker.
Rules about hooks:
Hooks are run in the order they are registered
Each hook needs a callable with a particular signature
Hooks are global – they apply to all
ObjectStoreinstances and instances instantiated (unless they are tagged hooks)
Registering hooks
Hooks have a name and a callable signature. Here is an example of registering a
hook to the pre_set hook, which will be run in ObjectStore.set
before the object is saved to the internal storage, and requires the callable
signature Callable[[ObjectStore, LDAPRecord, Optional[str]], None]:
from ldap_faker import hooks, ObjectStore, LDAPRecord
def pre_set_do_something_special(store: ObjectStore, record: LDAPRecord, bind_dn: str = None) -> None:
...
hooks.register('pre_set', pre_set_do_something_special)
Thereafter, whenever any code calls ObjectStore.set, this function
will be called with the store as the first argument, the record to be written as
the second argument and the bind_dn of the binding user as the third
argument.
Tagged hooks
Using tags, you can register a hook that will only apply to
ObjectStore instances which are themselves tagged with one of those
tags:
from ldap_faker import hooks, ObjectStore, LDAPRecord
def pre_set_do_something_special(store: ObjectStore, record: LDAPRecord, bind_dn: str = None) -> None:
print(f'{bind_dn} ran pre_set_do_something_sepcial')
hooks.register('pre_set', pre_set_do_something_special, tags=['special'])
This hook will only be executed for ObjectStore instances whose tags
include special:
>>> store = ObjectStore(tags=['special'])
>>> obj = ('mydn', {'objectclass': [b'top']))
>>> store.set(obj, bind_dn='auser')
auser ran pre_set_do_something_special
It will not be executed for ObjectStore instances whose tags do not
include special:
>>> store = ObjectStore(tags=['other'])
>>> obj = ('mydn', {'objectclass': [b'top']))
>>> store.set(obj, bind_dn='auser')
Tagging ObjectClass instances in LDAPFakerMixin
When using LDAPFakerMixin, you can tag ldap_fixtures with particular tags.
To tag the default “server”, specify the fixture as a 2-tuple, where the first element is the filename of the fixture file, and the second element is a list of tags:
import unittest
from ldap_faker import LDAPFakerMixin
class TestDefaultTaggedServer(LDAPFakerMixin, unittest.TestCase):
ldap_modules = ['myapp']
ldap_fixtures = ('data.json', ['special'])
To tag named “servers”, you can tag individual servers by providing a 3-tuple instad of a 2-tuple, where the third element is the list of tags:
import unittest
from ldap_faker import LDAPFakerMixin
class TestDefaultTaggedServer(LDAPFakerMixin, unittest.TestCase):
ldap_modules = ['myapp']
ldap_fixtures = [
('server1.json', 'ldap://server1', ['special']),
('server2.json', 'ldap://server2')
]
Above, ldap://server1 will use all hooks tagged with special in addition
to any untagged hooks, while ldap://server2 will use only the untagged
hooks.
Available hooks
pre_objectstore_initSignature:
Callable[[store: ObjectStore], None]Where
storeis theObjectStoreobject.This will be at the end of
ObjectStore.__init__.You can use this to set up any state you might need for later hooks by adding keys to
ObjectStore.controls, or to add attributes toObjectStore.operational_attributes.pre_setSignature:
Callable[[store: ObjectStore, record: LDAPRecord, bind_dn: Optional[str] = None], None]Where
storeis theObjectStoreobject,recordis the record to besetandbind_dnis the dn of the user doing theset(possiblyNone)This will be executed on
ObjectStore.setbefore the object actually gets saved.ObjectStore.setis called for every write operation:post_setSignature:
Callable[[store: ObjectStore, record: LDAPRecord, bind_dn: Optional[str] = None], None]Where
storeis theObjectStoreobject,recordis the record to besetandbind_dnis the dn of the user doing theset(possiblyNone).This will be executed on
ObjectStore.setafter the object gets saved.pre_copySignature:
Callable[[store: ObjectStore, dn: str], None]Where
storeis theObjectStoreobject, anddnis the DN of the object to copy.This will be executed on
ObjectStore.copybefore the object actually gets retrieved from the store to be copied.post_copySignature:
Callable[[store: ObjectStore, data: LDAPData], LDAPData]Where
storeis theObjectStoreobject, anddnis the DN of the object to copy. It should return the modifiedLDAPDatadict.This will be executed on
ObjectStore.copyafter the object is retrieved from the store and :py:func:copy.deepcopyhas run, but before returning the data to the caller.pre_createSignature:
Callable[[store: ObjectStore, dn: str, modlist: AddModlist, bind_dn: str = None], None]Where
storeis theObjectStoreobject,dnis the record to be created,modlistis modlist to be used for creating the record, andbind_dnis the dn of the user doing thecreate(possiblyNone).This will be executed on
ObjectStore.createbefore the modlist gets processed.ObjectStore.createis what actually does the work whenFakeLDAPObject.add_sis called.post_createSignature:
Callable[[store: ObjectStore, record: LDAPRecord, bind_dn: Optional[str] = None], None]Where
storeis theObjectStoreobject,recordis the record to be created, andbind_dnis the dn of the user doing thecreate(possiblyNone).This will be executed on
ObjectStore.createafter the modlist has processed to build the object, but before it has been writen to the data store.pre_updateSignature:
Callable[[store: ObjectStore, dn: str, modlist: Modlist, bind_dn: str = None], None]Where
storeis theObjectStoreobject,dnis the record to be modified`,modlistis modlist to be applied to the record, andbind_dnis the dn of the user doing theupdate(possiblyNone).This will be executed on
ObjectStore.updatebefore the object actually gets saved.ObjectStore.updateis what actually does the work whenFakeLDAPObject.modify_sis called.post_updateSignature:
Callable[[store: ObjectStore, record: LDAPRecord, bind_dn: Optional[str] = None], None]Where
storeis theObjectStoreobject,recordis the updated record andbind_dnis the dn of the user doing theupdate(possiblyNone)This will be executed on
ObjectStore.updateafter the modlist has been applied to the object, but before it has been writen to the data store.pre_deleteSignature:
Callable[[store: ObjectStore, record: LDAPRecord, bind_dn: Optional[str] = None], None]Where
storeis theObjectStoreobject,recordis the record to deleted, andbind_dnis the dn of the user doing theset(possiblyNone).This will be executed on
ObjectStore.deletebefore the object actually gets deleted from the data store.ObjectStore.deleteis what actually does the work whenFakeLDAPObject.delete_sis called, and is also called duringFakeLDAPObject.rename_sto delete the old object.post_deleteSignature:
Callable[[store: ObjectStore, record: LDAPRecord, bind_dn: Optional[str] = None], None]Where
storeis theObjectStoreobject,recordis the record deleted, andbind_dnis the dn of the user doing theset(possiblyNone).This will be executed on
ObjectStore.deleteafter the object actually gets deleted from the data store.pre_register_objectSignature:
Callable[[store: ObjectStore, record: LDAPRecord], None]Where
storeis theObjectStoreobject andrecordis the record to be registered.This will be executed on
ObjectStore.register_objectbefore the object actually gets saved.post_register_objectSignature:
Callable[[store: ObjectStore, record: LDAPRecord], None]Where
storeis theObjectStoreobject andrecordis the record that was registered.This will be executed on
ObjectStore.register_objectafter the object gets saved.pre_register_objectsSignature:
Callable[[store: ObjectStore, records: List[LDAPRecord]], None]Where
storeis theObjectStoreobject andrecordsis the list of records to be registered.This will be executed on
ObjectStore.register_objectsbefore the objects actually get saved.post_register_objectsSignature:
Callable[[store: ObjectStore, records: List[LDAPRecord]], None]Where
storeis theObjectStoreobject andrecordsare the records that were registered.This will be executed on
ObjectStore.register_objectsafter the objects get saved.pre_load_objectsSignature:
Callable[[store: ObjectStore, filename: str], None]Where
storeis theObjectStoreobject andfilenameis the name of the data file to load.This will be executed on
ObjectStore.load_objectsbefore the file gets loaded.post_load_objectsSignature:
Callable[[store: ObjectStore, records: List[LDAPRecord]], None]Where
storeis theObjectStoreobject andrecordsare the records that were loaded from the file.This will be executed on
ObjectStore.load_objectsafter the objects loaded from the file get saved.