def get_ldap_search_root(self, user_domain=None): """Return the search root(s) for users in the LDAP server. If :setting:`AD_SEARCH_ROOT` is set, then it will be used. Otherwise, a suitable search root will be computed based on the domain name (either the provided ``user_domain`` or the result of :py:meth:`get_domain_name`) and any configured Organizational Unit name (:setting:`AD_OU_NAME`). Args: user_domain (unicode, optional): An explicit Active Directory domain to use for the search root. Returns: unicode: The search root used to locate users. """ if getattr(settings, 'AD_SEARCH_ROOT', None): return settings.AD_SEARCH_ROOT dn = [] if settings.AD_OU_NAME: dn.append([('ou', settings.AD_OU_NAME, None)]) if user_domain is None: user_domain = self.get_domain_name() if user_domain: dn += [[('dc', dc, None)] for dc in user_domain.split('.')] return dn2str(dn)
def _register_host(self, app, args): if not app.docker: self.debug('App is not docker. Skip registering host') return None, None hostdn = ucr_get(app.ucr_hostdn_key) lo, pos = self._get_ldap_connection(args) if hostdn: if lo.get(hostdn): self.log('Already found %s as a host for %s. Better do nothing...' % (hostdn, app.id)) return hostdn, None else: self.warn('%s should be the host for %s. But it was not found in LDAP. Creating a new one' % (hostdn, app.id)) # quasi unique hostname; make sure it does not exceed 14 chars # 5 chars of appid + '-' + 8 digits of Epoch hostname = '%s-%s' % (app.id[:5], str(int((time.time() * 1000000)))[-10:-2]) password = generate_password() self.log('Registering the container host %s for %s' % (hostname, app.id)) if app.docker_server_role == 'memberserver': base = 'cn=memberserver,cn=computers,%s' % ucr_get('ldap/base') else: base = 'cn=dc,cn=computers,%s' % ucr_get('ldap/base') while base and not lo.get(base): base = dn2str(str2dn(base)[1:]) pos.setDn(base) domain = ucr_get('domainname') description = '%s (%s)' % (app.name, app.version) policies = ['cn=app-release-update,cn=policies,%s' % ucr_get('ldap/base'), 'cn=app-update-schedule,cn=policies,%s' % ucr_get('ldap/base')] obj = create_object_if_not_exists('computers/%s' % app.docker_server_role, lo, pos, name=hostname, description=description, domain=domain, password=password, objectFlag='docker', policies=policies) ucr_save({app.ucr_hostdn_key: obj.dn}) return obj.dn, password
def __str__(self): try: return dn2str(self.rdns) except Exception as e: print(len(self.rdns)) print(self.rdns) raise
def __str__(self): try: return dn2str(self.rdns) except Exception, e: print len(self.rdns) print self.rdns raise
def get_adjusted_parent_dn(self, dn=None): index = PluginRegistry.getInstance("ObjectIndex") tdn = [] pdn = self.get_parent_dn(dn) # Skip base if len(pdn) < len(self.__env.base): return pdn while True: if pdn == self.__env.base or len(pdn) < len(self.__env.base): break # Fetch object type for pdn ptype = index.search({"dn": pdn}, {'_type': 1})[0]['_type'] schema = self.__factory.getXMLSchema(ptype) if not ("StructuralInvisible" in schema.__dict__ and schema.StructuralInvisible == True): #tdn.insert(0, str2dn(pdn.encode('utf-8'))[0]) tdn.append(str2dn(pdn.encode('utf-8'))[0]) pdn = self.get_parent_dn(pdn) tdn = str2dn(self.__env.base.encode('utf-8'))[::-1] + tdn[::-1] return dn2str(tdn[::-1]).decode('utf-8')
def get_root_suffix_by_entry(self, entry_dn): """Get the root suffix to which the entry belongs :param entry_dn: An entry DN :type entry_dn: str :returns: str """ mapping_tree_list = sorted(self.list(), key=lambda b: len(b.dn), reverse=True) entry_dn_parts = str2dn(entry_dn) processing = True while processing: compare_dn = dn2str(entry_dn_parts) for mapping_tree in mapping_tree_list: if str.lower(compare_dn) == str.lower(mapping_tree.rdn): processing = False return mapping_tree.rdn if entry_dn_parts: entry_dn_parts.pop(0) else: processing = False raise ldap.NO_SUCH_OBJECT(f"{entry_dn} doesn't belong to any suffix")
def __str__(self): try: return dn2str(self.rdns) except Exception, e: print(len(self.rdns)) print(self.rdns) raise
def domainroot(d): """ get the domain ldap root for the given dn >>> dn = "sambaDomainName=IEPER-TEST,dc=Ieper,dc=elex" >>> domainroot(dn) 'dc=Ieper,dc=elex' """ dn = str2dn(d) dn.pop(0) return dn2str(dn)
def get_servers(self, lo): old_name = self.name old_position = self.position old_dn = str2dn(self.old_dn or self.dn) self.position = dn2str(old_dn[1:]) self.name = old_dn[0][0][1] try: return super(AnyDHCPService, self).get_servers(lo) finally: self.position = old_position self.name = old_name
def __init__(self, dn): logging.debug('Plugin.__init__(dn=' + repr(dn) + ')') self.core = OgpCore.getInstance() self.dn = dn # Dirty but it pleases Michel :-P # Loads RW XML conf from LDAP self.cancel() # Parent DN (to find parent conf) self.parentDn = str2dn(dn) del self.parentDn[0] self.parentDn = dn2str(self.parentDn)
def build_dn_and_filter(self, ressource, ldap_attributes): '''Build the target record dn''' base_dn = ressource['base_dn'] rdn_attributes = ressource['rdn_attributes'] dn = str2dn(base_dn) rdn = [] for ldap_attribute in rdn_attributes: values = ldap_attributes.get(ldap_attribute, []) assert len(values) == 1, 'RDN attribute must have exactly one value %r %r' % \ (rdn_attributes, ldap_attributes) rdn.append((ldap_attribute, values[0], 1)) dn = [rdn] + dn return dn2str(dn), ('&', [(a, b) for a, b, c in rdn])
def build_dn_and_filter(self, ressource, ldap_attributes): '''Build the target record dn''' base_dn = ressource['base_dn'] rdn_attributes = ressource['rdn_attributes'] dn = str2dn(base_dn) rdn = [] for ldap_attribute in rdn_attributes: values = ldap_attributes.get(ldap_attribute, []) assert len(values) == 1, 'RDN attribute must have exactly one value %r %r' % \ (rdn_attributes, ldap_attributes) rdn.append((ldap_attribute, values[0], 1)) dn = [rdn] + dn return dn2str(dn), ('&', [(a,b) for a, b, c in rdn])
def getDestinationIndicator(self, client_id, uid, cn_query, rotate=True): """ :param client_id: UUID of the client used to find the closest destinationIndicators :param uid: uid of the user :param cn_query: filter for destinationIndicator-cns (e.g. 'lts-% for wildcards) :param rotate: rotate the destinationIndicators (do not use the last one twice in a row) :return: FQDN of the server marked as destinationIndicator """ index = PluginRegistry.getInstance('ObjectIndex') res = index.search({'_type': 'User', 'uid': uid}, {'dn': 1}) if len(res) == 0: raise ValueError(C.make_error("USER_NOT_FOUND", user=uid, status_code=404)) user = ObjectProxy(res[0]['dn']) if rotate is False and user.destinationIndicator is not None: # nothing to rotate, take the stored one return user.destinationIndicator client = self.__open_device(client_id, read_only=True) parent_dn = client.get_adjusted_parent_dn() res = index.search({'_type': 'Device', 'extension': 'GoServer', 'cn': cn_query, '_adjusted_parent_dn': parent_dn}, {'dn': 1}) while len(res) == 0 and len(parent_dn) > len(self.env.base): parent_dn = dn2str(str2dn(parent_dn, flags=ldap.DN_FORMAT_LDAPV3)[1:]) res = index.search({'_type': 'Device', 'cn': cn_query, '_adjusted_parent_dn': parent_dn}, {'dn': 1}) if len(res) > 0: di_pool = sorted([x['dn'] for x in res]) if user.destinationIndicator is None: # nothing to rotate, take the first one user.destinationIndicator = di_pool[0] user.commit() elif rotate is True: if user.destinationIndicator in di_pool: # take the next one from list position = di_pool.index(user.destinationIndicator)+1 if position >= len(di_pool): position = 0 user.destinationIndicator = di_pool[position] user.commit() else: # nothing to rotate, take the first one user.destinationIndicator = di_pool[0] user.commit() return user.destinationIndicator return None
def changed_cases(members): def mixed_case(attr): return attr[0].lower() + attr[1:].upper() variants = [] for transform in (str.lower, str.upper, mixed_case): result = {} for key, dn in members.items(): dn = str2dn(dn) rdn = dn.pop(0) rdn = [tuple([transform(str(rdn[0][0]))] + list(rdn[0][1:]))] dn.insert(0, rdn) result[key] = [dn2str(dn)] variants.append(result) return variants
def get(self, selector=[], dn=None, json=False): """Create a test user with uid=test_user_UID rdn :param uid: User id :type uid: int :param gid: Group id :type gid: int :returns: DSLdapObject of the created entry """ # Normalise escaped characters if is_dn(selector): selector = dn2str(str2dn(selector)) return super(MappingTrees, self).get(selector, dn, json)
def check_correct_domain_admin(): """Check domain administrator saved for ucs-test and correct if needed""" print('=== Checking / Correcting ucs-test domain administrator ===') ucr.load() ucr_domain_admin = ucr['tests/domainadmin/account'] if ucr_domain_admin: ucr_domain_admin_parts = str2dn(ucr_domain_admin) if ucr_domain_admin_parts[0][0][1] != options.domain_admin: ucr_domain_admin_parts[0][0] = ('uid', options.domain_admin, ucr_domain_admin_parts[0][0][2]) ucr['tests/domainadmin/account'] = dn2str(ucr_domain_admin_parts) else: print( "=== tests/domainadmin/account is not set, trying to create it ===" ) ucr['tests/domainadmin/account'] = 'uid=%s,cn=users,%s' % ( escape_dn_chars(options.domain_admin), ucr['ldap/base']) ucr.save()
def _get_single_entry(self, *args, **kwargs): dn = kwargs.pop('dn', None) if dn: normalized_dn = lambda x: dn2str(str2dn(x)) assert normalized_dn(dn).lower().endswith( normalized_dn(self.model.base).lower()) self.search.scope = ldap.SCOPE_BASE self.search.base = dn if not args and not kwargs: self.search.filter = filterbuilder(objectclass__ne=None) else: self.search.filter = filterbuilder(*args, **kwargs) else: self.search.filter = filterbuilder(*args, **kwargs) self._evaluate() if not self.results: return None if len(self.results) > 1: raise ValueError("Multiple Results Found") else: dn, entry = self.results.pop() return self._map_to_model(dn, entry)
def ldap_text(self): return dn2str(self.rdns)
def save(self): """ Save object to LDAP. :return: self :rtype: GenericObject :raises univention.udm.exceptions.MoveError: when a move operation fails """ if self._deleted: raise DeletedError('{} has been deleted.'.format(self), dn=self.dn, module_name=self._udm_module.name) if not self._fresh: ud.warn('Saving stale UDM object instance') self._copy_to_udm_obj() if self.dn: if self._old_position and self._old_position != self.position: new_dn_li = [str2dn(self._orig_udm_object.dn)[0]] new_dn_li.extend(str2dn(self.position)) new_dn = dn2str(new_dn_li) ud.process('Moving {!r} object {!r} to {!r}'.format(self._udm_module.name, self.dn, new_dn)) try: self.dn = self._orig_udm_object.move(new_dn) except univention.admin.uexceptions.invalidOperation as exc: raise MoveError, MoveError( 'Moving {!r} object is not supported ({}).'.format(self._udm_module.name, exc), dn=self.dn, module_name=self._udm_module.name ), sys.exc_info()[2] except (univention.admin.uexceptions.base, ldap.error) as exc: raise MoveError, MoveError( 'Error moving {!r} object from {!r} to {!r}: {}'.format( self._udm_module.name, self.dn, self.position, exc ), dn=self.dn, module_name=self._udm_module.name ), sys.exc_info()[2] assert self.dn == self._orig_udm_object.dn self.position = self._lo.parentDn(self.dn) self._old_position = self.position self._orig_udm_object.position.setDn(self.position) try: self.dn = self._orig_udm_object.modify() except univention.admin.uexceptions.base as exc: raise ModifyError, ModifyError( 'Error saving {!r} object at {!r}: {} ({})'.format( self._udm_module.name, self.dn, exc.message, exc ), dn=self.dn, module_name=self._udm_module.name ), sys.exc_info()[2] ud.process('Modified {!r} object {!r}'.format(self._udm_module.name, self.dn)) else: try: self.dn = self._orig_udm_object.create() except ldap.INVALID_DN_SYNTAX as exc: raise CreateError, CreateError( 'Error creating {!r} object: {} ({})'.format( self._udm_module.name, exc.message, exc ), module_name=self._udm_module.name ), sys.exc_info()[2] except univention.admin.uexceptions.base as exc: raise CreateError, CreateError( 'Error creating {!r} object: {} ({})'.format( self._udm_module.name, exc.message, exc ), module_name=self._udm_module.name ), sys.exc_info()[2] ud.process('Created {!r} object {!r}'.format(self._udm_module.name, self.dn)) assert self.dn == self._orig_udm_object.dn assert self.position == self._lo.parentDn(self.dn) self._fresh = False if self._udm_module.meta.auto_reload: self.reload() return self
def get_parent_dn(self, dn=None): if not dn: dn = self.__base.dn return dn2str(str2dn(dn.encode('utf-8'))[1:]).decode('utf-8')
def x500_text(self): return dn2str(reversed(self.rdns))
def commit(self): # Check create permissions if self.__base_mode == "create": topic = "%s.objects.%s" % (self.__env.domain, self.__base_type) if self.__current_user is not None and not self.__acl_resolver.check(self.__current_user, topic, "c", base=self.dn): self.__log.debug("user '%s' has insufficient permissions to create %s, required is %s:%s" % ( self.__current_user, self.__base.dn, topic, 'c')) raise ACLException(C.make_error('PERMISSION_CREATE', target=self.__base.dn)) zope.event.notify(ObjectChanged("pre object %s" % self.__base_mode, self.__base)) # Gather information about children old_base = self.__base.dn # Get primary backend of the object to be moved p_backend = getattr(self.__base, '_backend') # Traverse tree and find different backends foreign_backends = {} index = PluginRegistry.getInstance("ObjectIndex") children = index.search({"dn": re.compile("^(.*,)?" + re.escape(self.__base.dn) + "$")}, {'dn': 1, '_type': 1}) # Note all elements with different backends for v in children: cdn = v['dn'] ctype = v['_type'] cback = self.__factory.getObjectTypes()[ctype]['backend'] if cback != p_backend: if not cback in foreign_backends: foreign_backends[cback] = [] foreign_backends[cback].append(cdn) # Only keep the first per backend that is close to the root root_elements = {} for fbe, fdns in foreign_backends.items(): fdns.sort(key=len) root_elements[fbe] = fdns[0] # Handle retracts for idx in self.__retractions.keys(): if self.__initial_extension_state[idx]: self.__retractions[idx].retract() del self.__retractions[idx] # Check each extension before trying to save them check_props = self.__base.check() for extension in [ext for ext in self.__extensions.values() if ext]: check_props.update(extension.check(check_props)) # Handle commits save_props = self.__base.commit() for extension in [ext for ext in self.__extensions.values() if ext]: # Populate the base uuid to the extensions if extension.uuid and extension.uuid != self.__base.uuid: raise ProxyException(C.make_error('OBJECT_UUID_MISMATCH', b_uuid=self.__base.uuid, e_uuid=extension.uuid)) if not extension.uuid: extension.uuid = self.__base.uuid extension.dn = self.__base.dn save_props.update(extension.commit(save_props)) # Skip further actions if we're in create mode if self.__base_mode == "create": pass # Did the commit result in a move? elif self.dn != self.__base.dn: if children: # Move additional backends if needed for fbe, fdn in root_elements.items(): # Get new base of child new_child_dn = fdn[:len(fdn) - len(old_base)] + self.__base.dn new_child_base = dn2str(str2dn(new_child_dn.encode('utf-8'))[1:]).decode('utf-8') # Select objects with different base and trigger a move, the # primary backend move will be triggered and do a recursive # move for that backend. obj = self.__factory.getObject(children[fdn], fdn) obj.move(new_child_base) # Update all DN references # Emit 'post move' events for entry in children: cdn = entry['dn'] ctype = entry['_type'] # Don't handle objects that already have been moved if cdn in root_elements.values(): continue # These objects have been moved automatically. Open # them and let them do a simulated move to update # their refs. new_cdn = cdn[:len(cdn) - len(old_base)] + self.__base.dn obj = self.__factory.getObject(ctype, new_cdn) obj.simulate_move(cdn) self.dn = self.__base.dn zope.event.notify(ObjectChanged("post object move", self.__base)) zope.event.notify(ObjectChanged("post object %s" % self.__base_mode, self.__base))
def __str__(self): return dn2str(self.rdns)
def get_parent_dn(self): return dn2str(str2dn(self.__base.dn.encode('utf-8'))[1:]).decode('utf-8')
def modify(self, dn, mod_type=None, attrs=None, bind_dn=None, bind_pwd=None): """ Modify a record """ self._complainIfReadOnly() unescaped_dn = self._encode_incoming(dn) dn = escape_dn(unescaped_dn) res = self.search( base=unescaped_dn , scope=ldap.SCOPE_BASE , bind_dn=bind_dn , bind_pwd=bind_pwd , raw=True ) attrs = attrs and attrs or {} cur_rec = res['results'][0] mod_list = [] for key, values in attrs.items(): if key.endswith(';binary'): key = key[:-7] elif isinstance(values, basestring): values = [self._encode_incoming(x) for x in values.split(';')] else: values = [self._encode_incoming(x) for x in values] if mod_type is None: if not cur_rec.has_key(key) and values != ['']: mod_list.append((ldap.MOD_ADD, key, values)) elif cur_rec.get(key,['']) != values and values not in ([''],[]): mod_list.append((ldap.MOD_REPLACE, key, values)) elif cur_rec.has_key(key) and values in ([''], []): mod_list.append((ldap.MOD_DELETE, key, None)) elif mod_type in (ldap.MOD_ADD, ldap.MOD_DELETE) and values == ['']: continue elif ( mod_type == ldap.MOD_DELETE and set(values).difference(set(cur_rec.get(key, []))) ): continue else: mod_list.append((mod_type, key, values)) try: connection = self.connect(bind_dn=bind_dn, bind_pwd=bind_pwd) dn_parts = str2dn(dn) rdn = dn_parts[0] rdn_attr = rdn[0][0] raw_rdn = attrs.get(rdn_attr, '') if isinstance(raw_rdn, basestring): raw_rdn = [raw_rdn] new_rdn = raw_rdn[0] if new_rdn: rdn_value = self._encode_incoming(new_rdn) if rdn_value != cur_rec.get(rdn_attr)[0]: dn_parts[0] = [(rdn_attr, rdn_value, 1)] raw_utf8_rdn = rdn_attr + '=' + rdn_value new_rdn = escape_dn(raw_utf8_rdn) connection.modrdn_s(dn, new_rdn) dn = dn2str(dn_parts) if mod_list: connection.modify_s(dn, mod_list) else: debug_msg = 'Nothing to modify: %s' % dn self.logger().debug(debug_msg) except ldap.REFERRAL, e: connection = self._handle_referral(e) connection.modify_s(dn, mod_list)
def normalize_dn(dn, flags=0): return dn2str(str2dn(dn, flags=flags))
def _write_json(self): self.logger.info('Gathering AppAttributes...') locales = [locale.split('.')[0] for locale in self.ucr.get('locale', 'en_US.UTF-8:UTF-8').split() if '.' in locale] if 'en_US' not in locales: locales.append('en_US') cache = {} custom_attributes_base = 'cn=custom attributes,cn=univention,%s' % self.ucr.get('ldap/base') for current_locale in locales: locale_cache = cache[current_locale] = {} app_objs = search_objects('appcenter/app', self.lo, self.po) apps = {} for app_obj in app_objs: app_version = app_obj['version'] app_id = app_obj['id'][:-len(app_version) - 1] app = AllApps().find(app_id, app_version=app_version) if app: if app.id in apps: if apps[app.id] > app: continue apps[app.id] = app for app in apps.itervalues(): for attribute in app.umc_options_attributes: attribute, option_name = (attribute.split(':', 1) * 2)[:2] objs = search_objects('settings/extended_attribute', self.lo, self.po, custom_attributes_base, CLIName=attribute) for obj in objs: for module in obj['module']: if search_objects('settings/extended_options', self.lo, self.po, custom_attributes_base, objectClass=obj['objectClass'], module=module): # a newer version of the App is installed that uses the # superior settings/extended_option continue if module not in locale_cache: locale_cache[module] = {} option_def = locale_cache[module] group_name = obj['groupName'] for loc, desc in obj['translationGroupName']: if loc == current_locale: group_name = desc break tab_name = obj['tabName'] for loc, desc in obj['translationTabName']: if loc == current_locale: tab_name = desc break short_description = obj['shortDescription'] for loc, desc in obj['translationShortDescription']: if loc == current_locale: short_description = desc break if obj['syntax'] == 'boolean': boolean_values = ['1', '0'] elif obj['syntax'] in ['TrueFalseUp', 'TrueFalseUpper']: boolean_values = ['TRUE', 'FALSE'] elif obj['syntax'] == 'TrueFalse': boolean_values = ['true', 'false'] elif obj['syntax'] == 'OkOrNot': boolean_values = ['OK', 'Not'] else: continue default = int(obj['default'] == boolean_values[0]) attributes = [] layout = [] option_def[option_name] = { 'label': group_name or tab_name, 'description': short_description, 'default': default, 'boolean_values': boolean_values, 'attributes': attributes, 'layout': layout, 'attribute_name': obj['CLIName'], } base = dn2str(str2dn(obj.dn)[1:]) for _obj in search_objects('settings/extended_attribute', self.lo, self.po, base, univentionUDMPropertyModule=module): if obj.dn == _obj.dn: continue if _obj['disableUDMWeb'] == '1': continue attributes.append(_obj['CLIName']) if _obj['tabAdvanced']: group_name = _obj['tabName'] for loc, desc in _obj['translationTabName']: if loc == current_locale: group_name = desc break group_position = _obj['tabPosition'] else: group_name = _obj['groupName'] for loc, desc in _obj['translationGroupName']: if loc == current_locale: group_name = desc break group_position = _obj['groupPosition'] for group in layout: if group['label'] == group_name: break else: group = { 'label': group_name, 'description': '', 'advanced': False, 'is_app_tab': False, 'layout': [], 'unsorted': [], } layout.append(group) group_layout = group['layout'] if group_position: group_position = int(group_position) while len(group_layout) < group_position: group_layout.append([]) group_layout[group_position - 1].append(_obj['CLIName']) else: group['unsorted'].append(_obj['CLIName']) for group in layout: unsorted = group.pop('unsorted') if unsorted: group['layout'].append(unsorted) self.logger.info('Finished') tmp_fname = FNAME + '.tmp' with open(tmp_fname, 'w') as fd: json.dump(cache, fd) shutil.move(tmp_fname, FNAME)
def get_parent_dn(self, dn=None): if not dn: dn = self.__base.dn return dn2str(str2dn(dn)[1:])
def __str__(self): return dn2str([self.to_openldap()])
def move(self, new_base, recursive=False): """ Moves the currently proxied object to another base """ # Check ACLs # to move an object we need the 'w' (write) right on the virtual attribute base, # the d (delete) right for the complete source object and at least the c (create) # right on the target base. if self.__current_user is not None: # Prepare ACL results topic_user = "******" % (self.__env.domain, self.__base_type) topic_base = "%s.objects.%s.attributes.base" % (self.__env.domain, self.__base_type) allowed_base_mod = self.__acl_resolver.check(self.__current_user, topic_base, "w", base=self.dn) allowed_delete = self.__acl_resolver.check(self.__current_user, topic_user, "d", base=self.dn) allowed_create = self.__acl_resolver.check(self.__current_user, topic_user, "c", base=new_base) # Check for 'w' access to attribute base if not allowed_base_mod: self.__log.debug("user '%s' has insufficient permissions to move %s, required is %s:%s on %s" % ( self.__current_user, self.__base.dn, topic_base, "w", self.__base.dn)) raise ACLException(C.make_error('PERMISSION_MOVE', source=self.__base.dn, target=new_base)) # Check for 'd' permission on the source object if not allowed_delete: self.__log.debug("user '%s' has insufficient permissions to move %s, required is %s:%s on %s" % ( self.__current_user, self.__base.dn, topic_user, "d", self.__base.dn)) raise ACLException(C.make_error('PERMISSION_MOVE', source=self.__base.dn, target=new_base)) # Check for 'c' permission on the source object if not allowed_create: self.__log.debug("user '%s' has insufficient permissions to move %s, required is %s:%s on %s" % ( self.__current_user, self.__base.dn, topic_user, "c", new_base)) raise ACLException(C.make_error('PERMISSION_MOVE', source=self.__base.dn, target=new_base)) zope.event.notify(ObjectChanged("pre object move", self.__base)) if recursive: old_base = self.__base.dn try: child_new_base = dn2str([str2dn(self.__base.dn.encode('utf-8'))[0]]).decode('utf-8') + "," + new_base # Get primary backend of the object to be moved p_backend = getattr(self.__base, '_backend') # Traverse tree and find different backends foreign_backends = {} index = PluginRegistry.getInstance("ObjectIndex") children = index.search({"dn": re.compile("^(.*,)?" + re.escape(self.__base.dn) + "$")}, {'dn': 1, '_type': 1}) # Note all elements with different backends for v in children: cdn = v['dn'] ctype = v['_type'] cback = self.__factory.getObjectTypes()[ctype]['backend'] if cback != p_backend: if not cback in foreign_backends: foreign_backends = [] foreign_backends[cback].append(cdn.decode('utf-8')) # Only keep the first per backend that is close to the root root_elements = {} for fbe, fdns in foreign_backends.items(): fdns.sort(key=len) root_elements[fbe] = fdns[0] # Move base object self.__base.move(new_base) # Move additional backends if needed for fbe, fdn in root_elements.items(): # Get new base of child new_child_dn = fdn[:len(fdn) - len(old_base)] + child_new_base new_child_base = dn2str(str2dn(new_child_dn.encode('utf-8'))[1:]).decode('utf-8') # Select objects with different base and trigger a move, the # primary backend move will be triggered and do a recursive # move for that backend. obj = self.__factory.getObject(children[fdn], fdn) obj.move(new_child_base) # Update all DN references # Emit 'post move' events for cdn, ctype in children.items(): # Don't handle objects that already have been moved if cdn in root_elements.values(): continue # These objects have been moved automatically. Open # them and let them do a simulated move to update # their refs. new_cdn = cdn[:len(cdn) - len(old_base)] + child_new_base obj = self.__factory.getObject(ctype, new_cdn) obj.simulate_move(cdn) zope.event.notify(ObjectChanged("post object move", self.__base)) return True except Exception as e: from traceback import print_exc print_exc() self.__log.error("moving object '%s' from '%s' to '%s' failed: %s" % (self.__base.uuid, old_base, new_base, str(e))) return False else: # Test if we've children if len(self.__factory.getObjectChildren(self.__base.dn)): raise ProxyException(C.make_error('OBJECT_HAS_CHILDREN', target=self.__base.dn)) res = self.__base.move(new_base) if res: zope.event.notify(ObjectChanged("post object move", self.__base)) return res
def canonical_dn(dn): return dn2str(str2dn(dn))