ucsschool.importer.utils package

Contents

ucsschool.importer.utils package#

Submodules#

ucsschool.importer.utils.computer_import module#

ucsschool.importer.utils.computer_import.call_hook(lo: access, meth_name: str, obj: SchoolComputer, line: List[str]) None[source]#
ucsschool.importer.utils.computer_import.get_ip_iface(ip_address: str) IPv4Interface[source]#
ucsschool.importer.utils.computer_import.mac_address_is_used(mac_address: str, lo: access) bool[source]#

ucsschool.importer.utils.computer_pyhook module#

Base class for all Python based Computer hooks.

class ucsschool.importer.utils.computer_pyhook.ComputerPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Base class for Python based computer import hooks.

The base class’ __init__() provides the following attributes:

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

Parameters:
priority: Dict[str, int | None] = {'post_create': None, 'pre_create': None}#
pre_create(computer: SchoolComputer, row: List[str]) None[source]#

Run code before creating a computer.

  • The computer does not exist in LDAP, yet.

  • computer.dn is the future DN of the computer,

  • set priority[“pre_create”] to an int, to enable this method

Parameters:
  • computer (SchoolComputer) – Computer (subclass of SchoolComputer)

  • row (List[str]) – the CSV line (split by the separator)

Returns:

None

post_create(computer: SchoolComputer, row: List[str]) None[source]#

Run code after creating a computer.

  • The hook is only executed if adding the computer succeeded.

  • computer will be an SchoolComputer, loaded from LDAP.

  • set priority[“post_create”] to an int, to enable this method

Parameters:
  • computer (SchoolComputer) – Computer (subclass of SchoolComputer)

  • row (List[str]) – the CSV line (split by the separator)

Returns:

None

ucsschool.importer.utils.config_pyhook module#

Base class for configuration hooks.

class ucsschool.importer.utils.config_pyhook.ConfigPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Hook to manipulate the configuration after reading the configuration files and applying the command line arguments.

The base class’ __init__() provides the following attributes:

  • self.dry_run # whether hook is executed during a dry-run (1)

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

(1) Hooks are only executed during dry-runs, if the class attribute supports_dry_run is set to True (default is False). Hooks with supports_dry_run == True must not modify LDAP objects. Therefore the LDAP connection object self.lo will be a read-only connection during a dry-run. (2) Read-write cn=admin connection in a real run, read-only cn=admin connection during a dry-run.

Parameters:
priority = {'post_config_files_read': None}#
post_config_files_read(config: ReadOnlyDict, used_conffiles: List[str], used_kwargs: Dict[str, Any]) ReadOnlyDict[source]#

Hook that runs after reading the configuration files used_conffiles and applying the command line arguments used_kwargs. Resulting configuration is config, which can be manipulated and must be returned.

Parameters:
  • config (ReadOnlyDict) – configuration that will be used by the import if not modified here, not yet read-only.

  • used_conffiles (list) – configuration files read and applied

  • used_kwargs (dict) – command line options read and applied

Returns:

config dict

Return type:

ReadOnlyDict

ucsschool.importer.utils.configuration_checks module#

Configuration checks.

After the configuration has been read, checks run.

To add your own checks, subclass ConfigurationChecks, save the module in /usr/share/ucs-school-import/checks and add its module name (without .py) to the list in the configuration key configuration_checks.

Remove defaults from your configuration_checks only if you know what you are doing.


Example: Save the following to /usr/share/ucs-school-import/checks/mychecks.py:

>>> from ucsschool.importer.exceptions import InitialisationError
>>> from ucsschool.importer.utils.configuration_checks import ConfigurationChecks
>>>
>>> class MyConfigurationChecks(ConfigurationChecks):
>>>     def test_nonzero_deactivation_grace(self):
>>>             if self.config.get('deletion_grace_period', {}).get('deactivation', 0) == 0:
>>>                     raise InitialisationError('deletion_grace_period:deactivation must not be zero.')

Then add a configuration entry to /var/lib/ucs-school-import/configs/user_import.json:

{
[..]
    "configuration_checks": ["defaults", "mychecks"]
}
class ucsschool.importer.utils.configuration_checks.ConfigurationChecks(config: ReadOnlyDict)[source]#

Bases: object

Base class for configuration checks.

Provides the configuration singleton in self.config, a read-only LDAP connection object in self.lo and a logging instance in self.logger.

All methods with names starting with test_ will be executed in alphanumerical order. Failing tests should raise a py:exception:ucsschool.importer.exceptions.InitialisationError exception.

ucsschool.importer.utils.constants module#

ucsschool.importer.utils.constants.get_default_prefixlen()[source]#
ucsschool.importer.utils.constants.get_sep_char()[source]#

ucsschool.importer.utils.format_pyhook module#

Base class for all Python based format hooks.

class ucsschool.importer.utils.format_pyhook.FormatPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Format hook base class

See ImportPyHook base class for documentation regarding the class’ attributes.

Parameters:
priority = {'patch_fields_staff': None, 'patch_fields_student': None, 'patch_fields_teacher': None, 'patch_fields_teacher_and_staff': None}#
properties = ()#
patch_fields_staff(property_name, fields)[source]#

Run code before formatting an property using a schema in format_from_scheme().

Parameters:
  • property_name (str) – Name of property_name that will be formatted

  • fields (dict) – dictionary with the users attributes and udm_properties

Returns:

fields dictionary that be used by format_from_scheme()

Return type:

dict

patch_fields_student(property_name, fields)[source]#

Run code before formatting an property using a schema in format_from_scheme().

Parameters:
  • property_name (str) – Name of property_name that will be formatted

  • fields (dict) – dictionary with the users attributes and udm_properties

Returns:

fields dictionary that be used by format_from_scheme()

Return type:

dict

patch_fields_teacher(property_name, fields)[source]#

Run code before formatting an property using a schema in format_from_scheme().

Parameters:
  • property_name (str) – Name of property_name that will be formatted

  • fields (dict) – dictionary with the users attributes and udm_properties

Returns:

fields dictionary that be used by format_from_scheme()

Return type:

dict

patch_fields_teacher_and_staff(property_name, fields)[source]#

Run code before formatting a property using a schema in format_from_scheme().

Parameters:
  • property_name (str) – Name of property_name that will be formatted

  • fields (dict) – dictionary with the users attributes and udm_properties

Returns:

fields dictionary that be used by format_from_scheme()

Return type:

dict

ucsschool.importer.utils.import_pyhook module#

Base class for all Python based import hooks.

class ucsschool.importer.utils.import_pyhook.ImportPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: PyHook

Base class for Python based import hooks.

  • self.dry_run # whether hook is executed during a dry-run (1)

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

(1) Hooks are only executed during dry-runs, if the class attribute supports_dry_run is set to True (default is False). Hooks with supports_dry_run == True must not modify LDAP objects. Therefore the LDAP connection object self.lo will be a read-only connection during a dry-run. (2) Read-write cn=admin connection in a real run, read-only cn=admin connection during a dry-run.

Parameters:
supports_dry_run = False#
dry_run#

Whether this is a dry-run

lo: univention.admin.uldap.access#

LDAP connection object

logger: logging.Logger#

Python logging instance

class ucsschool.importer.utils.import_pyhook.ImportPyHookLoader(pyhooks_base_path: str)[source]#

Bases: object

Load and initialize hooks.

If hooks should be instantiated with arguments, use init_hook() before call_hook().

init_hook(hook_cls: Type[ImportPyHookTV] | str, filter_func: Callable[[Type[ImportPyHookTV]], bool] | None = None, *args: Any, **kwargs: Any) Dict[str, List[Callable[[...], Any]]][source]#

Load and initialize hook class hook_cls.

Parameters:
  • hook_cls (ucsschool.importer.utils.import_pyhook.ImportPyHook or str) – class object - load and run hooks that are a subclass of this

  • filter_func (callable or None) – function to filter out undesired hook classes (takes a class and returns a bool), passed to PyHooksLoader

  • args (tuple) – arguments to pass to __init__ of hooks

  • kwargs (dict) – arguments to pass to __init__ of hooks

Returns:

mapping from method names to list of methods of initialized hook objects, sorted by method priority

Return type:

dict[str, list[callable]]

call_hooks(hook_cls: Type[ImportPyHookTV], func_name: str, *args: Any, **kwargs: Any) List[Any][source]#

Run hooks with name func_name from class hook_cls.

Parameters:
  • hook_cls – class object - load and run hooks that are a subclass of this

  • func_name (str) – name of method to run in each hook

  • args – arguments to pass to hook function

  • kwargs – arguments to pass to hook function

Returns:

list of when all executed hooks returned

Return type:

list

ucsschool.importer.utils.import_pyhook.get_import_pyhooks(hook_cls: Type[ImportPyHookTV] | str, filter_func: Callable[[Type[ImportPyHookTV]], bool] | None = None, *args: Any, **kwargs: Any) Dict[str, List[Callable[[...], Any]]][source]#

Retrieve (and initialize subclasses of hook_cls, if not yet done) pyhooks of type hook_cls. Results are cached.

If no argument must be passed to the hook_cls __init__() or PyHooksLoader, then it is not necessary to call this function, just use run_import_pyhooks() directly.

Convenience function for easy usage of PyHooksLoader.

Parameters:
  • hook_cls (ucsschool.importer.utils.import_pyhook.ImportPyHook or str) – class object or fully dotted Python path to a class definition - load and run hooks that are a subclass of this

  • filter_func (callable or None) – function to filter out undesired hook classes (takes a class and returns a bool), passed to PyHooksLoader

  • args – arguments to pass to __init__ of hooks

  • kwargs – arguments to pass to __init__ of hooks

Returns:

mapping from method names to list of methods of initialized hook objects, sorted by method priority

Return type:

dict[str, list[callable]]

ucsschool.importer.utils.import_pyhook.run_import_pyhooks(hook_cls: Type[ImportPyHookTV] | str, func_name: str, *args: Any, **kwargs: Any) List[Any][source]#

Execute method func_name of subclasses of hook_cls, load and initialize if required.

Convenience function for easy usage of PyHooksLoader.

Parameters:
  • hook_cls (ucsschool.importer.utils.import_pyhook.ImportPyHook or str) – class object or fully dotted Python path to a class definition - load and run hooks that are a subclass of this

  • func_name (str) – name of method to run in each hook

  • args – arguments to pass to hook function func_name

  • kwargs – arguments to pass to hook function func_name

Returns:

list of when all executed hooks returned

Return type:

list

ucsschool.importer.utils.ldap_connection module#

Create LDAP connections for import.

ucsschool.importer.utils.ldap_connection.get_admin_connection() Tuple[LoType, PoType][source]#

Read-write cn=admin connection.

Return type:

tuple(univention.admin.uldap.access, univention.admin.uldap.position)

ucsschool.importer.utils.ldap_connection.get_machine_connection() Tuple[LoType, PoType][source]#

Read-write machine connection.

Return type:

tuple(univention.admin.uldap.access, univention.admin.uldap.position)

ucsschool.importer.utils.ldap_connection.get_unprivileged_connection() Tuple[LoType, PoType][source]#

Unprivileged read-write connection.

Return type:

tuple(univention.admin.uldap.access, univention.admin.uldap.position)

class ucsschool.importer.utils.ldap_connection.ReadOnlyAccess(*args, **kwargs)[source]#

Bases: access

LDAP access class that prevents LDAP write access.

Must be a descendant of univention.admin.uldap.access, or UDM will raise a TypeError.

Parameters:
  • host – The hostname of the LDAP server.

  • port – The TCP port number of the LDAP server.

  • base – The base distinguished name.

  • binddn – The distinguished name of the account.

  • bindpw – The user password for simple authentication.

  • start_tls – Negotiate TLS with server. If 2 is given, the command will require the operation to be successful.

  • loLDAP connection.

  • follow_referral – Follow LDAP referrals.

  • uri – LDAP connection string.

add(*args, **kwargs)[source]#

Add LDAP entry at distinguished name and attributes in add_list=(attribute-name, old-values. new-values) or (attribute-name, new-values).

Parameters:
  • dn (str) – The distinguished name of the object to add.

  • al – The add-list of 2-tuples (attribute-name, new-values).

  • exceptions (bool) – Raise the low level exception instead of the wrapping UDM exceptions.

  • serverctrls – a list of ldap.controls.LDAPControl instances sent to the server along with the LDAP request

  • ignore_license (bool) – Ignore license check if True.

  • response (dict) – An optional dictionary to receive the server controls of the result.

Raises:
modify(*args, **kwargs)[source]#

Modify LDAP entry DN with attributes in changes=(attribute-name, old-values, new-values).

Parameters:
  • dn (str) – The distinguished name of the object to modify.

  • changes – The modify-list of 3-tuples (attribute-name, old-values, new-values).

  • exceptions (bool) – Raise the low level exception instead of the wrapping UDM exceptions.

  • ignore_license (bool) – Ignore license check if True.

  • serverctrls – a list of ldap.controls.LDAPControl instances sent to the server along with the LDAP request

  • response (dict) – An optional dictionary to receive the server controls of the result.

Returns:

The distinguished name.

rename(*args, **kwargs)[source]#

Rename a LDAP object.

Parameters:
  • dn – The old distinguished name of the object to rename.

  • newdn – The new distinguished name of the object to rename.

  • move_childs – Also rename the sub children. Must be 0 always as 1 is not implemented.

  • ignore_license – Ignore license check if True.

  • serverctrls – a list of ldap.controls.LDAPControl instances sent to the server along with the LDAP request

  • response – An optional dictionary to receive the server controls of the result.

delete(*args, **kwargs)[source]#

Delete a LDAP object.

Parameters:
  • dn (str) – The distinguished name of the object to remove.

  • exceptions (bool) – Raise the low level exception instead of the wrapping UDM exceptions.

Raises:
ucsschool.importer.utils.ldap_connection.get_readonly_connection() Tuple[LoType, PoType][source]#

Read-only cn=admin connection.

Return type:

tuple(univention.admin.uldap.access, univention.admin.uldap.position)

ucsschool.importer.utils.logging module#

Central place to get logger for import.

ucsschool.importer.utils.logging.get_logger() Logger[source]#

Get a logging instance with name ucsschool.

Deprecated since version 4.4: v2 Use logging.getLogger(__name__) and get_stream_handler(), get_file_handler().

Returns:

Logger

Return type:

logging.Logger

ucsschool.importer.utils.logging.make_stdout_verbose() Logger[source]#
ucsschool.importer.utils.logging.add_file_handler(filename: str, uid: int | None = None, gid: int | None = None, mode: int | None = None) Logger[source]#
ucsschool.importer.utils.logging.move_our_handlers_to_lib_logger() None[source]#

Move logging handlers from ucsschool.import to ucsschool logger.

Deprecated since version 4.4: v2 Use logging.getLogger(__name__) and get_stream_handler(), get_file_handler() for the logger hierarchie required.

ucsschool.importer.utils.post_read_pyhook module#

Base class for all Python based Post-Read-Pyhooks.

class ucsschool.importer.utils.post_read_pyhook.PostReadPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Hook that is called directly after data has been read from CSV/…

The base class’ __init__() provides the following attributes:

  • self.dry_run # whether hook is executed during a dry-run (1)

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

(1) Hooks are only executed during dry-runs, if the class attribute supports_dry_run is set to True (default is False). Hooks with supports_dry_run == True must not modify LDAP objects. Therefore the LDAP connection object self.lo will be a read-only connection during a dry-run. (2) Read-write cn=admin connection in a real run, read-only cn=admin connection during a dry-run.

Parameters:
priority = {'all_entries_read': None, 'entry_read': None}#
entry_read(entry_count, input_data, input_dict)[source]#

Run code after an entry has been read and saved in input_data and input_dict. This hook may alter input_data and input_dict to modify the input data. This function may raise the exception UcsSchoolImportSkipImportRecord to ignore the read import data.

Parameters:
  • entry_count (int) – index of the data entry (e.g. line of the CSV file)

  • input_data (list[str]) – input data as raw as possible (e.g. raw CSV columns). The input_data may be changed.

  • input_dict (dict[str, str]) – input data mapped to column names. The input_dict may be changed.

Returns:

None

Raises:

UcsSchoolImportSkipImportRecord – if an entry (e.g. a CSV line) should be skipped

all_entries_read(imported_users, errors)[source]#

Run code after all entries have been read. ImportUser objects for all lines are passed to the hook. Also errors are passed. Please note that the “entry_read” hook method may skip one or several input records, so they may be missing in imported_users. errors contains a list of catched errors/exceptions.

Parameters:
  • imported_users (list[ImportUser]) – list of ImportUser objects created from the input records

  • errors (list[Exception]) – list of exceptions that are caught during processing the input records

Returns:

None

ucsschool.importer.utils.pre_read_pyhook module#

Base class for all Python based pre-read hooks.

class ucsschool.importer.utils.pre_read_pyhook.PreReadPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = False, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Hook that is called before starting to read the input file.

The base class’ __init__() provides the following attributes:

  • self.dry_run # whether hook is executed during a dry-run (1)

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

  • self.config # read-only import configuration

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

(1) Hooks are only executed during dry-runs, if the class attribute supports_dry_run is set to True (default is False). Hooks with supports_dry_run == True must not modify LDAP objects. Therefore the LDAP connection object self.lo will be a read-only connection during a dry-run. (2) Read-write cn=admin connection in a real run, read-only cn=admin connection during a dry-run.

Parameters:
priority = {'pre_read': None}#
pre_read() None[source]#

Run code before starting to read the input file.

Returns:

None

ucsschool.importer.utils.result_pyhook module#

Base class for all Python based Result-Pyhooks.

class ucsschool.importer.utils.result_pyhook.ResultPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Hook that is called after import has finished.

The base class’ __init__() provides the following attributes:

  • self.dry_run # whether hook is executed during a dry-run (1)

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

(1) Hooks are only executed during dry-runs, if the class attribute supports_dry_run is set to True (default is False). Hooks with supports_dry_run == True must not modify LDAP objects. Therefore the LDAP connection object self.lo will be a read-only connection during a dry-run. (2) Read-write cn=admin connection in a real run, read-only cn=admin connection during a dry-run.

Parameters:
priority = {'user_result': None}#
user_result(user_import_data: UserImportData) None[source]#

Run code after user import has finished. Relevant data from the UserImport class is passed to this hook, so result summaries etc are possible.

Parameters:

user_import_data (UserImportData) – relevant data from the UserImport class

Returns:

None

ucsschool.importer.utils.shell module#

Module to ease interactive use of import system.

ucsschool.importer.utils.test_user_creator module#

Class to create lots of test users.

class ucsschool.importer.utils.test_user_creator.TestUserCreator(ous, staff=0, students=0, teachers=0, staffteachers=0, classes=0, inclasses=2, schools=2, email=False)[source]#

Bases: object

static get_test_data(filename)[source]#
make_classes()[source]#
make_users()[source]#

ucsschool.importer.utils.user_pyhook module#

Base class for all Python based User hooks.

class ucsschool.importer.utils.user_pyhook.UserPyHook(lo: univention.admin.uldap.access | None = None, dry_run: bool | None = None, *args: Any, **kwargs: Any)[source]#

Bases: ImportPyHook

Base class for Python based user import hooks.

An example is provided in /usr/share/doc/ucs-school-import/hook_example.py

The base class’ __init__() provides the following attributes:

  • self.dry_run # whether hook is executed during a dry-run (1)

  • self.lo # LDAP connection object (2)

  • self.logger # Python logging instance

If multiple hook classes are found, hook functions with higher priority numbers run before those with lower priorities. None disables a function (no need to remove it / comment it out).

(1) Hooks are only executed during dry-runs, if the class attribute supports_dry_run is set to True (default is False). Hooks with supports_dry_run == True must not modify LDAP objects. Therefore the LDAP connection object self.lo will be a read-only connection during a dry-run. (2) Read-write cn=admin connection in a real run, read-only cn=admin connection during a dry-run.

Parameters:
priority: Dict[str, int | None] = {'post_create': None, 'post_modify': None, 'post_move': None, 'post_remove': None, 'pre_create': None, 'pre_modify': None, 'pre_move': None, 'pre_remove': None}#
pre_create(user: ImportUser) None[source]#

Run code before creating a user.

  • The user does not exist in LDAP, yet.

  • user.dn is the future DN of the user, if username and school does not change.

  • user.input_data contains the (csv) input data, if the user was create during an import job

  • set priority[“pre_create”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

post_create(user: ImportUser) None[source]#

Run code after creating a user.

  • The hook is only executed if adding the user succeeded.

  • user will be an ImportUser, loaded from LDAP.

  • Do not run user.modify(), it will create a recursion. Please use

    user.modify_without_hooks().

  • set priority[“post_create”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

pre_modify(user: ImportUser) None[source]#

Run code before modifying a user.

  • user will be a ImportUser, loaded from LDAP.

  • set priority[“pre_modify”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

post_modify(user: ImportUser) None[source]#

Run code after modifying a user.

  • The hook is only executed if modifying the user succeeded.

  • user will be an ImportUser, loaded from LDAP.

  • Do not run user.modify(), it will create a recursion. Please use

    user.modify_without_hooks().

  • If running in an import job, the user may not have been removed, but merely deactivated. If

    user.udm_properties[“ucsschoolPurgeTimestamp”] is set, the user is marked for removal.

  • set priority[“post_modify”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

pre_move(user: ImportUser) None[source]#

Run code before changing a users primary school (position).

  • user will be an ImportUser, loaded from LDAP.

  • set priority[“pre_move”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

post_move(user: ImportUser) None[source]#

Run code after changing a users primary school (position).

  • The hook is only executed if moving the user succeeded.

  • user will be an ImportUser, loaded from LDAP.

  • Do not run user.modify(), it will create a recursion. Please use

    user.modify_without_hooks().

  • set priority[“post_move”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

pre_remove(user: ImportUser) None[source]#

Run code before deleting a user.

  • user will be an ImportUser, loaded from LDAP.

  • set priority[“pre_remove”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

post_remove(user: ImportUser) None[source]#

Run code after deleting a user.

  • The hook is only executed if the deleting the user succeeded.

  • user will be an ImportUser, loaded from LDAP.

  • The user was removed, do not try to modify() it.

  • set priority[“post_remove”] to an int, to enable this method

Parameters:

user (ImportUser) – User (or a subclass of it, eg. ImportUser)

Returns:

None

ucsschool.importer.utils.username_handler module#

Create historically unique usernames/email addresses.

class ucsschool.importer.utils.username_handler.NameCounterStorageBackend[source]#

Bases: object

create(name: str, value: int) None[source]#

Store a value for a new name.

Parameters:
  • name (str) – name

  • value (int) – value to store

Returns:

None

Raises:

NameKeyExists – if a value is already stored to such a name

modify(name: str, old_value: int, new_value: int) None[source]#

Store a value for an existing name.

Parameters:
  • name (str) – name

  • old_value (int) – old value

  • new_value (int) – new value

Returns:

None

Raises:

NameKeyExists – if no value is stored by that name

retrieve(name: str) int[source]#

Retrieve a value for a name.

Parameters:

name (str) – name to retrieve

Returns:

int

Raises:
remove(name: str) None[source]#

Remove a name from storage.

Parameters:

name (str) – name

Returns:

None

purge() None[source]#

Remove all names from storage. NEVER do this in a production environment!

Returns:

None

class ucsschool.importer.utils.username_handler.LdapStorageBackend(attribute_storage_name: str, lo: univention.admin.uldap.access | None = None, pos: univention.admin.uldap.position | None = None)[source]#

Bases: NameCounterStorageBackend

Prior to using this class, a node must exist in LDAP: ‘cn=unique-<attribute_name>,cn=ucsschool,cn=univention,<base>’.

create(name: str, value: int) None[source]#

Store a value for a new name.

Parameters:
  • name (str) – name

  • value (int) – value to store

Returns:

None

Raises:

NameKeyExists – if a value is already stored to such a name

modify(name: str, old_value: int, new_value: int) None[source]#

Store a value for an existing name.

Parameters:
  • name (str) – name

  • old_value (int) – old value

  • new_value (int) – new value

Returns:

None

Raises:

NameKeyExists – if no value is stored by that name

retrieve(name: str) int[source]#

Retrieve a value for a name.

Parameters:

name (str) – name to retrieve

Returns:

int

Raises:
remove(name: str) None[source]#

Remove a name from storage.

Parameters:

name (str) – name

Returns:

None

purge() None[source]#

Remove all names from storage. NEVER do this in a production environment!

Returns:

None

class ucsschool.importer.utils.username_handler.MemoryStorageBackend(attribute_storage_name: str)[source]#

Bases: NameCounterStorageBackend

create(name: str, value: int) None[source]#

Store a value for a new name.

Parameters:
  • name (str) – name

  • value (int) – value to store

Returns:

None

Raises:

NameKeyExists – if a value is already stored to such a name

modify(name: str, old_value: int, new_value: int) None[source]#

Store a value for an existing name.

Parameters:
  • name (str) – name

  • old_value (int) – old value

  • new_value (int) – new value

Returns:

None

Raises:

NameKeyExists – if no value is stored by that name

retrieve(name: str) int[source]#

Retrieve a value for a name.

Parameters:

name (str) – name to retrieve

Returns:

int

Raises:
remove(name: str) None[source]#

This will remove the key only from memory. It may still be stored in the LDAP backend.

Parameters:

name (str) – name

Returns:

None

purge() None[source]#

This will remove keys only from memory. They may still be stored in the LDAP backend.

Returns:

None

class ucsschool.importer.utils.username_handler.UsernameHandler(max_length: int, dry_run: bool = True)[source]#

Bases: object

>>> BAD_CHARS = ''.join(sorted(set(map(chr, range(128))) - set(UsernameHandler(20).allowed_chars)))
>>> UsernameHandler(20).format_username('Max.Mustermann')
'Max.Mustermann'
>>> UsernameHandler(20).format_username('Foo[COUNTER2][COUNTER2]')
Traceback (most recent call last):
...
FormatError:
>>> UsernameHandler(20).format_username('.')
''
>>> UsernameHandler(20).format_username('.Max.Mustermann.')
'Max.Mustermann'
>>> UsernameHandler(4).format_username('Max.Mustermann')
'Max'
>>> for c in BAD_CHARS:
...  assert 'Max' == UsernameHandler(20).format_username('Ma%sx' % (c,))
...
>>> UsernameHandler(20).format_username('Max.Mustermann12.4567890')
'Max.Mustermann12.456'
>>> for c in '.1032547698ACBEDGFIHKJMLONQPSRUTWVYXZacbedgfihkjmlonqpsrutwvyxz':
...  assert 'Ma%sx' % (c,) == UsernameHandler(20).format_username('Ma%sx' % (c,))
...
>>> UsernameHandler(20).format_username('Max[Muster]Mann')
'MaxMusterMann'
>>> UsernameHandler(20).format_username('Max[ALWAYSCOUNTER].Mustermann')
'Max1.Mustermann'
>>> UsernameHandler(20).format_username('Max[ALWAYSCOUNTER].Mustermann')
'Max2.Mustermann'
>>> UsernameHandler(20).format_username('Max[ALWAYSCOUNTER].Mustermann')
'Max3.Mustermann'
>>> UsernameHandler(20).format_username('Max[COUNTER2].Mustermann')
'Max4.Mustermann'
>>> UsernameHandler(20).format_username('Maria[ALWAYSCOUNTER].Musterfrau')
'Maria1.Musterfrau'
>>> UsernameHandler(20).format_username('Moritz[COUNTER2]')
'Moritz'
>>> UsernameHandler(20).format_username('Moritz[COUNTER2]')
'Moritz2'
>>> UsernameHandler(20).format_username('Foo[ALWAYSCOUNTER]')
'Foo1'
>>> for i, c in enumerate(BAD_CHARS + BAD_CHARS, 2):
...  username = UsernameHandler(20).format_username('Fo%so[ALWAYSCOUNTER]' % (c,))
...  assert 'Foo%d' % (i,) == username, (username, i, c)
>>> UsernameHandler(8).format_username('aaaa[COUNTER2]bbbbcccc')
'aaaab'
>>> UsernameHandler(8).format_username('aaaa[COUNTER2]bbbbcccc')
'aaaa2b'
>>> UsernameHandler(8).format_username('bbbb[ALWAYSCOUNTER]ccccdddd')
'bbbb1c'
>>> UsernameHandler(8).format_username('bbbb[ALWAYSCOUNTER]ccccdddd')
'bbbb2c'
>>> UsernameHandler(20).format_username('..[ALWAYSCOUNTER]..')
''
>>> UsernameHandler(20).format_username('[ALWAYSCOUNTER]')
''
>>> UsernameHandler(20).format_username('[FOObar]')
'FOObar'
Parameters:

max_length (int) – created usernames will be no longer

than this :param bool dry_run: if False use LDAP to store already-used usernames if True store for one run only in memory

attribute_name = 'username'#
attribute_storage_name = 'usernames'#
property allowed_chars: str#
get_storage_backend() NameCounterStorageBackend[source]#
Returns:

NameCounterStorageBackend instance

Return type:

NameCounterStorageBackend

remove_bad_chars(name: str) str[source]#

Remove characters disallowed for names. * Name must only contain numbers, letters and dots, and may not be ‘admin’! * Name must not start or end in a dot.

Parameters:

name (str) – name to check

Returns:

copy of input, possibly modified

Return type:

str

format_name(name: str, max_length: int | None = None) str[source]#

Create a username/email from <name>, possibly replacing a counter variable. * This is intended to be called before/by/after ImportUser.format_from_scheme(). * Supports inserting the counter anywhere in the name, as long as its length does not overflow max_length. * Only one counter variable is allowed. * Counters should run only to 999. The algorithm will not honor max_length for higher numbers! * Subclass->override counter_variable_to_function() and the called methods to support other schemes than [ALWAYSCOUNTER] and [COUNTER2] or change their meaning.

Parameters:
  • name (str) – username/email, possibly a template

  • max_length (int) – overwrite max length specified at object instanciation time

Returns:

unique name

Return type:

str

Raises:

FormatError – if more than one counter variable was found in the scheme

format_username(name: str) str[source]#

Deprecated method. Please use format_name() instead.

property counter_variable_to_function: Dict[str, Callable[[str], str]]#

Subclass->override this to support other variables than [ALWAYSCOUNTER] and [COUNTER2] or change their meaning. Add/Modify corresponding methods in your subclass. Variables have to start with ‘[’, end with ‘]’ and must be all upper case.

Returns:

mapping: variable name -> function

Return type:

dict

always_counter(name_base: str) str[source]#

[ALWAYSCOUNTER]

Parameters:

name_base (str) – the name without [ALWAYSCOUNTER]

Returns:

number to append to name

Return type:

str

counter2(name_base: str) str[source]#

[COUNTER2]

Parameters:

name_base (str) – the name without [COUNTER2]

Returns:

number to append to name

Return type:

str

get_and_raise(name_base: str, initial_value: str) str[source]#

Returns the current counter value or initial_value if unset and stores it raised by 1.

Parameters:
  • name_base (str) – name without []

  • initial_value (str) – lowest value

Returns:

current counter value

Return type:

str

class ucsschool.importer.utils.username_handler.EmailHandler(max_length: int = 254, dry_run: bool = True)[source]#

Bases: UsernameHandler

Create unique email addresses. * Maximum length of an email address is 254 characters. * Applies counters [ALWAYSCOUNTER/COUNTER2] to local part (left of @) only.

:param int max_length maximum length of email address :param bool dry_run: if False use LDAP to store already-used email addresses if True store for one run only in memory

attribute_name = 'email'#
attribute_storage_name = 'email'#
property allowed_chars: str#
remove_bad_chars(name: str) str[source]#

Space is actually allowed (inside a quoted string), but we’ll remove it anyway. (Although technically allowed, not all mail servers support it.)

format_name(name: str, max_length: int | None = None) str[source]#

Create a username/email from <name>, possibly replacing a counter variable. * This is intended to be called before/by/after ImportUser.format_from_scheme(). * Supports inserting the counter anywhere in the name, as long as its length does not overflow max_length. * Only one counter variable is allowed. * Counters should run only to 999. The algorithm will not honor max_length for higher numbers! * Subclass->override counter_variable_to_function() and the called methods to support other schemes than [ALWAYSCOUNTER] and [COUNTER2] or change their meaning.

Parameters:
  • name (str) – username/email, possibly a template

  • max_length (int) – overwrite max length specified at object instanciation time

Returns:

unique name

Return type:

str

Raises:

FormatError – if more than one counter variable was found in the scheme

ucsschool.importer.utils.utils module#

ucsschool.importer.utils.utils.get_ldap_mapping_for_udm_property(udm_prop, udm_type)[source]#

Get the name of the LDAP attribute, a UDM property is mapped to.

Parameters:
  • udm_prop (str) – name of UDM property

  • udm_type (str) – name of UDM module (e.g. ‘users/user’)

Returns:

name of LDAP attribute or empty str if no mapping was found

Return type:

str

ucsschool.importer.utils.utils.nullcontext()[source]#

Context manager that does nothing.

ucsschool.importer.utils.utils.verify_school_ou(school_nr, lo)[source]#