diff --git a/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py b/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py index aac8355..a13285d 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py @@ -527,6 +527,57 @@ def _ldap_pre_remove(self): def _ldap_post_remove(self): pass + def remove_value(self, key, value=None): + """Removes a specific value from the specified property. + If this value is currently not set univention.admin.uexceptions.valueNotSet is raised. + if value is None the whole values of this property gets removed. + """ + if not self.has_key(key): + raise univention.admin.uexceptions.noProperty(key) + + if value is None: + # remove all currently set values + self[key] = [] if self.descriptions[key].multivalue else None + return + + if not self.contains_value(key, value): + raise univention.admin.uexceptions.valueNotSet(key, value) + + if not self.descriptions[key].multivalue: + self[key] = value + return + + current_values = list(self[key]) + for val in current_values[:]: + if self.compare_value(key, value, val): + current_values.remove(val) + self[key] = current_values + + def contains_value(self, key, value): + if self.descriptions[key].multivalue and any(self.compare_value(key, value, val) for val in self[key]): + return True + if not self.descriptions[key].multivalue and self.compare_value(key, value, self[key]): + return True + return False + + def compare_value(self, key, a, b): + return self.descriptions[key].syntax.compare(a, b) + + def append_value(self, key, value): + if not self.has_key(key): + raise univention.admin.uexceptions.noProperty(key) + + if self.contains_value(key, value): + raise univention.admin.uexceptions.valueAlreadySet(key, value) + + if not self.descriptions[key].multivalue: + self[key] = value + return + + current_values = self[key] + current_values.append(value) + self[key] = current_values + def _not_implemented_method(attr): def _not_implemented_error(self, *args, **kwargs): diff --git a/management/univention-directory-manager-modules/modules/univention/admin/syntax.py b/management/univention-directory-manager-modules/modules/univention/admin/syntax.py index 3a04a1e..5605c9b 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/syntax.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/syntax.py @@ -138,6 +138,10 @@ def type(cls): def tostring(self, text): return text + @classmethod + def compare(cls, a, b): + return a == b + class simple(ISyntax): regex = None @@ -2055,17 +2059,23 @@ class soundModule(select): ] -class GroupDN(UDM_Objects): +class _DNCompare(UDM_Objects): + @classmethod + def compare(cls, a, b): + return univention.admin.uldap.DN(a) == univention.admin.uldap.DN(b) + + +class GroupDN(_DNCompare): udm_modules = ('groups/group', ) use_objects = False -class UserDN(UDM_Objects): +class UserDN(_DNCompare): udm_modules = ('users/user', ) use_objects = False -class HostDN(UDM_Objects): +class HostDN(_DNCompare): udm_modules = ('computers/computer', ) udm_filter = '!(univentionObjectFlag=docker)' diff --git a/management/univention-directory-manager-modules/modules/univention/admin/uexceptions.py b/management/univention-directory-manager-modules/modules/univention/admin/uexceptions.py index 1f7e72a..4fa02c0 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/uexceptions.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/uexceptions.py @@ -97,6 +97,14 @@ class valueMismatch(valueError): message = _('Values do not match.') +class valueNotSet(valueError): + message = _('The value is not set.') + + +class valueAlreadySet(valueError): + message = _('The value is already set.') + + class noLock(base): message = _('Could not acquire lock.') diff --git a/management/univention-directory-manager-modules/modules/univention/admincli/admin.py b/management/univention-directory-manager-modules/modules/univention/admincli/admin.py index 2f5701c..dd8bbaf 100755 --- a/management/univention-directory-manager-modules/modules/univention/admincli/admin.py +++ b/management/univention-directory-manager-modules/modules/univention/admincli/admin.py @@ -254,93 +254,46 @@ def _2utf8(text): def object_input(module, object, input, append=None, remove=None): out = [] if append: - for key, value in append.items(): + for key, values in append.items(): if module.property_descriptions[key].syntax.name == 'file': - if os.path.exists(value): - fh = open(value, 'r') + if os.path.exists(values): + fh = open(values, 'r') content = '' for line in fh.readlines(): content += line object[key] = content fh.close() else: - out.append('WARNING: file not found: %s' % value) + out.append('WARNING: file not found: %s' % values) + continue - elif univention.admin.syntax.is_syntax(module.property_descriptions[key].syntax, univention.admin.syntax.complex): - for i in range(0, len(value)): - test_val = value[i].split('"') - if test_val[0] and test_val[0] == value[i]: - val = value[i].split(' ') - else: - val = [] - for j in test_val: - if j and j.rstrip().lstrip(): - val.append(j.rstrip().lstrip()) - - if not object.has_key(key): - object[key] = [] - if val in object[key]: - out.append('WARNING: cannot append %s to %s, value exists' % (val, key)) - elif object[key] == [''] or object[key] == []: - object[key] = [val] - else: - object[key].append(val) - else: - for val in value: - if val in object[key]: - out.append('WARNING: cannot append %s to %s, value exists' % (val, key)) - elif object[key] == [''] or object[key] == []: - object[key] = [val] - else: - try: - tmp = list(object[key]) - tmp.append(val) - object[key] = list(tmp) - except univention.admin.uexceptions.valueInvalidSyntax, errmsg: - out.append('E: Invalid Syntax: %s' % str(errmsg)) - if remove: - for key, value in remove.items(): if univention.admin.syntax.is_syntax(module.property_descriptions[key].syntax, univention.admin.syntax.complex): - if value: - for i in range(0, len(value)): - test_val = value[i].split('"') - if test_val[0] and test_val[0] == value[i]: - val = value[i].split(' ') - else: - val = [] - out.append('test_val=%s' % test_val) - for j in test_val: - if j and j.rstrip().lstrip(): - val.append(j.rstrip().lstrip()) + values = _parse_complex_syntax_input(values) - for j in range(0, len(val)): - val[j] = '"%s"' % val[j] - - if val and val in object[key]: - object[key].remove(val) - else: - out.append("WARNING: cannot remove %s from %s, value does not exist" % (val, key)) - else: - object[key] = [] + for value in values: + try: + object.append_value(key, value) + except univention.admin.uexceptions.noProperty as exc: + out.append("WARNING: No attribute with name %s in this module, value not appended." % (key,)) + except univention.admin.uexceptions.valueNotSet as exc: + out.append("WARNING: cannot append %s to %s: %s" % (value, key, exc)) + except univention.admin.uexceptions.valueInvalidSyntax as exc: + out.append('E: Invalid Syntax: %s' % (exc,)) + if remove: + for key, values in remove.items(): + if not values: + values = [None] # remove the whole property - else: - current_values = [object[key]] if isinstance(object[key], basestring) else list(object[key]) - if value is None: - current_values = [] - else: - vallist = [value] if isinstance(value, basestring) else value + if univention.admin.syntax.is_syntax(module.property_descriptions[key].syntax, univention.admin.syntax.complex): + values = _parse_complex_syntax_input(values) - for val in vallist: - if val in current_values: - current_values.remove(val) - else: - out.append("WARNING: cannot remove %s from %s, value does not exist" % (val, key)) - if not module.property_descriptions[key].multivalue: - try: - current_values = current_values[0] - except IndexError: - current_values = None - object[key] = current_values + for value in values: + try: + object.remove_value(key, value) + except univention.admin.uexceptions.noProperty as exc: + out.append("WARNING: No attribute with name %s in this module, value not removed." % (key,)) + except univention.admin.uexceptions.valueNotSet as exc: + out.append("WARNING: cannot remove %s from %s: %s" % (value, key, exc)) if input: for key, value in input.items(): if module.property_descriptions[key].syntax.name == 'binaryfile': @@ -360,18 +313,7 @@ def object_input(module, object, input, append=None, remove=None): out.append('WARNING: file not found: %s' % value) elif univention.admin.syntax.is_syntax(module.property_descriptions[key].syntax, univention.admin.syntax.complex): - if isinstance(value, list): - for i in range(0, len(value)): - test_val = value[i].split('"') - if test_val[0] and test_val[0] == value[i]: - val = value[i].split(' ') - else: - val = [] - for j in test_val: - if j and j.rstrip().lstrip(): - val.append(j.rstrip().lstrip()) - else: - val = value.split(' ') + val = _parse_complex_syntax_input(value) if module.property_descriptions[key].multivalue: object[key] = [val] else: @@ -386,6 +328,18 @@ def object_input(module, object, input, append=None, remove=None): return out +def _parse_complex_syntax_input(values): + parsed_values = [] + for value in values: + if '"' not in value: + value = value.split(' ') + else: + value = ['"%s"' % (x.strip(),) for x in value.split('"') if x.strip()] + parsed_values.append(value) + return parsed_values + + + def list_available_modules(o=[]): o.append("Available Modules are:") @@ -629,68 +583,49 @@ def _doit(arglist): for opt, val in opts: if opt == '--set': - pos = val.find('=') - name = val[:pos] - value = _2utf8(val[pos + 1:]) + try: + name, value = val.split('=', 1) + except ValueError: + name, value = val, '' + value = _2utf8(value) - was_set = 0 for mod, (properties, options) in information.items(): - if properties.has_key(name): - if properties[name].multivalue: - if not input.has_key(name): - input[name] = [] - was_set = 1 - if value: - input[name].append(value) - was_set = 1 - else: - input[name] = value - was_set = 1 - - if not was_set: - out.append("WARNING: No attribute with name '%s' in this module, value not set." % name) + if name not in properties: + out.append("WARNING: No attribute with name '%s' in this module, value not set." % name) + continue + if properties[name].multivalue: + input.setdefault(name, []) + if value: + input[name].append(value) + else: + input[name] = value elif opt == '--append': - pos = val.find('=') - name = val[:pos] - value = _2utf8(val[pos + 1:]) - was_set = 0 + try: + name, value = val.split('=', 1) + except ValueError: + name, value = val, '' + value = _2utf8(value) for mod, (properties, options) in information.items(): - if properties.has_key(name): + try: + properties[name] + except KeyError: + pass + else: if properties[name].multivalue: - if not append.has_key(name): - append[name] = [] + append.setdefault(name, []) if value: append[name].append(value) - was_set = 1 - else: + else: # hmm. this should be illegal ^^ append[name] = value - was_set = 1 - if not was_set: - out.append("WARNING: No attribute with name %s in this module, value not appended." % name) - elif opt == '--remove': - pos = val.find('=') - if pos == -1: + try: + name, value = val.split('=', 1) + except ValueError: name = val - value = None + remove[name] = None else: - name = val[:pos] - value = _2utf8(val[pos + 1:]) - was_set = False - for mod, (properties, options) in information.items(): - if properties.has_key(name): - was_set = True - if properties[name].multivalue: - if value is None: - remove[name] = value - elif value: - remove.setdefault(name, []) - if remove[name] is not None: - remove[name].append(value) - else: - remove[name] = value - if not was_set: - out.append("WARNING: No attribute with name %s in this module, value not removed." % name) + value = _2utf8(value) + remove.setdefault(name, []).append(value) elif opt == '--remove_referring': remove_referring = 1 elif opt == '--recursive':