View | Details | Raw Unified | Return to bug 45252 | Differences between
and this patch

Collapse All | Expand All

(-)a/services/univention-s4-connector/modules/univention/s4connector/__init__.py (-7 / +4 lines)
 Lines 43-48   import traceback Link Here 
43
import types
43
import types
44
import ldap
44
import ldap
45
import univention.uldap
45
import univention.uldap
46
from univention.lib import ordered_set
46
import univention.admin.uldap
47
import univention.admin.uldap
47
import univention.admin.modules
48
import univention.admin.modules
48
import univention.admin.objects
49
import univention.admin.objects
 Lines 1207-1219   class ucs: Link Here 
1207
						else:
1208
						else:
1208
							equal = compare[0] == compare[1]
1209
							equal = compare[0] == compare[1]
1209
						if not equal:
1210
						if not equal:
1210
							# This is deduplication of LDAP attribute values for S4 -> UCS.
1211
							# This is deduplication of LDAP attribute values
1211
							# It destroys ordering of multi-valued attributes. This seems problematic
1212
							# for S4 -> UCS with preserved order.
1212
							# as the handling of `con_other_attribute` assumes preserved ordering
1213
							# (this is not guaranteed by LDAP).
1214
							# See the MODIFY-case in `sync_from_ucs()` for more.
1213
							# See the MODIFY-case in `sync_from_ucs()` for more.
1215
							if isinstance(value, list):
1214
							if isinstance(value, list):
1216
								ucs_object[ucs_key] = list(set(value))
1215
								ucs_object[ucs_key] = list(ordered_set.OrderedSet(value))
1217
							else:
1216
							else:
1218
								ucs_object[ucs_key] = value
1217
								ucs_object[ucs_key] = value
1219
							ud.debug(ud.LDAP, ud.INFO, "set key in ucs-object: %s" % ucs_key)
1218
							ud.debug(ud.LDAP, ud.INFO, "set key in ucs-object: %s" % ucs_key)
1220
- 
1221
--
1222
.../modules/univention/s4connector/s4/__init__.py  | 253 +++++++++------------
1219
.../modules/univention/s4connector/s4/__init__.py  | 253 +++++++++------------
1223
1 file changed, 113 insertions(+), 140 deletions(-)
1220
1 file changed, 113 insertions(+), 140 deletions(-)
(-)a/services/univention-s4-connector/modules/univention/s4connector/s4/__init__.py (-142 / +113 lines)
 Lines 2579-2738   class s4(univention.s4connector.ucs): Link Here 
2579
		#
2579
		#
2580
		elif (object['modtype'] == 'modify' and s4_object) or (object['modtype'] == 'add' and s4_object):
2580
		elif (object['modtype'] == 'modify' and s4_object) or (object['modtype'] == 'add' and s4_object):
2581
			ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: modify object: %s" % object['dn'])
2581
			ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: modify object: %s" % object['dn'])
2582
			ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: old_object: %s" % old_ucs_object)
2582
			ud.debug(ud.LDAP, ud.INFO,
2583
			ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: new_object: %s" % new_ucs_object)
2583
				"sync_from_ucs: old_object: %s" % old_ucs_object)
2584
			ud.debug(ud.LDAP, ud.INFO,
2585
				"sync_from_ucs: new_object: %s" % new_ucs_object)
2584
			object['old_ucs_object'] = old_ucs_object
2586
			object['old_ucs_object'] = old_ucs_object
2585
			object['new_ucs_object'] = new_ucs_object
2587
			object['new_ucs_object'] = new_ucs_object
2586
			attribute_list = set(old_ucs_object.keys()).union(set(new_ucs_object.keys()))
2588
			attribute_list = set(old_ucs_object.keys() + new_ucs_object.keys())
2587
			if hasattr(self.property[property_type], "con_sync_function"):
2588
				self.property[property_type].con_sync_function(self, property_type, object)
2589
			else:
2590
				# Iterate over attributes and post_attributes
2591
				for attribute_type_name, attribute_type in [('attributes', self.property[property_type].attributes), ('post_attributes', self.property[property_type].post_attributes)]:
2592
					if hasattr(self.property[property_type], attribute_type_name) and attribute_type is not None:
2593
						for attr in attribute_list:
2594
							value = new_ucs_object.get(attr)
2595
							if not self.__has_attribute_value_changed(attr, old_ucs_object, new_ucs_object):
2596
								continue
2597
2589
2598
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The following attribute has been changed: %s" % attr)
2590
			def find_case_independent(s4_object, attribute):
2599
2591
				attr = attribute.lower()
2600
							for attribute in attribute_type.keys():
2592
				matching = (v for (k, v) in s4_object.iteritems() if k.lower() == attr)
2601
								if attribute_type[attribute].ldap_attribute != attr:
2593
				try:
2602
									continue
2594
					values = next(matching)
2595
				except StopIteration:
2596
					values = []
2597
				return set(values)
2598
2599
			# Iterate over attributes and post_attributes
2600
			for attribute_type_name, attribute_type in [('attributes', self.property[property_type].attributes),
2601
					('post_attributes', self.property[property_type].post_attributes)]:
2602
				if hasattr(self.property[property_type], attribute_type_name) and attribute_type is not None:
2603
					for attr in attribute_list:
2604
						if not self.__has_attribute_value_changed(attr, old_ucs_object, new_ucs_object):
2605
							continue
2603
2606
2604
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: Found a corresponding mapping defintion: %s" % attribute)
2607
						ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The following attribute has been changed: %s" % attr)
2605
								s4_attribute = attribute_type[attribute].con_attribute
2606
								s4_other_attribute = attribute_type[attribute].con_other_attribute
2607
2608
2608
								if not attribute_type[attribute].sync_mode in ['write', 'sync']:
2609
						for attribute in attribute_type.keys():
2609
									ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: %s is in not in write or sync mode. Skipping" % attribute)
2610
							if attribute_type[attribute].ldap_attribute != attr:
2610
									continue
2611
								continue
2611
2612
2612
								modify = False
2613
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: Found a corresponding mapping defintion: %s" % attribute)
2614
							s4_attribute = attribute_type[attribute].con_attribute
2615
							s4_other_attribute = attribute_type[attribute].con_other_attribute
2613
2616
2614
								# Get the UCS attributes
2617
							if not attribute_type[attribute].sync_mode in ['write', 'sync']:
2615
								old_values = set(old_ucs_object.get(attr, []))
2618
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: %s is in not in write or sync mode. Skipping" % attribute)
2616
								new_values = set(new_ucs_object.get(attr, []))
2619
								continue
2617
2620
2618
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: old_values: %s" % old_values)
2621
							# Get the UCS attributes
2619
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: new_values: %s" % new_values)
2622
							old_values = set(old_ucs_object.get(attr, []))
2623
							new_values = set(new_ucs_object.get(attr, []))
2620
2624
2621
								if attribute_type[attribute].compare_function:
2625
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: old_values: %s" % old_values)
2622
									if not attribute_type[attribute].compare_function(list(old_values), list(new_values)):
2626
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: new_values: %s" % new_values)
2623
										modify = True
2624
								elif not univention.s4connector.compare_lowercase(list(old_values), list(new_values)):  # FIXME: use defined compare-function from mapping.py
2625
									modify = True
2626
2627
2627
								if not modify:
2628
							current_s4_values = find_case_independent(s4_object, s4_attribute)
2628
									ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: no modification necessary for %s" % attribute)
2629
									continue
2630
2629
2631
								# So, at this point we have the old and the new UCS object.
2630
							compare_function = attribute_type[attribute].compare_function or \
2632
								# Thus we can create the diff, but we have to check the current S4 object
2631
								univention.s4connector.compare_lowercase
2632
							if compare_function(list(old_values), list(new_values)):
2633
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: no modification necessary for %s" % attribute)
2634
								continue
2633
2635
2634
								if not old_values:
2636
							# So, at this point we have the old and the new UCS object.
2635
									to_add = new_values
2637
							# Thus we can create the diff, but we have to check the current S4 object
2636
									to_remove = set([])
2638
							to_add = new_values - old_values
2637
								elif not new_values:
2639
							to_remove = old_values - new_values
2638
									to_remove = old_values
2640
2639
									to_add = set([])
2641
							if s4_other_attribute:
2640
								else:
2642
								# This is the case, where we map from a multi-valued UCS attribute to two S4 attributes.
2641
									to_add = new_values - old_values
2643
								# telephoneNumber/otherTelephone (S4) to telephoneNumber (UCS) would be an example.
2642
									to_remove = old_values - new_values
2644
								#
2643
2645
								# The direct mapping assumes preserved ordering of the multi-valued UCS
2644
								if s4_other_attribute:
2646
								# attributes and places the first value in the primary S4 attribute,
2645
									# This is the case, where we map from a multi-valued UCS attribute to two S4 attributes.
2647
								# the rest in the secondary S4 attributes.
2646
									# telephoneNumber/otherTelephone (S4) to telephoneNumber (UCS) would be an example.
2648
								#
2647
									#
2649
								# The following code handles the correct distribution of the UCS attribute,
2648
									# The direct mapping assumes preserved ordering of the multi-valued UCS
2650
								# to two S4 attributes. It also ensures, that the primary S4 attribute keeps
2649
									# attributes and places the first value in the primary S4 attribute,
2651
								# its value as long as that value is not removed. If removed the primary
2650
									# the rest in the secondary S4 attributes.
2652
								# attribute is assigned a random value from the UCS attribute.
2651
									# Assuming preserved ordering is wrong, as LDAP does not guarantee is and the
2653
								current_s4_other_values = find_case_independent(s4_object, s4_other_attribute)
2652
									# deduplication of LDAP attribute values in `__set_values()` destroys it.
2654
2653
									#
2655
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 values: %s" % current_s4_values)
2654
									# The following code handles the correct distribution of the UCS attribute,
2656
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 other values: %s" % current_s4_other_values)
2655
									# to two S4 attributes. It also ensures, that the primary S4 attribute keeps
2657
2656
									# its value as long as that value is not removed. If removed the primary
2658
								# If we removed the value on the UCS side that was contained in the `s4_attribute`,
2657
									# attribute is assigned a random value from the UCS attribute.
2659
								# but are adding new values, we choose a random value from the new values.
2658
									try:
2660
								new_s4_values = current_s4_values - to_remove
2659
										current_s4_values = set([v for k, v in s4_object.iteritems() if s4_attribute.lower() == k.lower()][0])
2661
								if not new_s4_values and to_add:
2660
									except IndexError:
2662
									new_s4_values.add(to_add.pop())
2661
										current_s4_values = set([])
2663
								new_s4_other_values = (current_s4_other_values | to_add) - to_remove - current_s4_values
2662
									ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 values: %s" % current_s4_values)
2664
2663
2665
								if current_s4_values != new_s4_values:
2664
									try:
2666
									modlist.append((ldap.MOD_REPLACE, s4_attribute, new_s4_values))
2665
										current_s4_other_values = set([v for k, v in s4_object.iteritems() if s4_other_attribute.lower() == k.lower()][0])
2667
								if current_s4_other_values != new_s4_other_values:
2666
									except IndexError:
2668
									modlist.append((ldap.MOD_REPLACE, s4_other_attribute, new_s4_other_values))
2667
										current_s4_other_values = set([])
2669
							else:
2668
									ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 other values: %s" % current_s4_other_values)
2670
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 values: %s" % current_s4_values)
2669
2671
2670
									new_s4_values = current_s4_values - to_remove
2672
								if (to_add or to_remove) and attribute_type[attribute].single_value:
2671
									if not new_s4_values and to_add:
2673
									value = new_ucs_object.get(attr)
2672
										for n_value in new_ucs_object.get(attr, []):
2674
									modify = not current_s4_values or not value or \
2673
											if n_value in to_add:
2675
										not compare_function(list(current_s4_values), list(value))
2674
												to_add = to_add - set([n_value])
2676
									if modify:
2675
												new_s4_values = [n_value]
2677
										mapping = getattr(attribute_type[attribute], 'mapping', ())
2676
												break
2678
										if len(mapping) > 0 and mapping[0]:
2677
2679
											ud.debug(ud.LDAP, ud.PROCESS, "Calling single value mapping function")
2678
									new_s4_other_values = (current_s4_other_values | to_add) - to_remove - current_s4_values
2680
											value = mapping[0](self, None, object)
2679
									if current_s4_values != new_s4_values:
2681
										modlist.append((ldap.MOD_REPLACE, s4_attribute, value))
2680
										if new_s4_values:
2681
											modlist.append((ldap.MOD_REPLACE, s4_attribute, new_s4_values))
2682
										else:
2683
											modlist.append((ldap.MOD_REPLACE, s4_attribute, []))
2684
2685
									if current_s4_other_values != new_s4_other_values:
2686
										modlist.append((ldap.MOD_REPLACE, s4_other_attribute, new_s4_other_values))
2687
								else:
2682
								else:
2688
									try:
2683
									if to_remove:
2689
										current_s4_values = set([v for k, v in s4_object.iteritems() if s4_attribute.lower() == k.lower()][0])
2684
										r = current_s4_values & to_remove
2690
									except IndexError:
2685
										if r:
2691
										current_s4_values = set([])
2686
											modlist.append((ldap.MOD_DELETE, s4_attribute, r))
2692
2687
									if to_add:
2693
									ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 values: %s" % current_s4_values)
2688
										a = to_add - current_s4_values
2694
2689
										if a:
2695
									if (to_add or to_remove) and attribute_type[attribute].single_value:
2690
											modlist.append((ldap.MOD_ADD, s4_attribute, a))
2696
										modify = False
2691
2697
										if not current_s4_values or not value:
2692
			if not modlist:
2698
											modify = True
2693
				ud.debug(ud.LDAP, ud.ALL, "nothing to modify: %s" % object['dn'])
2699
										elif attribute_type[attribute].compare_function:
2694
			else:
2700
											if not attribute_type[attribute].compare_function(list(current_s4_values), list(value)):
2695
				ud.debug(ud.LDAP, ud.INFO, "to modify: %s" % object['dn'])
2701
												modify = True
2696
				ud.debug(ud.LDAP, ud.ALL, "sync_from_ucs: modlist: %s" % modlist)
2702
										elif not univention.s4connector.compare_lowercase(list(current_s4_values), list(value)):
2697
				try:
2703
											modify = True
2698
					self.lo_s4.lo.modify_ext_s(compatible_modstring(object['dn']), compatible_modlist(modlist), serverctrls=self.serverctrls_for_add_and_modify)
2704
										if modify:
2699
				except:
2705
											if hasattr(attribute_type[attribute], 'mapping') and len(attribute_type[attribute].mapping) > 0 and attribute_type[attribute].mapping[0]:
2700
					ud.debug(ud.LDAP, ud.ERROR, "sync_from_ucs: traceback during modify object: %s" % object['dn'])
2706
												ud.debug(ud.LDAP, ud.PROCESS, "Calling single value mapping function")
2701
					ud.debug(ud.LDAP, ud.ERROR, "sync_from_ucs: traceback due to modlist: %s" % modlist)
2707
												value = attribute_type[attribute].mapping[0](self, None, object)
2702
					raise
2708
											modlist.append((ldap.MOD_REPLACE, s4_attribute, value))
2709
									else:
2710
										if to_remove:
2711
											r = current_s4_values & to_remove
2712
											if r:
2713
												modlist.append((ldap.MOD_DELETE, s4_attribute, r))
2714
										if to_add:
2715
											a = to_add - current_s4_values
2716
											if a:
2717
												modlist.append((ldap.MOD_ADD, s4_attribute, a))
2718
2719
				if not modlist:
2720
					ud.debug(ud.LDAP, ud.ALL, "nothing to modify: %s" % object['dn'])
2721
				else:
2722
					ud.debug(ud.LDAP, ud.INFO, "to modify: %s" % object['dn'])
2723
					ud.debug(ud.LDAP, ud.ALL, "sync_from_ucs: modlist: %s" % modlist)
2724
					try:
2725
						self.lo_s4.lo.modify_ext_s(compatible_modstring(object['dn']), compatible_modlist(modlist), serverctrls=self.serverctrls_for_add_and_modify)
2726
					except:
2727
						ud.debug(ud.LDAP, ud.ERROR, "sync_from_ucs: traceback during modify object: %s" % object['dn'])
2728
						ud.debug(ud.LDAP, ud.ERROR, "sync_from_ucs: traceback due to modlist: %s" % modlist)
2729
						raise
2730
2703
2731
				if hasattr(self.property[property_type], "post_con_modify_functions"):
2704
			if hasattr(self.property[property_type], "post_con_modify_functions"):
2732
					for f in self.property[property_type].post_con_modify_functions:
2705
				for f in self.property[property_type].post_con_modify_functions:
2733
						ud.debug(ud.LDAP, ud.INFO, "Call post_con_modify_functions: %s" % f)
2706
					ud.debug(ud.LDAP, ud.INFO, "Call post_con_modify_functions: %s" % f)
2734
						f(self, property_type, object)
2707
					f(self, property_type, object)
2735
						ud.debug(ud.LDAP, ud.INFO, "Call post_con_modify_functions: %s (done)" % f)
2708
					ud.debug(ud.LDAP, ud.INFO, "Call post_con_modify_functions: %s (done)" % f)
2736
		#
2709
		#
2737
		# DELETE
2710
		# DELETE
2738
		#
2711
		#
2739
- 
2740
handling
2712
handling
2741
--
2742
.../modules/univention/s4connector/s4/__init__.py  | 57 ++++++++++++++++------
2713
.../modules/univention/s4connector/s4/__init__.py  | 57 ++++++++++++++++------
2743
1 file changed, 41 insertions(+), 16 deletions(-)
2714
1 file changed, 41 insertions(+), 16 deletions(-)
(-)a/services/univention-s4-connector/modules/univention/s4connector/s4/__init__.py (-18 / +41 lines)
 Lines 42-47   import time Link Here 
42
import types
42
import types
43
import array
43
import array
44
import univention.uldap
44
import univention.uldap
45
from univention.lib import ordered_set
45
import univention.s4connector
46
import univention.s4connector
46
import univention.debug2 as ud
47
import univention.debug2 as ud
47
from ldap.controls import LDAPControl
48
from ldap.controls import LDAPControl
 Lines 51-56   from samba.dcerpc import security Link Here 
51
from samba.ndr import ndr_pack, ndr_unpack
52
from samba.ndr import ndr_pack, ndr_unpack
52
from samba.dcerpc import misc
53
from samba.dcerpc import misc
53
54
55
54
DECODE_IGNORELIST = ['objectSid', 'objectGUID', 'repsFrom', 'replUpToDateVector', 'ipsecData', 'logonHours', 'userCertificate', 'dNSProperty', 'dnsRecord']
56
DECODE_IGNORELIST = ['objectSid', 'objectGUID', 'repsFrom', 'replUpToDateVector', 'ipsecData', 'logonHours', 'userCertificate', 'dNSProperty', 'dnsRecord']
55
57
56
LDAP_SERVER_SHOW_DELETED_OID = "1.2.840.113556.1.4.417"
58
LDAP_SERVER_SHOW_DELETED_OID = "1.2.840.113556.1.4.417"
 Lines 2585-2591   class s4(univention.s4connector.ucs): Link Here 
2585
				"sync_from_ucs: new_object: %s" % new_ucs_object)
2587
				"sync_from_ucs: new_object: %s" % new_ucs_object)
2586
			object['old_ucs_object'] = old_ucs_object
2588
			object['old_ucs_object'] = old_ucs_object
2587
			object['new_ucs_object'] = new_ucs_object
2589
			object['new_ucs_object'] = new_ucs_object
2588
			attribute_list = set(old_ucs_object.keys() + new_ucs_object.keys())
2590
			attribute_list = ordered_set.OrderedSet(old_ucs_object.keys() + new_ucs_object.keys())
2589
2591
2590
			def find_case_independent(s4_object, attribute):
2592
			def find_case_independent(s4_object, attribute):
2591
				attr = attribute.lower()
2593
				attr = attribute.lower()
 Lines 2594-2600   class s4(univention.s4connector.ucs): Link Here 
2594
					values = next(matching)
2596
					values = next(matching)
2595
				except StopIteration:
2597
				except StopIteration:
2596
					values = []
2598
					values = []
2597
				return set(values)
2599
				return ordered_set.OrderedSet(values)
2598
2600
2599
			# Iterate over attributes and post_attributes
2601
			# Iterate over attributes and post_attributes
2600
			for attribute_type_name, attribute_type in [('attributes', self.property[property_type].attributes),
2602
			for attribute_type_name, attribute_type in [('attributes', self.property[property_type].attributes),
 Lines 2619-2626   class s4(univention.s4connector.ucs): Link Here 
2619
								continue
2621
								continue
2620
2622
2621
							# Get the UCS attributes
2623
							# Get the UCS attributes
2622
							old_values = set(old_ucs_object.get(attr, []))
2624
							old_values = ordered_set.OrderedSet(old_ucs_object.get(attr, []))
2623
							new_values = set(new_ucs_object.get(attr, []))
2625
							new_values = ordered_set.OrderedSet(new_ucs_object.get(attr, []))
2624
2626
2625
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: old_values: %s" % old_values)
2627
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: old_values: %s" % old_values)
2626
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: new_values: %s" % new_values)
2628
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: new_values: %s" % new_values)
 Lines 2638-2666   class s4(univention.s4connector.ucs): Link Here 
2638
							to_add = new_values - old_values
2640
							to_add = new_values - old_values
2639
							to_remove = old_values - new_values
2641
							to_remove = old_values - new_values
2640
2642
2643
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: to_add: %s" % to_add)
2644
							ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: to_remove: %s" % to_remove)
2645
2641
							if s4_other_attribute:
2646
							if s4_other_attribute:
2642
								# This is the case, where we map from a multi-valued UCS attribute to two S4 attributes.
2647
								# This is the case, where we map from a multi-valued UCS attribute to two S4 attributes.
2643
								# telephoneNumber/otherTelephone (S4) to telephoneNumber (UCS) would be an example.
2648
								# telephoneNumber/otherTelephone (S4) to telephoneNumber (UCS) would be an example.
2644
								#
2649
								#
2645
								# The direct mapping assumes preserved ordering of the multi-valued UCS
2650
								# In Active Directory, for attributes that are split in two the administrator is
2646
								# attributes and places the first value in the primary S4 attribute,
2651
								# responsible for keeping a value in `telephoneNumber`. Imagine the following:
2647
								# the rest in the secondary S4 attributes.
2652
								# (a) telephoneNumber = '123', otherTelephone = ['123', '456']
2653
								# In this case, if the administrator deletes the value of `telephoneNumber`,
2654
								# Active Directory does NOT automatically pull a new value from `otherTelephone`.
2655
								#
2656
								# This is impossible to support with the connector. Imagine again case (a). If
2657
								# we delete `123` from `phone` via UDM, AD would be synced into the following
2658
								# state: (b) telephoneNumber = '', otherTelephone = ['456']
2659
								# From now on, whenever we add a new value to `phone` via UDM, for example:
2660
								# (c) phone = ['456', '789'] it MUST be synced as
2661
								# (d) telephoneNumber = '', otherTelephone = ['456', '789'] as '456' came
2662
								# before '789' and '456' is definitely in `otherTelephone`.
2648
								#
2663
								#
2649
								# The following code handles the correct distribution of the UCS attribute,
2664
								# We therefore implement, that `telephoneNumber` is never empty, as long as there
2650
								# to two S4 attributes. It also ensures, that the primary S4 attribute keeps
2665
								# are values in `otherTelephone`. If a modification would delete the value of
2651
								# its value as long as that value is not removed. If removed the primary
2666
								# `telephoneNumber` and at least one value exists in `otherTelephone`, the
2652
								# attribute is assigned a random value from the UCS attribute.
2667
								# connector duplicates the first entry of `otherTelephone` into
2668
								# `telephoneNumber`.
2653
								current_s4_other_values = find_case_independent(s4_object, s4_other_attribute)
2669
								current_s4_other_values = find_case_independent(s4_object, s4_other_attribute)
2654
2670
2655
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 values: %s" % current_s4_values)
2671
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 values: %s" % current_s4_values)
2656
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 other values: %s" % current_s4_other_values)
2672
								ud.debug(ud.LDAP, ud.INFO, "sync_from_ucs: The current S4 other values: %s" % current_s4_other_values)
2657
2673
2658
								# If we removed the value on the UCS side that was contained in the `s4_attribute`,
2659
								# but are adding new values, we choose a random value from the new values.
2660
								new_s4_values = current_s4_values - to_remove
2674
								new_s4_values = current_s4_values - to_remove
2661
								if not new_s4_values and to_add:
2675
								retained_s4_other_values = current_s4_other_values - to_remove
2662
									new_s4_values.add(to_add.pop())
2676
2663
								new_s4_other_values = (current_s4_other_values | to_add) - to_remove - current_s4_values
2677
								# If we removed the value on the UCS side that was contained in the `current_s4_values`,
2678
								# but have values, we duplicate the first value from the `new_s4_other_values`.
2679
								if not new_s4_values:
2680
									if retained_s4_other_values:
2681
										new_s4_values.add(retained_s4_other_values[0])
2682
									elif to_add:
2683
										new_s4_values.add(to_add[0])
2684
2685
								# Take the old values without those to be removed. Add the new ones
2686
								# (without duplicating the `new_s4_values`, but preserving existing
2687
								# duplicates in `con_other_attribute`)
2688
								new_s4_other_values = retained_s4_other_values | (to_add - new_s4_values)
2664
2689
2665
								if current_s4_values != new_s4_values:
2690
								if current_s4_values != new_s4_values:
2666
									modlist.append((ldap.MOD_REPLACE, s4_attribute, new_s4_values))
2691
									modlist.append((ldap.MOD_REPLACE, s4_attribute, new_s4_values))
2667
- 
2668
sync test"
2692
sync test"
2669
--
2670
test/ucs-test/tests/52_s4connector/502_other_attribute_sync.py | 5 -----
2693
test/ucs-test/tests/52_s4connector/502_other_attribute_sync.py | 5 -----
2671
1 file changed, 5 deletions(-)
2694
1 file changed, 5 deletions(-)
(-)a/test/ucs-test/tests/52_s4connector/502_other_attribute_sync.py (-6 lines)
 Lines 8-18    Link Here 
8
##  - 35903
8
##  - 35903
9
##  - 36480
9
##  - 36480
10
##  - 45252
10
##  - 45252
11
## versions:
12
##  4.2-2: skip
13
14
# We skip this since 4.2-2, as the corresponding implementation is not yet committed.
15
# See https://forge.univention.org/bugzilla/show_bug.cgi?id=45252.
16
11
17
import pytest
12
import pytest
18
13
19
- 

Return to bug 45252