def getFailoverConfig(self): """ Return failover configuration of server """ serviceData = self.getService()[1] primaryDN = False if 'dhcpPrimaryDN' in serviceData: primaryDN = serviceData['dhcpPrimaryDN'][0] primaryName = str2dn(primaryDN)[0][0][1] secondaryDN = False if 'dhcpSecondaryDN' in serviceData: secondaryDN = serviceData['dhcpSecondaryDN'][0] secondaryName = str2dn(secondaryDN)[0][0][1] if primaryDN and secondaryDN: pattern = 'failover.*address (?P<primaryIp>[0-9.]+); port (?P<primaryPort>[0-9]+); peer address (?P<secondaryIp>[0-9.]+); peer port (?P<secondaryPort>[0-9]+); max-response-delay (?P<delay>[0-9]+); max-unacked-updates (?P<update>[0-9]+); load balance max seconds (?P<balance>[0-9]+); mclt (?P<mclt>[0-9]+); split (?P<split>[0-9]+);' for statement in self.getObjectStatements(primaryDN): m = re.match(pattern, statement) if m: return { 'primary': [primaryName], 'secondary': [secondaryName], 'primaryIp': [m.group("primaryIp")], 'secondaryIp': [m.group("secondaryIp")], 'primaryPort': [m.group("primaryPort")], 'secondaryPort': [m.group("secondaryPort")], 'delay': [m.group("delay")], 'update': [m.group("update")], "balance": [m.group("balance")], 'mclt': [m.group("mclt")], "split": [m.group("split")] } return dict({ 'primary': [primaryName], 'secondary': [secondaryName] }.items() + self.getFailoverDefaultValues().items()) elif primaryDN: return dict({ 'primary': [primaryName] }.items() + self.getFailoverDefaultValues().items()) else: return self.getFailoverDefaultValues()
def setFailoverConfig(self, masterIp, slaveIp, serverPort=647, peerPort=647, delay=30, update=10, balance=3, mclt=1800, split=128): """ Setup the failover configuration on servers """ serviceData = self.getService()[1] if 'dhcpPrimaryDN' in serviceData and 'dhcpSecondaryDN' in serviceData: primaryDN = serviceData['dhcpPrimaryDN'][0] secondaryDN = serviceData['dhcpSecondaryDN'][0] primaryName = str2dn(primaryDN)[0][0][1] secondaryName = str2dn(secondaryDN)[0][0][1] self.setServerFailover(primaryDN, primaryName, "primary", masterIp, slaveIp, int(serverPort), int(peerPort), int(delay), int(update), int(balance), int(mclt), int(split)) self.setServerFailover(secondaryDN, secondaryName, "secondary", slaveIp, masterIp, int(peerPort), int(serverPort), int(delay), int(update), int(balance), int(mclt), int(split)) else: return False
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 handle(self, dn, entry): if (dn != 'o=gluu') and (dn != 'ou=appliances,o=gluu'): self.DNs.append(dn) self.entries[dn] = entry if not self.inumOrg and 'gluuOrganization' in entry['objectClass']: self.inumOrg_dn = dn dne = str2dn(dn) self.inumOrg = dne[0][0][1] if not self.inumApllience and 'gluuAppliance' in entry['objectClass']: self.inumApllience_dn = dn dne = str2dn(dn) self.inumApllience = dne[0][0][1]
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 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 _rdns_from_value(self, value): if isinstance(value, six.string_types): try: if isinstance(value, six.text_type): value = val_encode(value) rdns = str2dn(value) except DECODING_ERROR: raise ValueError("malformed RDN string = \"%s\"" % value) for rdn in rdns: sort_avas(rdn) elif isinstance(value, DN): rdns = value._copy_rdns() elif isinstance(value, (tuple, list, AVA)): ava = get_ava(value) rdns = [[ava]] elif isinstance(value, RDN): rdns = [value.to_openldap()] elif isinstance(value, cryptography.x509.name.Name): rdns = list( reversed([[ get_ava( _ATTR_NAME_BY_OID.get(ava.oid, ava.oid.dotted_string), ava.value) ] for ava in value])) else: raise TypeError( "must be str, unicode, tuple, Name, RDN or DN, got %s instead" % type(value)) return rdns
def _rdns_from_value(self, value): if isinstance(value, six.string_types): try: if isinstance(value, six.text_type): value = val_encode(value) rdns = str2dn(value) except DECODING_ERROR: raise ValueError("malformed RDN string = \"%s\"" % value) for rdn in rdns: sort_avas(rdn) elif isinstance(value, DN): rdns = value._copy_rdns() elif isinstance(value, (tuple, list, AVA)): ava = get_ava(value) rdns = [[ava]] elif isinstance(value, RDN): rdns = [value.to_openldap()] elif isinstance(value, cryptography.x509.name.Name): rdns = list(reversed([ [get_ava( _ATTR_NAME_BY_OID.get(ava.oid, ava.oid.dotted_string), ava.value)] for ava in value ])) else: raise TypeError( "must be str, unicode, tuple, Name, RDN or DN, got %s instead" % type(value)) return rdns
def str2rdn(value): try: rdns = str2dn(value.encode('utf-8')) except DECODING_ERROR: raise ValueError("malformed AVA string = \"%s\"" % value) if len(rdns) != 1: raise ValueError("multiple RDN's specified by \"%s\"" % (value)) return rdns[0]
def setFailoverConfig(self, masterIp, slaveIp, serverPort = 647, peerPort = 647, delay = 30, update = 10, balance = 3, mclt = 1800, split = 128): """ Setup the failover configuration on servers """ serviceData = self.getService()[1] if 'dhcpPrimaryDN' in serviceData and 'dhcpSecondaryDN' in serviceData: primaryDN = serviceData['dhcpPrimaryDN'][0] secondaryDN = serviceData['dhcpSecondaryDN'][0] primaryName = str2dn(primaryDN)[0][0][1] secondaryName = str2dn(secondaryDN)[0][0][1] self.setServerFailover(primaryDN, primaryName, "primary", masterIp, slaveIp, int(serverPort), int(peerPort), int(delay), int(update), int(balance), int(mclt), int(split)) self.setServerFailover(secondaryDN, secondaryName, "secondary", slaveIp, masterIp, int(peerPort), int(serverPort), int(delay), int(update), int(balance), int(mclt), int(split)) else: return False
def extract_cn(user_dn): """ Get cn from a user dn """ try: for rdn in dn.str2dn(user_dn): rdn = rdn[0] if rdn[0] == 'cn': return rdn[1] except Exception: return user_dn
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 __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 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 getFailoverConfig(self): """ Return failover configuration of server """ serviceData = self.getService()[1] primaryDN = False if 'dhcpPrimaryDN' in serviceData: primaryDN = serviceData['dhcpPrimaryDN'][0] primaryName = str2dn(primaryDN)[0][0][1] secondaryDN = False if 'dhcpSecondaryDN' in serviceData: secondaryDN = serviceData['dhcpSecondaryDN'][0] secondaryName = str2dn(secondaryDN)[0][0][1] if primaryDN and secondaryDN: pattern = 'failover.*address (?P<primaryIp>[0-9.]+); port (?P<primaryPort>[0-9]+); peer address (?P<secondaryIp>[0-9.]+); peer port (?P<secondaryPort>[0-9]+); max-response-delay (?P<delay>[0-9]+); max-unacked-updates (?P<update>[0-9]+); load balance max seconds (?P<balance>[0-9]+); mclt (?P<mclt>[0-9]+); split (?P<split>[0-9]+);' for statement in self.getObjectStatements(primaryDN): m = re.match(pattern, statement) if m: return { 'primary': [primaryName], 'secondary': [secondaryName], 'primaryIp': [m.group("primaryIp")], 'secondaryIp': [m.group("secondaryIp")], 'primaryPort': [m.group("primaryPort")], 'secondaryPort': [m.group("secondaryPort")], 'delay': [m.group("delay")], 'update': [m.group("update")], "balance": [m.group("balance")], 'mclt': [m.group("mclt")], "split": [m.group("split")] } return dict({ 'primary': [primaryName], 'secondary': [secondaryName] }.items() + self.getFailoverDefaultValues().items()) elif primaryDN: return dict({'primary': [primaryName]}.items() + self.getFailoverDefaultValues().items()) else: return self.getFailoverDefaultValues()
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 create_base_c(instance, basedn): """Create the base country object""" c = Country(instance, dn=basedn) # Explode the dn to get the first bit. avas = dn.str2dn(basedn) c_ava = avas[0][0][1] c.create(properties={ 'c': c_ava, }) return c
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 create_base_cn(instance, basedn): """Create the base nsContainer object""" cn = nsContainer(instance, dn=basedn) # Explode the dn to get the first bit. avas = dn.str2dn(basedn) cn_ava = avas[0][0][1] cn.create(properties={ # I think in python 2 this forces unicode return ... 'cn': cn_ava, }) return cn
def _refresh(self): try: with open(self._password_file) as fd: password = fd.read().rstrip("\n") except EnvironmentError: get_logger("cache").warning("Unable to read {}".format( self._password_file)) return None con = ldap.initialize(self._ldap_uri) con.simple_bind_s(self._bind_dn, password) ldap_content = {} users = {} groups = con.search_s(self._ldap_base, ldap.SCOPE_SUBTREE, u"(objectClass=posixGroup)") for dn, attrs in groups: usernames = [] groups = [] member_uids = [ member.decode("utf-8").lower() for member in attrs.get("memberUid", []) ] unique_members = [ member.decode("utf-8").lower() for member in attrs.get("uniqueMember", []) ] for member in member_uids: if not member.endswith("$"): usernames.append(member.lower()) for member in unique_members: if member.startswith("cn="): member_uid = str2dn(member)[0][0][1] if "{}$".format(member_uid) not in member_uids: groups.append(member) ldap_content[dn.lower()] = { "usernames": usernames, "groups": groups } groups_with_nested_groups = {} for group_dn in ldap_content: self._nested_groups(group_dn, ldap_content, groups_with_nested_groups) for group_dn, attrs in ldap_content.items(): for user in attrs["usernames"]: groups = users.setdefault(user, set()) groups.update(groups_with_nested_groups[group_dn]) users = dict((user, list(groups)) for user, groups in users.items()) with tempfile.NamedTemporaryFile(mode="w", delete=False) as fd: json.dump(users, fd, sort_keys=True, indent=4) return fd
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_key_from(dn): dns = [] for d in str2dn(dn): for rd in d: if rd[0] == 'o' and rd[1] == 'gluu': continue dns.append(rd[1]) dns.reverse(), key = '_'.join(dns) if not key: key = '_' return key
def create_without_hooks(self, lo, validate): # prepare LDAP: create containers where this basic group lives if necessary container_dn = self.get_own_container()[:-len(ucr.get('ldap/base')) - 1] containers = str2dn(container_dn) super_container_dn = ucr.get('ldap/base') for container_info in reversed(containers): dn_part, cn = container_info[0][0:2] if dn_part.lower() == 'ou': container = OU(name=cn) else: container = Container(name=cn, school='', group_path='1') container.position = super_container_dn super_container_dn = container.create(lo, False) return super(BasicGroup, self).create_without_hooks(lo, validate)
def create_base_orgunit(instance, basedn): """Create the base org unit object for a org unit""" orgunit = OrganizationalUnit(instance, dn=basedn) # Explode the dn to get the first bit. avas = dn.str2dn(basedn) ou_ava = avas[0][0][1] orgunit.create( properties={ # I think in python 2 this forces unicode return ... 'ou': ou_ava, 'description': basedn, }) return orgunit
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 create_base_domain(instance, basedn): """Create the base domain object""" domain = Domain(instance, dn=basedn) # Explode the dn to get the first bit. avas = dn.str2dn(basedn) dc_ava = avas[0][0][1] domain.create(properties={ # I think in python 2 this forces unicode return ... 'dc': dc_ava, 'description': basedn, }) # ACI can be added later according to your needs return domain
def getVAliasUsers(self, alias): """ Get the user list of a virtual alias entry @param alias: virtual alias name @type alias: str """ dn = "mailalias=" + alias + ", " + self.conf.vAliasesDN s = self.l.search_s(dn, ldap.SCOPE_BASE) c, attrs = s[0] users = [] if "mailaliasmember" in attrs: for user in attrs["mailaliasmember"]: # get the user uid users.append(str2dn(user)[0][0][1]) return users
def _rdns_from_value(self, value): if isinstance(value, six.string_types): try: if isinstance(value, unicode): value = value.encode('utf-8') rdns = str2dn(value) except DECODING_ERROR: raise ValueError("malformed RDN string = \"%s\"" % value) for rdn in rdns: sort_avas(rdn) elif isinstance(value, DN): rdns = value._copy_rdns() elif isinstance(value, (tuple, list, AVA)): ava = get_ava(value) rdns = [[ava]] elif isinstance(value, RDN): rdns = [value.to_openldap()] else: raise TypeError("must be str, unicode, tuple, or RDN or DN, got %s instead" % type(value)) return rdns
def _rdns_from_value(self, value): if isinstance(value, six.string_types): try: if isinstance(value, six.text_type): value = val_encode(value) rdns = str2dn(value) except DECODING_ERROR: raise ValueError("malformed RDN string = \"%s\"" % value) for rdn in rdns: sort_avas(rdn) elif isinstance(value, DN): rdns = value._copy_rdns() elif isinstance(value, (tuple, list, AVA)): ava = get_ava(value) rdns = [[ava]] elif isinstance(value, RDN): rdns = [value.to_openldap()] else: raise TypeError( "must be str, unicode, tuple, or RDN or DN, got %s instead" % type(value)) return rdns
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 get_parent_dn(self, dn=None): if not dn: dn = self.__base.dn return dn2str(str2dn(dn)[1:])
def get_parent_dn(self): return dn2str(str2dn(self.__base.dn.encode('utf-8'))[1:]).decode('utf-8')
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 _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 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 _apply(self): # Create the base domain object domain = Domain(self._instance, dn=self._basedn) # Explode the dn to get the first bit. avas = dn.str2dn(self._basedn) dc_ava = avas[0][0][1] domain.create(properties={ # I think in python 2 this forces unicode return ... 'dc': dc_ava, 'description': self._basedn, 'aci': [ # Allow reading the base domain object '(targetattr="dc || description || objectClass")(targetfilter="(objectClass=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search, compare)(userdn="ldap:///anyone");)', # Allow reading the ou '(targetattr="ou || objectClass")(targetfilter="(objectClass=organizationalUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compare)(userdn="ldap:///anyone");)' ] }) # Create the 389 service container # This could also move to be part of core later .... hidden_containers = nsHiddenContainers(self._instance, self._basedn) ns389container = hidden_containers.create(properties={ 'cn': '389_ds_system' }) # Create our ous. ous = OrganisationalUnits(self._instance, self._basedn) ous.create(properties = { 'ou': 'groups', 'aci': [ # Allow anon partial read '(targetattr="cn || member || gidNumber || nsUniqueId || description || objectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enable anyone group read"; allow (read, search, compare)(userdn="ldap:///anyone");)', # Allow group_modify to modify but not create groups '(targetattr="member")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enable group_modify to alter members"; allow (write)(groupdn="ldap:///cn=group_modify,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn), # Allow group_admin to fully manage groups (posix or not). '(targetattr="cn || member || gidNumber || description || objectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enable group_admin to manage groups"; allow (write, add, delete)(groupdn="ldap:///cn=group_admin,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn), ] }) ous.create(properties = { 'ou': 'people', 'aci': [ # allow anon partial read. '(targetattr="objectClass || description || nsUniqueId || uid || displayName || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn || memberOf || mail || nsSshPublicKey || nsAccountLock || userCertificate")(targetfilter="(objectClass=posixaccount)")(version 3.0; acl "Enable anyone user read"; allow (read, search, compare)(userdn="ldap:///anyone");)', # allow self partial mod '(targetattr="displayName || nsSshPublicKey")(version 3.0; acl "Enable self partial modify"; allow (write)(userdn="ldap:///self");)', # Allow self full read '(targetattr="legalName || telephoneNumber || mobile")(targetfilter="(objectClass=nsPerson)")(version 3.0; acl "Enable self legalname read"; allow (read, search, compare)(userdn="ldap:///self");)', # Allow reading legal name '(targetattr="legalName || telephoneNumber")(targetfilter="(objectClass=nsPerson)")(version 3.0; acl "Enable user legalname read"; allow (read, search, compare)(groupdn="ldap:///cn=user_private_read,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn), # These below need READ so they can read userPassword and legalName # Allow user admin create mod '(targetattr="uid || description || displayName || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalName || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objectClass=nsAccount))")(version 3.0; acl "Enable user admin create"; allow (write, add, delete, read)(groupdn="ldap:///cn=user_admin,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn), # Allow user mod mod only '(targetattr="uid || description || displayName || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalName || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objectClass=nsAccount))")(version 3.0; acl "Enable user modify to change users"; allow (write, read)(groupdn="ldap:///cn=user_modify,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn), # Allow user_pw_admin to nsaccountlock and password '(targetattr="userPassword || nsAccountLock || userCertificate || nsSshPublicKey")(targetfilter="(objectClass=nsAccount)")(version 3.0; acl "Enable user password reset"; allow (write, read)(groupdn="ldap:///cn=user_passwd_reset,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn), ] }) ous.create(properties = { 'ou': 'permissions', }) ous.create(properties = { 'ou': 'services', 'aci': [ # Minimal service read '(targetattr="objectClass || description || nsUniqueId || cn || memberOf || nsAccountLock ")(targetfilter="(objectClass=netscapeServer)")(version 3.0; acl "Enable anyone service account read"; allow (read, search, compare)(userdn="ldap:///anyone");)', ] }) # Create the demo user users = nsUserAccounts(self._instance, self._basedn) users.create(properties={ 'uid': 'demo_user', 'cn': 'Demo User', 'displayName': 'Demo User', 'legalName': 'Demo User Name', 'uidNumber': '99998', 'gidNumber': '99998', 'homeDirectory': '/var/empty', 'loginShell': '/bin/false', 'nsAccountlock': 'true' }) # Create the demo group groups = PosixGroups(self._instance, self._basedn) groups.create(properties={ 'cn' : 'demo_group', 'gidNumber': '99999' }) # Create the permission groups required for the acis permissions = Groups(self._instance, self._basedn, rdn='ou=permissions') permissions.create(properties={ 'cn': 'group_admin', }) permissions.create(properties={ 'cn': 'group_modify', }) permissions.create(properties={ 'cn': 'user_admin', }) permissions.create(properties={ 'cn': 'user_modify', }) permissions.create(properties={ 'cn': 'user_passwd_reset', }) permissions.create(properties={ 'cn': 'user_private_read', })
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, self.ldap_encoding) 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 list(attrs.items()): if key.endswith(';binary'): key = key[:-7] is_binary = True else: is_binary = False if not isinstance(key, six.binary_type): key = self._encode_incoming(key) if not is_binary: if isinstance(values, six.string_types): values = [ self._encode_incoming(x) for x in values.split(';') ] else: values = [self._encode_incoming(x) for x in values] if isinstance(key, six.text_type): key = self._encode_incoming(key) if mod_type is None: if key not in cur_rec and values != [b'']: mod_list.append((ldap.MOD_ADD, key, values)) elif cur_rec.get(key, [b'']) != values and \ values not in ([b''], []): mod_list.append((ldap.MOD_REPLACE, key, values)) elif key in cur_rec and values in ([b''], []): mod_list.append((ldap.MOD_DELETE, key, None)) elif mod_type in (ldap.MOD_ADD, ldap.MOD_DELETE) and \ values == [b'']: 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)) attrs[key] = values try: connection = self.connect(bind_dn=bind_dn, bind_pwd=bind_pwd) dn_parts = str2dn(dn) clean_dn_parts = [] for dn_part in dn_parts: for (attr_name, attr_val, flag) in dn_part: if isinstance(attr_name, six.text_type): attr_name = self._encode_incoming(attr_name) if isinstance(attr_val, six.text_type): attr_val = self._encode_incoming(attr_val) clean_dn_parts.append([(attr_name, attr_val, flag)]) rdn_attr = clean_dn_parts[0][0][0] raw_rdn = attrs.get(rdn_attr, '') if isinstance(raw_rdn, six.string_types): 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]: clean_dn_parts[0] = [(rdn_attr, rdn_value, 1)] dn_parts[0] = [(rdn_attr, raw_rdn[0], 1)] raw_utf8_rdn = rdn_attr + b'=' + rdn_value new_rdn = escape_dn(raw_utf8_rdn, self.ldap_encoding) connection.modrdn_s(dn, new_rdn) dn = dn2str(clean_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 as e: connection = self._handle_referral(e) connection.modify_s(dn, mod_list)
def group_names(group_dn_list): return {str2dn(group)[0][0][1].lower() for group in group_dn_list}
def canonical_dn(dn): return dn2str(str2dn(dn))
def _extract_country(self, user_dn): """ Get country from a user dn """ for rdn in dn.str2dn(user_dn): rdn = rdn[0] if rdn[0] == self.country_attr: return rdn[1]
def _extract_cn(self, user_dn): """ Get cn from a user dn """ for rdn in dn.str2dn(user_dn): rdn = rdn[0] if rdn[0] == self.login_attr: return rdn[1]
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 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 is_immediate_subdn(subdn, dn): subdn_comps = str2dn(subdn) dn_comps = str2dn(dn) return len(subdn_comps) > 0 and subdn_comps[1:] == dn_comps
def _rdn(_dn): return str2dn(_dn)[0][0][1]
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')