UMC modules
Contents
UMC modules¶
Convenience decorators for developers of UMC modules¶
Functions exposed by UMC modules often share some logic. They check the existence and formatting of variables or check permissions. If anything fails, they react in a similar way. If everything is correct, the real logic is often as simple as returning one single value.
This module provides functions that can be used to separate repeating tasks from the actual business logic. This means:
less time to code
fewer bugs
consistent behavior throughout the UMC in standard cases
Note that the functions defined herein do not cover every corner case during UMC module development. You are not bound to use them if you need more flexibility.
- univention.management.console.modules.decorators.simple_response(function=None, with_flavor=None, with_progress=False)[source]¶
If your function is as simple as: “Just return some variables” this decorator is for you.
Instead of defining the function
def my_func(self, response): pass
you now define a function with the variables you would expect in request.options. Default values are supported:
@simple_response def my_func(self, var1, var2='default'): pass
The decorator extracts variables from request.options. If the variable is not found, it either returns a failure or sets it to a default value (if specified by you).
If you need to get the flavor passed to the function you can do it like this:
@simple_response(with_flavor=True) def my_func(self, flavor, var1, var2='default'): pass
With with_flavor set, the flavor is extracted from the request. You can also set with_flavor=’varname’, in which case the variable name for the flavor is varname. True means ‘flavor’. As with ordinary option arguments, you may specify a default value for flavor in the function definition:
@simple_response(with_flavor='module_flavor') def my_func(self, flavor='this comes from request.options', module_flavor='this is the flavor (and its default value)'): pass
Instead of stating at the end of your function
self.finished(request.id, some_value)
you now just
return some_value
Before:
def my_func(self, request): variable1 = request.options.get('variable1') variable2 = request.options.get('variable2') flavor = request.flavor or 'default flavor' if variable1 is None: self.finished(request.id, None, message='variable1 is required', success=False) return if variable2 is None: variable2 = '' try: value = '%s_%s_%s' % (self._saved_dict[variable1], variable2, flavor) except KeyError: self.finished(request.id, None, message='Something went wrong', success=False, status=500) return self.finished(request.id, value)
After:
@simple_response(with_flavor=True) def my_func(self, variable1, variable2='', flavor='default_flavor'): try: return '%s_%s_%s' % (self._saved_dict[variable1], variable2, flavor) except KeyError: raise UMC_Error('Something went wrong')
- univention.management.console.modules.decorators.multi_response(function=None, with_flavor=None, single_values=False, progress=False)[source]¶
This decorator acts similar to
simple_response()
but can handle a list of dicts instead of a single dict.Technically another object is passed to the function that you can name as you like. You can iterate over this object and get the values from each dictionary in request.options.
Default values and flavors are supported.
You do not return a value, you yield them (and you are supposed to yield!):
@multi_response def my_multi_func(self, iterator, variable1, variable2=''): # here, variable1 and variable2 are yet to be initialised # i.e. variable1 and variable2 will be None! do_some_initial_stuff() try: for variable1, variable2 in iterator: # now they are set yield '%s_%s' % (self._saved_dict[variable1], variable2) except KeyError: raise UMC_Error('Something went wrong') else: # only when everything went right... do_some_cleanup_stuff()
The above code will send a list of answers to the client as soon as the function is finished (i.e. after do_some_cleanup_stuff()) filled with values yielded.
If you have just one variable in your dictionary, do not forget to add a comma, otherwise Python will assign the first value a list of one element:
for var, in iterator: # now var is set correctly pass
- univention.management.console.modules.decorators.sanitize(*args, **kwargs)[source]¶
Decorator that lets you sanitize the user input.
The sanitize function can be used to validate the input as well as change it.
Note that changing a value here will actually alter the request object. This should be no problem though.
If the validation step fails an error will be passed to the user instead of executing the function. This step should not raise anything other than
ValidationError
orUnformattedValidationError
(one should use the methodraise_validation_error()
).You can find some predefined Sanitize classes in the corresponding module or you define one yourself, deriving it from
Sanitizer
:class SplitPathSanitizer(Sanitizer): def __init__(self): super(SplitPathSanitizer, self).__init__( validate_none=True, may_change_value=True) def _sanitize(self, value, name, further_fields): if value is None: return [] try: return value.split('/') except BaseException: self.raise_validation_error('Split failed')
Before:
def my_func(self, request): var1 = request.options.get('var1') var2 = request.options.get('var2', 20) try: var1 = int(var1) var2 = int(var2) except (ValueError, TypeError): self.finished(request.id, None, 'Cannot convert to int', status=400) return if var2 < 10: self.finished(request.id, None, 'var2 must be >= 10', status=400) return self.finished(request.id, var1 + var2)
After:
@sanitize( var1=IntegerSanitizer(required=True), var2=IntegerSanitizer(required=True, minimum=10, default=20) ) def add(self, request): var1 = request.options.get('var1') # could now use ['var1'] var2 = request.options.get('var2') self.finished(request.id, var1 + var2)
The decorator can be combined with other decorators like
simple_response()
(be careful with ordering of decorators here):@sanitize( var1=IntegerSanitizer(required=True), var2=IntegerSanitizer(required=True, minimum=10) ) @simple_response def add(self, var1, var2): return var1 + var2
Note that you lose the capability of specifying defaults in @simple_response. You need to do it in @sanitize now.
- univention.management.console.modules.decorators.log(function=None, sensitives=None, customs=None, single_values=False)[source]¶
Log decorator to be used with
simple_response()
:@simple_response @log def my_func(self, var1, var2): return "%s__%s" % (var1, var2)
The above example will write two lines into the logfile for the module (given that the UCR variable umc/module/debug/level is set to at least 3):
<date> MODULE ( INFO ) : my_func got: var1='value1', var2='value2' <date> MODULE ( INFO ) : my_func returned: 'value1__value2'
The variable names are ordered by appearance and hold the values that are actually going to be passed to the function (i.e. after they were
sanitize()
‘d or set to their default value). You may specify the names of sensitive arguments that should not show up in log files and custom functions that can alter the representation of a certain variable’s values (useful for non-standard datatypes like regular expressions - you may have used aPatternSanitizer
):@sanitize(pattern=PatternSanitizer()) @simple_reponse @log(sensitives=['password'], customs={'pattern':lambda x: x.pattern}) def count_ucr(self, username, password, pattern): return self._ucr_count(username, password, pattern)
This results in something like:
<date> MODULE ( INFO ) : count_ucr got: password='********', username='Administrator', pattern='.*' <date> MODULE ( INFO ) : count_ucr returned: 650
The decorator also works with
multi_response()
:@multi_response @log def multi_my_func(self, var1, var2): return "%s__%s" % (var1, var2)
This results in something like:
<date> MODULE ( INFO ) : multi_my_func got: [var1='value1', var2='value2'], [var1='value3', var2='value4'] <date> MODULE ( INFO ) : multi_my_func returned: ['value1__value2', 'value3__value4']
- univention.management.console.modules.decorators.file_upload(function)[source]¶
This decorator restricts requests to be UPLOAD-commands. Simple, yet effective
Sanitize classes for the sanitize decorator¶
This module provides the Sanitize base class as well as some important
and often used Sanitizers. They are used in the
sanitize
function.
If the provided classes do not meet your requirements you can easily
make one yourself.
The main job of sanitizers is to alter values if needed so that they cannot do something harmful in the exposed UMC-functions. But they are also very helpful when one needs to just validate input.
- exception univention.management.console.modules.sanitizers.UnformattedValidationError(msg, kwargs)[source]¶
Bases:
Exception
Unformatted error raised when the sanitizer finds a value he cannot use at all (e.g. letters when an int is expected). Should be “enhanced” to a ValidationError.
- exception univention.management.console.modules.sanitizers.ValidationError(msg, name, value)[source]¶
Bases:
Exception
Error raised when the sanitizer finds a value he cannot use at all (e.g. letters when an int is expected).
- exception univention.management.console.modules.sanitizers.MultiValidationError[source]¶
Bases:
univention.management.console.modules.sanitizers.ValidationError
Error used for validation of an arbitrary number of sanitizers. Used by
DictSanitizer
andListSanitizer
.- add_error(e, name)[source]¶
Adds a
ValidationError
- class univention.management.console.modules.sanitizers.Sanitizer(**kwargs)[source]¶
Bases:
object
Base class of all sanitizers.
For reasons of extensibility and for ease of subclassing, the parameters are **kwargs. But only the following are meaningful:
- Parameters
further_arguments (str) – names of arguments that should be passed along with the actual argument in order to return something reasonable. Default: None
required (bool) – if the argument is required. Default: False
default (object) – if argument is not given and not
required
, default is returned - even when notmay_change_value
. Note that this value is not passing the sanitizing procedure, so make sure to be able to handle it. Default: Nonemay_change_value (bool) – if the process of sanitizing is allowed to alter request.options. If not, the sanitizer can still be used for validation. Default: True
allow_none (bool) – if None is allowed and not further validated. Default: False
- sanitize(name, options)[source]¶
Sanitize function. Internally calls _sanitize with the correct values and returns the new value (together with a flag indicating whether the value was found at all). If you write your own Sanitize class, you probably want to override
_sanitize()
.- _sanitize(value, name, further_arguments)[source]¶
The method where the actual sanitizing takes place.
The standard method just returns value so be sure to override this method in your Sanitize class.
- Parameters
value (object) – the value as found in request.options.
name (str) – the name of the argument currently sanitized.
further_arguments (dict[str, object]) – dictionary holding the values of those additional arguments in request.options that are needed for sanitizing. the arguments come straight from the not altered options dict (i.e. before potentially changing sanitizing happened).
- raise_validation_error(msg, **kwargs)[source]¶
Used to more or less uniformly raise a
ValidationError
. This will actually raise anUnformattedValidationError
for your convenience. If used in_sanitize()
, it will be automatically enriched with name, value und formatting insanitize()
.- Parameters
**kwargs (dict) – additional arguments for formatting
- raise_formatted_validation_error(msg, name, value, **kwargs)[source]¶
Used to more or less uniformly raise a
ValidationError
. name and value need to passed because the sanitizer should be thread safe.
- class univention.management.console.modules.sanitizers.DictSanitizer(sanitizers, allow_other_keys=True, default_sanitizer=None, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
DictSanitizer makes sure that the value is a dict and sanitizes its fields.
You can give the same parameters as the base class. Plus:
- class univention.management.console.modules.sanitizers.ListSanitizer(sanitizer=None, min_elements=None, max_elements=None, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
ListSanitizer makes sure that the value is a list and sanitizes its elements.
You can give the same parameters as the base class. Plus:
- class univention.management.console.modules.sanitizers.BooleanSanitizer(**kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
BooleanSanitizer makes sure that the value is a bool. It converts other data types if possible.
- class univention.management.console.modules.sanitizers.IntegerSanitizer(minimum=None, maximum=None, minimum_strict=None, maximum_strict=None, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
IntegerSanitizer makes sure that the value is an int. It converts other data types if possible and is able to validate boundaries.
You can give the same parameters as the base class. Plus:
- class univention.management.console.modules.sanitizers.SearchSanitizer(**kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
Baseclass for other Sanitizers that are used for a simple search. That means that everything is escaped except for asterisks that are considered as wildcards for any number of characters. (If
use_asterisks
is True, which is default)Handles adding of asterisks and and some simple sanity checks. Real logic is done in a to-be-overridden method named
_escape_and_return()
.Currently used for
LDAPSearchSanitizer
andPatternSanitizer
.Like the Baseclass of all Sanitizers, it accepts only keyword-arguments (derived classes may vary). You may specify the same as in the Baseclass plus:
- Parameters
add_asterisks (bool) –
add asterisks at the beginning and the end of the value if needed. Examples:
”string” -> “*string*”
”” -> “*”
”string*” -> “string*”
Default: True
max_number_of_asterisks (int) – An error will be raised if the number of * in the string exceeds this limit. Useful because searching with too many of these patterns in a search query can be very expensive. Note that * from
add_asterisks
do count. None means an arbitrary number is allowed. Default: 5use_asterisks (bool) –
treat asterisks special, i.e. as a substring of arbitrary length. If False, it will be escaped as any other character. If False the defaults change:
add_asterisks
to Falsemax_number_of_asterisks
to None.
Default: True
- class univention.management.console.modules.sanitizers.LDAPSearchSanitizer(**kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.SearchSanitizer
Sanitizer for LDAP-Searches. Everything that could possibly confuse an LDAP-Search is escaped except for *.
- ESCAPED_WILDCARD = '\\2a'¶
- class univention.management.console.modules.sanitizers.PatternSanitizer(ignore_case=True, multiline=True, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.SearchSanitizer
PatternSanitizer converts the input into a regular expression. It can handle anything (through the inputs __str__ method), but only strings seem to make sense.
The input should be a string with asterisks (*) if needed. An askterisk stands for anything at any length (regular expression: .*).
The sanitizer escapes the input, replaces * with .* and applies the params.
You can give the same parameters as the base class.
If you specify a string as
default
, it will be compiled to a regular expression. Hints: default=’.*’ -> matches everything; default=’(?!)’ -> matches nothingPlus:
- class univention.management.console.modules.sanitizers.StringSanitizer(regex_pattern=None, re_flags=0, minimum=None, maximum=None, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
StringSanitizer makes sure that the input is a string. The input can be validated by a regular expression and by string length
- Parameters
regex_pattern (six.string_types or re._pattern_type) – a regex pattern or a string which will be compiled into a regex pattern
re_flags (int) – additional regex flags for the regex_pattern which will be compiled if
regex_pattern
is a stringminimum (int) – the minimum length of the string
maximum (int) – the maximum length of the string
- class univention.management.console.modules.sanitizers.DNSanitizer(regex_pattern=None, re_flags=0, minimum=None, maximum=None, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.StringSanitizer
DNSanitizer is a sanitizer that checks if the value has correct LDAP Distinguished Name syntax
- class univention.management.console.modules.sanitizers.EmailSanitizer(**kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.StringSanitizer
EmailSanitizer is a very simple sanitizer that checks the very basics of an email address: At least 3 characters and somewhere in the middle has to be an @-sign
- class univention.management.console.modules.sanitizers.ChoicesSanitizer(choices, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.Sanitizer
ChoicesSanitizer makes sure that the input is in a given set of choices.
- Parameters
choices (object) – the allowed choices used.
- class univention.management.console.modules.sanitizers.MappingSanitizer(mapping, **kwargs)[source]¶
Bases:
univention.management.console.modules.sanitizers.ChoicesSanitizer
MappingSanitizer makes sure that the input is in a key in a dictionary and returns the corresponding value.
- Parameters
mapping ({object : object}) – the dictionary that is used for sanitizing
Mixins for UMC module classes¶
This module provides some mixins that can be incorporated in existing UMC
modules. These mixins extend the functionality of the module in an easy-to-use
way. Just let your module derive from
Base
(as before) and the mixin
classes.
- class univention.management.console.modules.mixins.Progress(progress_id, title, total)[source]¶
Bases:
object
Class to keep track of the progress during execution of a function. Used internally.
- class univention.management.console.modules.mixins.ProgressMixin[source]¶
Bases:
object
Mixin to provide two new functions:
new_progress to create a new
Progress
.progress to let the client fetch the progress made up to this moment.
The progress function needs to be made public by the XML definition of the module. To use this mixin, just do:
class Instance(Base, ProgressMixin): pass