def test_all_servers_in_location(self): """Put master (as second server) to location and test if records changed properly """ # master --> location paris self.master.run_command([ 'ipa', 'server-mod', self.master.hostname, '--location', self.LOC_PARIS ]) tasks.restart_named(self.master) servers_prague_loc = ( (self.PRIO_LOW, self.WEIGHT, DNSName(self.master.hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) domain_prague_loc = (DNSName('{}._locations'.format(self.LOC_PRAGUE)) + DNSName(self.master.domain.name).make_absolute()) servers_paris_loc = ( (self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)), (self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) domain_paris_loc = (DNSName('{}._locations'.format(self.LOC_PARIS)) + DNSName(self.master.domain.name).make_absolute()) self._test_against_server(self.replicas[0].ip, domain_prague_loc, servers_prague_loc) for ip in (self.replicas[1].ip, self.master.ip): self._test_against_server(ip, domain_paris_loc, servers_paris_loc)
def test_change_weight(self): """Change weight of master and test if records changed properly """ new_weight = 2000 self.master.run_command([ 'ipa', 'server-mod', self.master.hostname, '--service-weight', str(new_weight) ]) # all servers must be restarted tasks.restart_named(self.master, self.replicas[0], self.replicas[1]) servers_prague_loc = ( (self.PRIO_LOW, new_weight, DNSName(self.master.hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) domain_prague_loc = (DNSName('{}._locations'.format(self.LOC_PRAGUE)) + DNSName(self.master.domain.name).make_absolute()) servers_paris_loc = ( (self.PRIO_HIGH, new_weight, DNSName(self.master.hostname)), (self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) domain_paris_loc = (DNSName('{}._locations'.format(self.LOC_PARIS)) + DNSName(self.master.domain.name).make_absolute()) self._test_against_server(self.replicas[0].ip, domain_prague_loc, servers_prague_loc) for ip in (self.replicas[1].ip, self.master.ip): self._test_against_server(ip, domain_paris_loc, servers_paris_loc)
def fake_query(qname, rdtype=rdatatype.A, rdclass=rdataclass.IN, count=1, fake_txt=False): """Fake a DNS query, returning count responses to the request Three kinds of lookups are faked: 1. A query for A records for a service will return the count as requested in the test. This simulates lookups for the ipa-ca A record. To force a difference in responses one can vary the count. 2. AAAA records are not yet supported, return no answer 3. TXT queries will return the Kerberos realm fake_txt will set an invalid Kerberos realm entry to provoke a warning. """ m = message.Message() if rdtype == rdatatype.A: fqdn = DNSName(qname) fqdn = fqdn.make_absolute() answers = Answer(fqdn, rdataclass.IN, rdatatype.A, m, raise_on_no_answer=False) rlist = rrset.from_text_list(fqdn, 86400, rdataclass.IN, rdatatype.A, gen_addrs(count)) answers.rrset = rlist elif rdtype == rdatatype.AAAA: raise NoAnswer(response=Response('no AAAA')) elif rdtype == rdatatype.TXT: if fake_txt: realm = 'FAKE_REALM' else: realm = m_api.env.realm qname = DNSName('_kerberos.' + m_api.env.domain) qname = qname.make_absolute() answers = Answer(qname, rdataclass.IN, rdatatype.TXT, m, raise_on_no_answer=False) rlist = rrset.from_text_list(qname, 86400, rdataclass.IN, rdatatype.TXT, [realm]) answers.rrset = rlist return answers
def test_one_replica_in_location(self): """Put one replica to location and test if records changed properly """ # create location prague, replica0 --> location prague self.master.run_command(['ipa', 'location-add', self.LOC_PRAGUE]) self.master.run_command([ 'ipa', 'server-mod', self.replicas[0].hostname, '--location', self.LOC_PRAGUE ]) tasks.restart_named(self.replicas[0]) servers_without_loc = ( (self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) domain_without_loc = DNSName(self.master.domain.name).make_absolute() servers_prague_loc = ( (self.PRIO_LOW, self.WEIGHT, DNSName(self.master.hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) domain_prague_loc = (DNSName('{}._locations'.format(self.LOC_PRAGUE)) + DNSName(self.master.domain.name).make_absolute()) self._test_against_server(self.replicas[0].ip, domain_prague_loc, servers_prague_loc) for ip in (self.master.ip, self.replicas[1].ip): self._test_against_server(ip, domain_without_loc, servers_without_loc)
def test_without_locations(self): """Servers are not in locations, this tests if basic system records are generated properly""" domain = DNSName(self.master.domain.name).make_absolute() expected_servers = ( (self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)), ) for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip): self._test_against_server(ip, domain, expected_servers)
def detect_dns_zone_realm_type(api, domain): """ Detects the type of the realm that the given DNS zone belongs to. Note: This method is heuristic. Possible values: - 'current': For IPA domains belonging in the current realm. - 'foreign': For domains belonging in a foreing kerberos realm. - 'unknown': For domains whose allegiance could not be detected. """ # First, try to detect _kerberos TXT record in the domain # This would indicate that the domain belongs to IPA realm kerberos_prefix = DNSName('_kerberos') domain_suffix = DNSName(domain) kerberos_record_name = kerberos_prefix + domain_suffix try: result = resolver.query(kerberos_record_name, rdatatype.TXT) answer = result.response.answer # IPA domain will have only one _kerberos TXT record if (len(answer) == 1 and len(answer[0]) == 1 and answer[0].rdtype == rdatatype.TXT): record = answer[0][0] # If the record contains our current realm, it is 'ipa-current' if record.to_text() == '"{0}"'.format(api.env.realm): return 'current' else: return 'foreign' except DNSException: pass # Try to detect AD specific record in the zone. # This would indicate that the domain belongs to foreign (AD) realm gc_prefix = DNSName('_ldap._tcp.gc._msdcs') ad_specific_record_name = gc_prefix + domain_suffix try: # The presence of this record is enough, return foreign in such case result = resolver.query(ad_specific_record_name, rdatatype.SRV) return 'foreign' except DNSException: pass # If we could not detect type with certainity, return unknown return 'unknown'
def __init__(self, name, description=u"Location description"): super(LocationTracker, self).__init__(default_version=None) # ugly hack to allow testing invalid inputs try: self.idnsname_obj = DNSName(name) except Exception: self.idnsname_obj = DNSName(u"placeholder-for-invalid-value") self.idnsname = name self.description = description self.dn = DN(('idnsname', self.idnsname_obj.ToASCII()), 'cn=locations', 'cn=etc', self.api.env.basedn) self.servers = {}
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) if options.get('ip_address') and dns_container_exists(ldap): parts = keys[-1].split('.') host = parts[0] domain = unicode('.'.join(parts[1:])) check_reverse = not options.get('no_reverse', False) add_records_for_host_validation('ip_address', DNSName(host), DNSName(domain).make_absolute(), options['ip_address'], check_forward=True, check_reverse=check_reverse) if not options.get('force', False) and not 'ip_address' in options: util.verify_host_resolvable(keys[-1], self.log) if 'locality' in entry_attrs: entry_attrs['l'] = entry_attrs['locality'] entry_attrs['cn'] = keys[-1] entry_attrs['serverhostname'] = keys[-1].split('.', 1)[0] if not entry_attrs.get('userpassword', False) and not options.get('random', False): entry_attrs['krbprincipalname'] = 'host/%s@%s' % ( keys[-1], self.api.env.realm ) if 'krbprincipalaux' not in entry_attrs['objectclass']: entry_attrs['objectclass'].append('krbprincipalaux') if 'krbprincipal' not in entry_attrs['objectclass']: entry_attrs['objectclass'].append('krbprincipal') else: if 'krbprincipalaux' in entry_attrs['objectclass']: entry_attrs['objectclass'].remove('krbprincipalaux') if 'krbprincipal' in entry_attrs['objectclass']: entry_attrs['objectclass'].remove('krbprincipal') if options.get('random'): entry_attrs['userpassword'] = ipa_generate_password(characters=host_pwd_chars) # save the password so it can be displayed in post_callback setattr(context, 'randompassword', entry_attrs['userpassword']) certs = options.get('usercertificate', []) certs_der = [x509.normalize_certificate(c) for c in certs] for cert in certs_der: x509.verify_cert_subject(ldap, keys[-1], cert) entry_attrs['usercertificate'] = certs_der entry_attrs['managedby'] = dn entry_attrs['objectclass'].append('ieee802device') entry_attrs['objectclass'].append('ipasshhost') update_krbticketflags(ldap, entry_attrs, attrs_list, options, False) if 'krbticketflags' in entry_attrs: entry_attrs['objectclass'].append('krbticketpolicyaux') return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) exc = None if dns_container_exists(ldap): try: parts = keys[-1].split('.') host = parts[0] domain = unicode('.'.join(parts[1:])) if options.get('ip_address'): add_reverse = not options.get('no_reverse', False) add_records_for_host(DNSName(host), DNSName(domain).make_absolute(), options['ip_address'], add_forward=True, add_reverse=add_reverse) del options['ip_address'] update_sshfp_record(domain, unicode(parts[0]), entry_attrs) except Exception as e: exc = e if options.get('random', False): try: entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword')) except AttributeError: # On the off-chance some other extension deletes this from the # context, don't crash. pass if exc: raise errors.NonFatalError( reason=_('The host was added but the DNS update failed with: %(exc)s') % dict(exc=exc) ) set_certificate_attrs(entry_attrs) set_kerberos_attrs(entry_attrs, options) rename_ipaallowedtoperform_from_ldap(entry_attrs, options) if options.get('all', False): entry_attrs['managing'] = self.obj.get_managed_hosts(dn) self.obj.get_password_attributes(ldap, dn, entry_attrs) if entry_attrs['has_password']: # If an OTP is set there is no keytab, at least not one # fetched anywhere. entry_attrs['has_keytab'] = False convert_sshpubkey_post(ldap, dn, entry_attrs) return dn
def test_nsupdate_without_locations(self): """Test nsupdate file generated by dns-update-system-records Remove all records and the use nsupdate to restore state and test if all record are there as expected""" domain = DNSName(self.master.domain.name).make_absolute() filepath = '/tmp/ipa.nsupdate' self.master.run_command([ 'ipa', 'dns-update-system-records', '--dry-run', '--out', filepath ]) # delete original records first for rname, _port in IPA_DEFAULT_MASTER_SRV_REC: self.master.run_command( ['ipa', 'dnsrecord-del', str(domain), str(rname), '--del-all']) # allow unauthenticates nsupdate (no need to testing authentication) self.master.run_command([ 'ipa', 'dnszone-mod', str(domain), '--update-policy=grant * wildcard *;' ]) self.master.run_command(['nsupdate', '-g', filepath]) time.sleep(5) # give time to named to process everything from update self.test_without_locations()
def remove_location_records(self, location): """ Remove all location records :param location: DNSName object :return: list of successfuly removed record names, list of record names that cannot be removed and returned exception in tuples [rname1, ...], [(rname2, exc), ...] """ success = [] failed = [] location = DNSName(location) loc_records = [] for records in (IPA_DEFAULT_MASTER_SRV_REC, IPA_DEFAULT_ADTRUST_SRV_REC, IPA_DEFAULT_NTP_SRV_REC): for name, _port in records: loc_records.append(name + self.__get_location_suffix(location)) for rname in loc_records: try: self.api_instance.Command.dnsrecord_del(self.domain_abs, rname, del_all=True) except errors.NotFound: pass except errors.PublicError as e: failed.append((rname, e)) else: success.append(rname) return success, failed
def __add_ca_records_from_hostname(self, zone_obj, hostname): assert isinstance(hostname, DNSName) and hostname.is_absolute() r_name = DNSName('ipa-ca') + self.domain_abs rrsets = [] end_time = time() + CA_RECORDS_DNS_TIMEOUT while time() < end_time: try: rrsets = resolve_rrsets(hostname, (rdatatype.A, rdatatype.AAAA)) except DNSException: # logging is done inside resolve_rrsets pass if rrsets: break sleep(5) if not rrsets: logger.error( 'unable to resolve host name %s to IP address, ' 'ipa-ca DNS record will be incomplete', hostname) return for rrset in rrsets: for rd in rrset: rdataset = zone_obj.get_rdataset(r_name, rd.rdtype, create=True) rdataset.add(rd, ttl=86400) # FIXME: use TTL from config
def __add_ca_records_from_hostname(self, zone_obj, hostname): assert isinstance(hostname, DNSName) and hostname.is_absolute() r_name = DNSName('ipa-ca') + self.domain_abs rrsets = None end_time = time() + CA_RECORDS_DNS_TIMEOUT while True: try: # function logs errors rrsets = installutils.resolve_rrsets_nss(hostname) except OSError: # also retry on EAI_AGAIN, EAI_FAIL pass if rrsets: break if time() >= end_time: break sleep(3) if not rrsets: logger.error( 'unable to resolve host name %s to IP address, ' 'ipa-ca DNS record will be incomplete', hostname) return for rrset in rrsets: for rd in rrset: rdataset = zone_obj.get_rdataset(r_name, rd.rdtype, create=True) rdataset.add(rd, ttl=self.TTL)
def _check_dnsrecords(self, hosts_expected, hosts_unexpected=()): domain = DNSName(self.master.domain.name).make_absolute() rset = [(rname, 'SRV') for rname, _port in IPA_DEFAULT_MASTER_SRV_REC] rset.append((DNSName(IPA_CA_RECORD), 'A')) for rname, rtype in rset: name_abs = rname.derelativize(domain) query = resolve_records_from_server(name_abs, rtype, self.master.ip) txt = query.to_text() for host in hosts_expected: value = host.hostname if rtype == 'SRV' else host.ip assert value in txt for host in hosts_unexpected: value = host.hostname if rtype == 'SRV' else host.ip assert value not in txt
def __add_kerberos_txt_rec(self, zone_obj): # FIXME: with external DNS, this should generate records for all # realmdomains r_name = DNSName('_kerberos') + self.domain_abs rd = rdata.from_text(rdataclass.IN, rdatatype.TXT, self.api_instance.env.realm) rdataset = zone_obj.get_rdataset(r_name, rdatatype.TXT, create=True) rdataset.add(rd, ttl=self.TTL)
def test_ntp_srv_records(self): """ Test NTP dns records with firstly removing the records and then using the nsupdate generated by dns-update-system-records.""" self.delete_update_system_records( rnames=(r[0] for r in IPA_DEFAULT_NTP_SRV_REC)) # we installed NTP only on master and replica[0] expected_servers = ( (self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)), (self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)), ) for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip): self._test_SRV_rec_against_server(ip, self.domain, expected_servers, rec_list=IPA_DEFAULT_NTP_SRV_REC)
def test_add_nonexistent_location_to_server(self, server): nonexistent_loc = DNSName(u'nonexistent-location') command = server.make_update_command( updates=dict(ipalocation_location=nonexistent_loc, )) with raises_exact( errors.NotFound( reason=u"{location}: location not found".format( location=nonexistent_loc))): command()
def add_srv_records(qname, port_map, priority=0, weight=100): rdlist = [] for name, port in port_map: answerlist = [] for host in qname: hostname = DNSName(host) rd = rdata.from_text( rdataclass.IN, rdatatype.SRV, '{0} {1} {2} {3}'.format(priority, weight, port, hostname.make_absolute())) answerlist.append(rd) rdlist.append(answerlist) return rdlist
def _get_location_dns_records_for_server(self, zone_obj, hostname, locations, roles=None, include_master_role=True): server = self.servers_data[hostname] if roles: eff_roles = server['roles'] & roles else: eff_roles = server['roles'] hostname_abs = DNSName(hostname).make_absolute() # generate locations specific records for location in locations: if location == self.servers_data[hostname]['location']: priority = self.PRIORITY_HIGH else: priority = self.PRIORITY_LOW if include_master_role: self.__add_srv_records(zone_obj, hostname_abs, IPA_DEFAULT_MASTER_SRV_REC, weight=server['weight'], priority=priority, location=location) self.__add_uri_records(zone_obj, hostname_abs, IPA_DEFAULT_MASTER_URI_REC, weight=server['weight'], priority=priority, location=location) if 'AD trust controller' in eff_roles: self.__add_srv_records(zone_obj, hostname_abs, IPA_DEFAULT_ADTRUST_SRV_REC, weight=server['weight'], priority=priority, location=location) if 'NTP server' in eff_roles: self.__add_srv_records(zone_obj, hostname_abs, IPA_DEFAULT_NTP_SRV_REC, weight=server['weight'], priority=priority, location=location) return zone_obj
def install(cls, mh): cls.domain = DNSName(cls.master.domain.name).make_absolute() tasks.install_master(cls.master, setup_dns=True) tasks.install_replica(cls.master, cls.replicas[0], setup_dns=True, setup_ca=False) tasks.install_replica(cls.master, cls.replicas[1], setup_dns=True, setup_ca=True) for host in (cls.master, cls.replicas[0], cls.replicas[1]): ldap = host.ldap_connect() tasks.wait_for_replication(ldap) # give time to named to retrieve new records time.sleep(20)
def __init__(self, api): hostname = DNSName(api.env.server).ToASCII() self._path = os.path.join(self._DIR, hostname) self._force_check = api.env.force_schema_check self._dict = {} # copy-paste from ipalib/rpc.py try: self._language = locale.setlocale(locale.LC_MESSAGES, '').split('.')[0].lower() except locale.Error: self._language = 'en_us' self._read()
def convert_location(self, entry_attrs, **options): """ Return a location name from DN """ if options.get('raw'): return converted_locations = [ DNSName(location_dn['idnsname']) for location_dn in entry_attrs.pop('ipalocation', []) ] if converted_locations: entry_attrs['ipalocation_location'] = converted_locations
def test_remove_replica_with_ntp(self): """Test NTP dns records after removing the replica with NTP""" tasks.uninstall_replica(self.master, self.replicas[0]) self.delete_update_system_records( rnames=(r[0] for r in IPA_DEFAULT_NTP_SRV_REC)) expected_servers = ((self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)), ) self._test_SRV_rec_against_server(self.master.ip, self.domain, expected_servers, rec_list=IPA_DEFAULT_NTP_SRV_REC)
def remove_replica_public_keys(self, replica_fqdn): ldap = api.Backend.ldap2 dn_base = DN(('cn', 'keys'), ('cn', 'sec'), ('cn', 'dns'), api.env.basedn) keylabel = replica_keylabel_template % DNSName(replica_fqdn).\ make_absolute().canonicalize().ToASCII() # get old keys from LDAP search_kw = { 'objectclass': u"ipaPublicKeyObject", 'ipk11Label': keylabel, 'ipk11Wrap': True, } filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) entries, _truncated = ldap.find_entries(filter=filter, base_dn=dn_base) for entry in entries: ldap.delete_entry(entry)
def validate_idna_domain(value): """ Validate if value is valid IDNA domain. If domain is not valid, raises ValueError :param value: :return: """ error = None try: DNSName(value) except dns.name.BadEscape: error = _('invalid escape code in domain name') except dns.name.EmptyLabel: error = _('empty DNS label') except dns.name.NameTooLong: error = _('domain name cannot be longer than 255 characters') except dns.name.LabelTooLong: error = _('DNS label cannot be longer than 63 characters') except dns.exception.SyntaxError: error = _('invalid domain name') else: #compare if IDN normalized and original domain match #there is N:1 mapping between unicode and IDNA names #user should use normalized names to avoid mistakes labels = re.split(u'[.\uff0e\u3002\uff61]', value, flags=re.UNICODE) try: for label in labels: label.encode("ascii") except UnicodeError: # IDNA is_nonnorm = any(encodings.idna.nameprep(x) != x for x in labels) if is_nonnorm: error = _("domain name '%(domain)s' should be normalized to" ": %(normalized)s") % { 'domain': value, 'normalized': '.'.join( [encodings.idna.nameprep(x) for x in labels]) } if error: raise ValueError(error)
def match_hostname(self, hostname): match_cert = {} match_cert['subject'] = match_subject = [] for rdn in self._cert.subject.rdns: match_rdn = [] for ava in rdn: if ava.oid == crypto_x509.oid.NameOID.COMMON_NAME: match_rdn.append(('commonName', ava.value)) match_subject.append(match_rdn) values = self.san_a_label_dns_names if values: match_cert['subjectAltName'] = match_san = [] for value in values: match_san.append(('DNS', value)) ssl.match_hostname(match_cert, DNSName(hostname).ToASCII())
def _ipa_obj_hook(dct, _iteritems=six.iteritems, _list=list): """JSON object hook :see: _JSONPrimer """ if '__base64__' in dct: return base64.b64decode(dct['__base64__']) elif '__datetime__' in dct: return datetime.datetime.strptime(dct['__datetime__'], LDAP_GENERALIZED_TIME_FORMAT) elif '__dns_name__' in dct: return DNSName(dct['__dns_name__']) else: # XXX tests assume tuples. Is this really necessary? for k, v in _iteritems(dct): if v.__class__ is _list: dct[k] = tuple(v) return dct
def _add_base_dns_records_for_server( self, zone_obj, hostname, roles=None, include_master_role=True, include_kerberos_realm=True, ): server = self.servers_data[hostname] if roles: eff_roles = server['roles'] & set(roles) else: eff_roles = server['roles'] hostname_abs = DNSName(hostname).make_absolute() if include_kerberos_realm: self.__add_kerberos_txt_rec(zone_obj) # get master records if include_master_role: self.__add_srv_records(zone_obj, hostname_abs, IPA_DEFAULT_MASTER_SRV_REC, weight=server['weight']) self.__add_uri_records(zone_obj, hostname_abs, IPA_DEFAULT_MASTER_URI_REC, weight=server['weight']) if 'CA server' in eff_roles: self.__add_ca_records_from_hostname(zone_obj, hostname_abs) if 'AD trust controller' in eff_roles: self.__add_srv_records(zone_obj, hostname_abs, IPA_DEFAULT_ADTRUST_SRV_REC, weight=server['weight']) if 'NTP server' in eff_roles: self.__add_srv_records(zone_obj, hostname_abs, IPA_DEFAULT_NTP_SRV_REC, weight=server['weight'])
def __setup_server_configuration(self): ensure_dnsserver_container_exists(api.Backend.ldap2, self.api) try: self.api.Command.dnsserver_add( self.fqdn, idnssoamname=DNSName(self.fqdn).make_absolute(), ) except errors.DuplicateEntry: # probably reinstallation of DNS pass try: self.api.Command.dnsserver_mod( self.fqdn, idnsforwarders=[unicode(f) for f in self.forwarders], idnsforwardpolicy=unicode(self.forward_policy)) except errors.EmptyModlist: pass sysupgrade.set_upgrade_state('dns', 'server_config_to_ldap', True)
def xml_unwrap(value, encoding='UTF-8'): """ Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``. When decoding data from an XML-RPC packet, the following transformations occur: * The binary payloads of all ``xmlrpc.client.Binary`` instances are returned as ``str`` instances. * All ``str`` instances are treated as UTF-8 encoded Unicode strings. They are decoded and the resulting ``unicode`` instance is returned. Also see `xml_wrap()`. :param value: The value to unwrap. :param encoding: The Unicode encoding to use (defaults to ``'UTF-8'``). """ if isinstance(value, (unicode, int, float, bool)): # most common first return value elif value is None: return value elif isinstance(value, bytes): return value.decode(encoding) elif isinstance(value, (list, tuple)): return tuple(xml_unwrap(v, encoding) for v in value) elif isinstance(value, dict): if '__dns_name__' in value: return DNSName(value['__dns_name__']) else: return dict( (k, xml_unwrap(v, encoding)) for (k, v) in value.items() ) elif isinstance(value, Binary): assert type(value.data) is bytes return value.data elif isinstance(value, DateTime): # xmlprc DateTime is converted to string of %Y%m%dT%H:%M:%S format return datetime.datetime.strptime(str(value), "%Y%m%dT%H:%M:%S") raise TypeError(value)