ipapython 4.10.2__py2.py3-none-any.whl → 4.13.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ipapython/admintool.py CHANGED
@@ -26,7 +26,6 @@ import logging
26
26
  import sys
27
27
  import os
28
28
  import traceback
29
- from optparse import OptionGroup # pylint: disable=deprecated-module
30
29
 
31
30
  from ipaplatform.osinfo import osinfo
32
31
  from ipapython import version
@@ -40,6 +39,45 @@ SERVER_NOT_CONFIGURED = 2
40
39
  logger = logging.getLogger(__name__)
41
40
 
42
41
 
42
+ def admin_cleanup_global_argv(option_parser, options, argv):
43
+ """Takes option parser and generated options and scrubs sensitive arguments
44
+ from the global program arguments. Note that this only works for GNU GLIBC
45
+ as Python has no generic way to get access to the original argv values to
46
+ modify them in place.
47
+
48
+ The code assumes Python behavior, e.g. there are two additional args in the
49
+ list (/path/to/python -I ...) than what's passed as 'argv' here.
50
+ """
51
+ import ctypes
52
+ import ctypes.util
53
+ try:
54
+ _c = ctypes.CDLL(ctypes.util.find_library("c"))
55
+ if _c._name is None:
56
+ return
57
+ _argv = ctypes.POINTER(ctypes.c_voidp).in_dll(_c, "_dl_argv")
58
+ # since we run as 'python -I <executable> ...', add two args
59
+ _argc = len(argv) + 2
60
+ all_options = []
61
+ if '_get_all_options' in dir(option_parser):
62
+ # OptParse parser
63
+ all_options = option_parser._get_all_options()
64
+ elif '_actions' in dir(option_parser):
65
+ # ArgParse parser
66
+ all_options = option_parser._actions
67
+
68
+ for opt in all_options:
69
+ if getattr(opt, 'sensitive', False):
70
+ v = getattr(options, opt.dest)
71
+ for i in range(0, _argc):
72
+ vi = ctypes.cast(_argv[i],
73
+ ctypes.c_char_p
74
+ ).value.decode('utf-8')
75
+ if vi == v:
76
+ ctypes.memset(_argv[i], ord('X'), len(v))
77
+ except Exception:
78
+ pass
79
+
80
+
43
81
  class ScriptError(Exception):
44
82
  """An exception that records an error message and a return value
45
83
  """
@@ -113,7 +151,7 @@ class AdminTool:
113
151
  :param parser: The parser to add options to
114
152
  :param debug_option: Add a --debug option as an alias to --verbose
115
153
  """
116
- group = OptionGroup(parser, "Logging and output options")
154
+ group = config.OptionGroup(parser, "Logging and output options")
117
155
  group.add_option("-v", "--verbose", dest="verbose", default=False,
118
156
  action="store_true", help="print debugging information")
119
157
  if debug_option:
@@ -149,6 +187,7 @@ class AdminTool:
149
187
  cls._option_parsers[cls] = cls.option_parser
150
188
 
151
189
  options, args = cls.option_parser.parse_args(argv[1:])
190
+ admin_cleanup_global_argv(cls.option_parser, options, argv)
152
191
 
153
192
  command_class = cls.get_command_class(options, args)
154
193
  command = command_class(options, args)
ipapython/certdb.py CHANGED
@@ -511,7 +511,8 @@ class NSSDatabase:
511
511
 
512
512
  :return: List of (name, trust_flags) tuples
513
513
  """
514
- result = self.run_certutil(["-L"], capture_output=True)
514
+ args = ["-L"]
515
+ result = self.run_certutil(args, capture_output=True)
515
516
  certs = result.output.splitlines()
516
517
 
517
518
  # FIXME, this relies on NSS never changing the formatting of certutil
@@ -632,7 +633,7 @@ class NSSDatabase:
632
633
  pkcs12_password_file.close()
633
634
 
634
635
  def import_files(self, files, import_keys=False, key_password=None,
635
- key_nickname=None):
636
+ key_nickname=None, trust_flags=EMPTY_TRUST_FLAGS):
636
637
  """
637
638
  Import certificates and a single private key from multiple files
638
639
 
@@ -808,7 +809,7 @@ class NSSDatabase:
808
809
 
809
810
  for cert in extracted_certs:
810
811
  nickname = str(DN(cert.subject))
811
- self.add_cert(cert, nickname, EMPTY_TRUST_FLAGS)
812
+ self.add_cert(cert, nickname, trust_flags)
812
813
 
813
814
  if extracted_key:
814
815
  with tempfile.NamedTemporaryFile() as in_file, \
@@ -866,6 +867,27 @@ class NSSDatabase:
866
867
  cert, _start = find_cert_from_txt(result.output, start=0)
867
868
  return cert
868
869
 
870
+ def get_all_certs(self, nickname):
871
+ """
872
+ :param nickname: nickname of the certificate in the NSS database
873
+ :returns: list of bytes of all certificates for the nickname
874
+ """
875
+ args = ['-L', '-n', nickname, '-a']
876
+ try:
877
+ result = self.run_certutil(args, capture_output=True)
878
+ except ipautil.CalledProcessError:
879
+ raise RuntimeError("Failed to get %s" % nickname)
880
+ certs = []
881
+
882
+ st = 0
883
+ while True:
884
+ try:
885
+ cert, st = find_cert_from_txt(result.output, start=st)
886
+ except RuntimeError:
887
+ break
888
+ certs.append(cert)
889
+ return certs
890
+
869
891
  def has_nickname(self, nickname):
870
892
  try:
871
893
  self.get_cert(nickname)
@@ -943,20 +965,21 @@ class NSSDatabase:
943
965
  def _verify_cert_validity(self, cert):
944
966
  """Common checks for cert validity
945
967
  """
946
- utcnow = datetime.datetime.utcnow()
947
- if cert.not_valid_before > utcnow:
968
+ utcnow = datetime.datetime.now(tz=datetime.timezone.utc)
969
+ if cert.not_valid_before_utc > utcnow:
948
970
  raise ValueError(
949
- f"not valid before {cert.not_valid_before} UTC is in the "
950
- "future."
971
+ f"not valid before {cert.not_valid_before_utc} UTC is in "
972
+ "the future."
951
973
  )
952
- if cert.not_valid_after < utcnow:
974
+ if cert.not_valid_after_utc < utcnow:
953
975
  raise ValueError(
954
- f"has expired {cert.not_valid_after} UTC"
976
+ f"has expired {cert.not_valid_after_utc} UTC"
955
977
  )
956
978
  # make sure the cert does not expire during installation
957
- if cert.not_valid_after + datetime.timedelta(hours=1) < utcnow:
979
+ if cert.not_valid_after_utc + datetime.timedelta(hours=1) < utcnow:
958
980
  raise ValueError(
959
- f"expires in less than one hour ({cert.not_valid_after} UTC)"
981
+ f"expires in less than one hour ({cert.not_valid_after_utc} "
982
+ "UTC)"
960
983
  )
961
984
 
962
985
  def verify_server_cert_validity(self, nickname, hostname):
@@ -988,53 +1011,58 @@ class NSSDatabase:
988
1011
  raise ValueError('invalid for server %s' % hostname)
989
1012
 
990
1013
  def verify_ca_cert_validity(self, nickname, minpathlen=None):
991
- cert = self.get_cert(nickname)
992
- self._verify_cert_validity(cert)
1014
+ def verify_ca_cert(cert, nickname, minpathlen):
1015
+ self._verify_cert_validity(cert)
993
1016
 
994
- if not cert.subject:
995
- raise ValueError("has empty subject")
1017
+ if not cert.subject:
1018
+ raise ValueError("has empty subject")
996
1019
 
997
- try:
998
- bc = cert.extensions.get_extension_for_class(
1020
+ try:
1021
+ bc = cert.extensions.get_extension_for_class(
999
1022
  cryptography.x509.BasicConstraints)
1000
- except cryptography.x509.ExtensionNotFound:
1001
- raise ValueError("missing basic constraints")
1002
-
1003
- if not bc.value.ca:
1004
- raise ValueError("not a CA certificate")
1005
- if minpathlen is not None:
1006
- # path_length is None means no limitation
1007
- pl = bc.value.path_length
1008
- if pl is not None and pl < minpathlen:
1009
- raise ValueError(
1010
- "basic contraint pathlen {}, must be at least {}".format(
1011
- pl, minpathlen
1023
+ except cryptography.x509.ExtensionNotFound:
1024
+ raise ValueError("missing basic constraints")
1025
+
1026
+ if not bc.value.ca:
1027
+ raise ValueError("not a CA certificate")
1028
+ if minpathlen is not None:
1029
+ # path_length is None means no limitation
1030
+ pl = bc.value.path_length
1031
+ if pl is not None and pl < minpathlen:
1032
+ raise ValueError(
1033
+ "basic contraint pathlen {}, "
1034
+ "must be at least {}".format(
1035
+ pl, minpathlen
1036
+ )
1012
1037
  )
1013
- )
1014
1038
 
1015
- try:
1016
- ski = cert.extensions.get_extension_for_class(
1039
+ try:
1040
+ ski = cert.extensions.get_extension_for_class(
1017
1041
  cryptography.x509.SubjectKeyIdentifier)
1018
- except cryptography.x509.ExtensionNotFound:
1019
- raise ValueError("missing subject key identifier extension")
1020
- else:
1021
- if len(ski.value.digest) == 0:
1022
- raise ValueError("subject key identifier must not be empty")
1042
+ except cryptography.x509.ExtensionNotFound:
1043
+ raise ValueError("missing subject key identifier extension")
1044
+ else:
1045
+ if len(ski.value.digest) == 0:
1046
+ raise ValueError("subject key identifier must not be empty")
1023
1047
 
1024
- try:
1025
- self.run_certutil(
1026
- [
1027
- '-V', # check validity of cert and attrs
1028
- '-n', nickname,
1029
- '-u', 'L', # usage; 'L' means "SSL CA"
1030
- '-e', # check signature(s); this checks
1031
- # key sizes, sig algorithm, etc.
1032
- ],
1033
- capture_output=True)
1034
- except ipautil.CalledProcessError as e:
1035
- # certutil output in case of error is
1036
- # 'certutil: certificate is invalid: <ERROR_STRING>\n'
1037
- raise ValueError(e.output)
1048
+ try:
1049
+ self.run_certutil(
1050
+ [
1051
+ '-V', # check validity of cert and attrs
1052
+ '-n', nickname,
1053
+ '-u', 'L', # usage; 'L' means "SSL CA"
1054
+ '-e', # check signature(s); this checks
1055
+ # key sizes, sig algorithm, etc.
1056
+ ],
1057
+ capture_output=True)
1058
+ except ipautil.CalledProcessError as e:
1059
+ # certutil output in case of error is
1060
+ # 'certutil: certificate is invalid: <ERROR_STRING>\n'
1061
+ raise ValueError(e.output)
1062
+
1063
+ certlist = self.get_all_certs(nickname)
1064
+ for cert in certlist:
1065
+ verify_ca_cert(cert, nickname, minpathlen)
1038
1066
 
1039
1067
  def verify_kdc_cert_validity(self, nickname, realm):
1040
1068
  nicknames = self.get_trust_chain(nickname)
ipapython/config.py CHANGED
@@ -18,9 +18,9 @@
18
18
  #
19
19
  from __future__ import absolute_import
20
20
 
21
- # pylint: disable=deprecated-module
22
- from optparse import (
23
- Option, Values, OptionParser, IndentedHelpFormatter, OptionValueError)
21
+ # pylint: disable=deprecated-module, disable=unused-import
22
+ from optparse import (Option, Values, OptionGroup, OptionParser, SUPPRESS_HELP,
23
+ IndentedHelpFormatter, OptionValueError, make_option)
24
24
  # pylint: enable=deprecated-module
25
25
  from copy import copy
26
26
  from configparser import ConfigParser as SafeConfigParser
@@ -113,10 +113,14 @@ class IPAOptionParser(OptionParser):
113
113
  description=None,
114
114
  formatter=None,
115
115
  add_help_option=True,
116
- prog=None):
117
- OptionParser.__init__(self, usage, option_list, option_class,
118
- version, conflict_handler, description,
119
- formatter, add_help_option, prog)
116
+ prog=None,
117
+ epilog=None):
118
+ OptionParser.__init__(self, usage=usage, option_list=option_list,
119
+ option_class=option_class, version=version,
120
+ conflict_handler=conflict_handler,
121
+ description=description, formatter=formatter,
122
+ add_help_option=add_help_option, prog=prog,
123
+ epilog=epilog)
120
124
 
121
125
  def get_safe_opts(self, opts):
122
126
  """
ipapython/cookie.py CHANGED
@@ -652,7 +652,7 @@ class Cookie:
652
652
 
653
653
  cookie_expiration = self.get_expiration()
654
654
  if cookie_expiration is not None:
655
- now = datetime.datetime.utcnow()
655
+ now = datetime.datetime.now(tz=datetime.timezone.utc)
656
656
  if cookie_expiration < now:
657
657
  raise Cookie.Expired("cookie named '%s'; expired at %s'" % \
658
658
  (cookie_name,
@@ -182,6 +182,9 @@ def get_directive(filename, directive, separator=' '):
182
182
  if separator == ' ':
183
183
  separator = '[ \t]+'
184
184
 
185
+ if directive is None:
186
+ return None
187
+
185
188
  result = None
186
189
  with open(filename, "r") as fd:
187
190
  for line in fd:
@@ -193,7 +196,7 @@ def get_directive(filename, directive, separator=' '):
193
196
  if match:
194
197
  value = match.group(1)
195
198
  else:
196
- raise ValueError("Malformed directive: {}".format(line))
199
+ continue
197
200
 
198
201
  result = unquote_directive_value(value.strip(), '"')
199
202
  result = result.strip(' ')
ipapython/dn.py CHANGED
@@ -1462,4 +1462,5 @@ ATTR_NAME_BY_OID = {
1462
1462
  cryptography.x509.ObjectIdentifier('2.5.4.9'): 'STREET',
1463
1463
  cryptography.x509.ObjectIdentifier('2.5.4.17'): 'postalCode',
1464
1464
  cryptography.x509.ObjectIdentifier('0.9.2342.19200300.100.1.1'): 'UID',
1465
+ cryptography.x509.ObjectIdentifier('2.5.4.97'): 'organizationIdentifier',
1465
1466
  }
ipapython/dogtag.py CHANGED
@@ -63,6 +63,14 @@ INCLUDED_PROFILES = {
63
63
 
64
64
  DEFAULT_PROFILE = u'caIPAserviceCert'
65
65
  KDC_PROFILE = u'KDCs_PKINIT_Certs'
66
+ OCSP_PROFILE = 'caOCSPCert'
67
+ SUBSYSTEM_PROFILE = 'caSubsystemCert'
68
+ AUDIT_PROFILE = 'caSignedLogCert'
69
+ CACERT_PROFILE = 'caCACert'
70
+ CASERVER_PROFILE = 'caServerCert'
71
+ KRA_AUDIT_PROFILE = 'caAuditSigningCert'
72
+ KRA_STORAGE_PROFILE = 'caStorageCert'
73
+ KRA_TRANSPORT_PROFILE = 'caTransportCert'
66
74
 
67
75
 
68
76
  if six.PY3:
ipapython/graph.py CHANGED
@@ -34,7 +34,7 @@ class Graph:
34
34
  def remove_edge(self, tail, head):
35
35
  try:
36
36
  self.edges.remove((tail, head))
37
- except KeyError:
37
+ except ValueError:
38
38
  raise ValueError(
39
39
  "graph does not contain edge: ({0}, {1})".format(tail, head)
40
40
  )
ipapython/ipaldap.py CHANGED
@@ -23,7 +23,7 @@ import binascii
23
23
  import errno
24
24
  import logging
25
25
  import time
26
- import datetime
26
+ from datetime import datetime
27
27
  from decimal import Decimal
28
28
  from copy import deepcopy
29
29
  import contextlib
@@ -33,7 +33,6 @@ import warnings
33
33
 
34
34
  from collections import OrderedDict
35
35
 
36
- from cryptography import x509 as crypto_x509
37
36
  from cryptography.hazmat.primitives import serialization
38
37
 
39
38
  import ldap
@@ -689,11 +688,12 @@ class LDAPClient:
689
688
  '1.3.6.1.4.1.1466.115.121.1.10' : bytes, # Certificate Pair
690
689
  '1.3.6.1.4.1.1466.115.121.1.12' : DN, # Distinguished Name
691
690
  '1.3.6.1.4.1.1466.115.121.1.23' : bytes, # Fax
692
- '1.3.6.1.4.1.1466.115.121.1.24' : datetime.datetime,
691
+ '1.3.6.1.4.1.1466.115.121.1.24' : datetime, # GeneralizedTime
693
692
  '1.3.6.1.4.1.1466.115.121.1.28' : bytes, # JPEG
694
693
  '1.3.6.1.4.1.1466.115.121.1.40' : bytes, # OctetString (same as Binary)
695
694
  '1.3.6.1.4.1.1466.115.121.1.49' : bytes, # Supported Algorithm
696
695
  '1.3.6.1.4.1.1466.115.121.1.51' : bytes, # Teletext Terminal Identifier
696
+ '1.3.6.1.4.1.5322.21.2.5' : datetime, # krbLastAdminUnlock
697
697
 
698
698
  '2.16.840.1.113730.3.8.3.3' : DN, # enrolledBy
699
699
  '2.16.840.1.113730.3.8.3.18' : DN, # managedBy
@@ -706,16 +706,23 @@ class LDAPClient:
706
706
  '2.16.840.1.113730.3.8.7.1' : DN, # memberAllowCmd
707
707
  '2.16.840.1.113730.3.8.7.2' : DN, # memberDenyCmd
708
708
 
709
+ '2.16.840.1.113719.1.301.4.6.1' : datetime, # krbPrincipalExpiration
709
710
  '2.16.840.1.113719.1.301.4.14.1' : DN, # krbRealmReferences
710
711
  '2.16.840.1.113719.1.301.4.17.1' : DN, # krbKdcServers
711
712
  '2.16.840.1.113719.1.301.4.18.1' : DN, # krbPwdServers
712
713
  '2.16.840.1.113719.1.301.4.26.1' : DN, # krbPrincipalReferences
713
714
  '2.16.840.1.113719.1.301.4.29.1' : DN, # krbAdmServers
714
715
  '2.16.840.1.113719.1.301.4.36.1' : DN, # krbPwdPolicyReference
716
+ '2.16.840.1.113719.1.301.4.37.1' : datetime, # krbPasswordExpiration
715
717
  '2.16.840.1.113719.1.301.4.40.1' : DN, # krbTicketPolicyReference
716
718
  '2.16.840.1.113719.1.301.4.41.1' : DN, # krbSubTrees
719
+ '2.16.840.1.113719.1.301.4.45.1' : datetime, # krbLastPwdChange
720
+ '2.16.840.1.113719.1.301.4.48.1' : datetime, # krbLastSuccessfulAuth
721
+ '2.16.840.1.113719.1.301.4.49.1' : datetime, # krbLastFailedAuth
717
722
  '2.16.840.1.113719.1.301.4.52.1' : DN, # krbObjectReferences
718
723
  '2.16.840.1.113719.1.301.4.53.1' : DN, # krbPrincContainerRef
724
+ '2.16.840.1.113730.3.8.16.1.3' : datetime, # ipatokenNotBefore
725
+ '2.16.840.1.113730.3.8.16.1.4' : datetime, # ipatokenNotAfter
719
726
  }
720
727
 
721
728
  # In most cases we lookup the syntax from the schema returned by
@@ -740,10 +747,10 @@ class LDAPClient:
740
747
  'dnszoneidnsname': DNSName,
741
748
  'krbcanonicalname': Principal,
742
749
  'krbprincipalname': Principal,
743
- 'usercertificate': crypto_x509.Certificate,
744
- 'usercertificate;binary': crypto_x509.Certificate,
745
- 'cACertificate': crypto_x509.Certificate,
746
- 'cACertificate;binary': crypto_x509.Certificate,
750
+ 'usercertificate': x509.IPACertificate,
751
+ 'usercertificate;binary': x509.IPACertificate,
752
+ 'cACertificate': x509.IPACertificate,
753
+ 'cACertificate;binary': x509.IPACertificate,
747
754
  'nsds5replicalastupdatestart': unicode,
748
755
  'nsds5replicalastupdateend': unicode,
749
756
  'nsds5replicalastinitstart': unicode,
@@ -990,9 +997,9 @@ class LDAPClient:
990
997
  # key in dict must be str not bytes
991
998
  dct = dict((k, self.encode(v)) for k, v in val.items())
992
999
  return dct
993
- elif isinstance(val, datetime.datetime):
1000
+ elif isinstance(val, datetime):
994
1001
  return val.strftime(LDAP_GENERALIZED_TIME_FORMAT).encode('utf-8')
995
- elif isinstance(val, crypto_x509.Certificate):
1002
+ elif isinstance(val, x509.IPACertificate):
996
1003
  return val.public_bytes(x509.Encoding.DER)
997
1004
  elif val is None:
998
1005
  return None
@@ -1012,14 +1019,14 @@ class LDAPClient:
1012
1019
  return val.decode('utf-8')
1013
1020
  elif target_type is bool:
1014
1021
  return val.decode('utf-8') == 'TRUE'
1015
- elif target_type is datetime.datetime:
1016
- return datetime.datetime.strptime(
1022
+ elif target_type is datetime:
1023
+ return datetime.strptime(
1017
1024
  val.decode('utf-8'), LDAP_GENERALIZED_TIME_FORMAT)
1018
1025
  elif target_type is DNSName:
1019
1026
  return DNSName.from_text(val.decode('utf-8'))
1020
1027
  elif target_type in (DN, Principal):
1021
1028
  return target_type(val.decode('utf-8'))
1022
- elif target_type is crypto_x509.Certificate:
1029
+ elif target_type is x509.IPACertificate:
1023
1030
  return x509.load_der_x509_certificate(val)
1024
1031
  else:
1025
1032
  return target_type(val)
@@ -1177,14 +1184,23 @@ class LDAPClient:
1177
1184
  """schema associated with this LDAP server"""
1178
1185
  return self._get_schema()
1179
1186
 
1180
- def get_allowed_attributes(self, objectclasses, raise_on_unknown=False):
1187
+ def get_allowed_attributes(self, objectclasses, raise_on_unknown=False,
1188
+ attributes="all"):
1181
1189
  if self.schema is None:
1182
1190
  return None
1183
1191
  allowed_attributes = []
1184
1192
  for oc in objectclasses:
1185
1193
  obj = self.schema.get_obj(ldap.schema.ObjectClass, oc)
1186
1194
  if obj is not None:
1187
- allowed_attributes += obj.must + obj.may
1195
+ if attributes == "must":
1196
+ # Only return required(must) attrs
1197
+ allowed_attributes += obj.must
1198
+ elif attributes == "may":
1199
+ # Only return allowed(may) attrs
1200
+ allowed_attributes += obj.may
1201
+ else:
1202
+ # Return both allowed & required attrs
1203
+ allowed_attributes += obj.must + obj.may
1188
1204
  elif raise_on_unknown:
1189
1205
  raise errors.NotFound(
1190
1206
  reason=_('objectclass %s not found') % oc)
@@ -1193,7 +1209,6 @@ class LDAPClient:
1193
1209
  def __enter__(self):
1194
1210
  return self
1195
1211
 
1196
-
1197
1212
  def __exit__(self, exc_type, exc_value, traceback):
1198
1213
  self.close()
1199
1214
 
@@ -1365,13 +1380,17 @@ class LDAPClient:
1365
1380
  ]
1366
1381
  return cls.combine_filters(flts, rules)
1367
1382
  elif value is not None:
1368
- if isinstance(value, crypto_x509.Certificate):
1383
+ if isinstance(value, x509.IPACertificate):
1369
1384
  value = value.public_bytes(serialization.Encoding.DER)
1370
1385
  if isinstance(value, bytes):
1371
1386
  value = binascii.hexlify(value).decode('ascii')
1372
1387
  # value[-2:0] is empty string for the initial '\\'
1373
1388
  value = u'\\'.join(
1374
1389
  value[i:i+2] for i in six.moves.range(-2, len(value), 2))
1390
+ elif isinstance(value, datetime):
1391
+ value = value.strftime(
1392
+ LDAP_GENERALIZED_TIME_FORMAT)
1393
+ value = ldap.filter.escape_filter_chars(value)
1375
1394
  else:
1376
1395
  value = str(value)
1377
1396
  value = ldap.filter.escape_filter_chars(value)
ipapython/ipautil.py CHANGED
@@ -48,9 +48,9 @@ import six
48
48
  from six.moves import input
49
49
 
50
50
  try:
51
- import netifaces
51
+ import ifaddr
52
52
  except ImportError:
53
- netifaces = None
53
+ ifaddr = None
54
54
 
55
55
  from ipapython.dn import DN
56
56
  from ipaplatform.paths import paths
@@ -119,7 +119,7 @@ class UnsafeIPAddress(netaddr.IPAddress):
119
119
  if addr.version != 6:
120
120
  raise
121
121
  except ValueError:
122
- self._net = netaddr.IPNetwork(addr, flags=self.netaddr_ip_flags)
122
+ self._net = netaddr.IPNetwork(addr, flags=0)
123
123
  addr = self._net.ip
124
124
  super(UnsafeIPAddress, self).__init__(addr,
125
125
  flags=self.netaddr_ip_flags)
@@ -203,42 +203,37 @@ class CheckedIPAddress(UnsafeIPAddress):
203
203
  :return: InterfaceDetails named tuple or None if no interface has
204
204
  this address
205
205
  """
206
- if netifaces is None:
207
- raise ImportError("netifaces")
206
+ if ifaddr is None:
207
+ raise ImportError("ifaddr")
208
208
  logger.debug("Searching for an interface of IP address: %s", self)
209
+
209
210
  if self.version == 4:
210
- family = netifaces.AF_INET
211
+ family_ips = (
212
+ (ip.ip, ip.network_prefix, ip.nice_name)
213
+ for ips in [a.ips for a in ifaddr.get_adapters()]
214
+ for ip in ips if not isinstance(ip.ip, tuple)
215
+ )
211
216
  elif self.version == 6:
212
- family = netifaces.AF_INET6
217
+ family_ips = (
218
+ (ip.ip[0], ip.network_prefix, ip.nice_name)
219
+ for ips in [a.ips for a in ifaddr.get_adapters()]
220
+ for ip in ips if isinstance(ip.ip, tuple)
221
+ )
213
222
  else:
214
223
  raise ValueError(
215
224
  "Unsupported address family ({})".format(self.version)
216
225
  )
217
226
 
218
- for interface in netifaces.interfaces():
219
- for ifdata in netifaces.ifaddresses(interface).get(family, []):
220
-
221
- # link-local addresses contain '%suffix' that causes parse
222
- # errors in IPNetwork
223
- ifaddr = ifdata['addr'].split(u'%', 1)[0]
224
-
225
- # newer versions of netifaces provide IPv6 netmask in format
226
- # 'ffff:ffff:ffff:ffff::/64'. We have to split and use prefix
227
- # or the netmask with older versions
228
- ifmask = ifdata['netmask'].split(u'/')[-1]
229
-
230
- ifaddrmask = '{addr}/{netmask}'.format(
231
- addr=ifaddr,
232
- netmask=ifmask
233
- )
234
- logger.debug(
235
- "Testing local IP address: %s (interface: %s)",
236
- ifaddrmask, interface)
227
+ for ip, prefix, ifname in family_ips:
228
+ ifaddrmask = "{ip}/{prefix}".format(ip=ip, prefix=prefix)
229
+ logger.debug(
230
+ "Testing local IP address: %s (interface: %s)",
231
+ ifaddrmask, ifname)
232
+ ifnet = netaddr.IPNetwork(ifaddrmask)
237
233
 
238
- ifnet = netaddr.IPNetwork(ifaddrmask)
234
+ if ifnet.ip == self:
235
+ return InterfaceDetails(ifname, ifnet)
239
236
 
240
- if ifnet.ip == self:
241
- return InterfaceDetails(interface, ifnet)
242
237
  return None
243
238
 
244
239
  def set_ip_net(self, ifnet):
@@ -271,6 +266,19 @@ class CheckedIPAddressLoopback(CheckedIPAddress):
271
266
  file=sys.stderr)
272
267
 
273
268
 
269
+ class IPAddressDoTForwarder(str):
270
+ """IPv4 or IPv6 address with added hostname as needed for DNS over TLS
271
+ configuration. Example: 1.2.3.4#dns.hostname.test
272
+ """
273
+ def __init__(self, addr):
274
+ addr_split = addr.split("#")
275
+ if len(addr_split) != 2 or not valid_ip(addr_split[0]):
276
+ raise ValueError(
277
+ "DoT forwarder must be in the format "
278
+ "of '1.2.3.4#dns.example.test'."
279
+ )
280
+
281
+
274
282
  def valid_ip(addr):
275
283
  return netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr)
276
284
 
@@ -1663,9 +1671,16 @@ def remove_ccache(ccache_path=None, run_as=None):
1663
1671
  "Failed to clear Kerberos credentials cache: %s", e)
1664
1672
 
1665
1673
 
1666
- def remove_file(filename):
1674
+ def remove_file(filename, only_if_empty=False):
1667
1675
  """Remove a file and log any exceptions raised.
1676
+
1677
+ :only_if_empty: only remove the file if empty. Default False.
1668
1678
  """
1679
+ if only_if_empty and os.path.exists(filename):
1680
+ file_stat = os.stat(filename)
1681
+ if file_stat.st_size > 0:
1682
+ logger.debug('%s is not empty.', filename)
1683
+ return
1669
1684
  try:
1670
1685
  os.unlink(filename)
1671
1686
  except Exception as e:
@@ -1674,6 +1689,15 @@ def remove_file(filename):
1674
1689
  logger.error('Error removing %s: %s', filename, str(e))
1675
1690
 
1676
1691
 
1692
+ def remove_directory(dir):
1693
+ """Remove an empty directory."""
1694
+ try:
1695
+ os.rmdir(dir)
1696
+ except OSError as e:
1697
+ if e.errno not in {errno.ENOENT, errno.ENOTEMPTY}:
1698
+ logger.error("Failed to remove directory %s", dir)
1699
+
1700
+
1677
1701
  def rmtree(path):
1678
1702
  """
1679
1703
  Remove a directory structure and log any exceptions raised.
@@ -1798,3 +1822,30 @@ def get_config_debug(context):
1798
1822
  return False
1799
1823
 
1800
1824
  return parser.get(CONFIG_SECTION, 'debug').lower() == 'true'
1825
+
1826
+
1827
+ @contextmanager
1828
+ def log_level_override():
1829
+ """The PKI python libraries log to info() which ends up polluting
1830
+ the ipa server installation output. Switch the log level
1831
+ prior to calling any PKI library functions and then restore
1832
+ the value in order to suppress the output. Lines like:
1833
+
1834
+ INFO Connecting to https://localhost:8443
1835
+ INFO Getting PKI server info from /pki/v2/info
1836
+
1837
+ This won't affect what is logged to files.
1838
+ """
1839
+ stream_handlers = [
1840
+ (handler, handler.level) for handler in logging.root.handlers
1841
+ if type(handler) is logging.StreamHandler
1842
+ ]
1843
+ for (handler, _level) in stream_handlers:
1844
+ handler.setLevel(logging.ERROR)
1845
+ try:
1846
+ yield
1847
+ except Exception as e:
1848
+ raise e
1849
+ finally:
1850
+ for (handler, orig_level) in stream_handlers:
1851
+ handler.setLevel(orig_level)
@@ -111,7 +111,7 @@ class KRB5Error(Exception):
111
111
 
112
112
 
113
113
  def krb5_errcheck(result, func, arguments):
114
- """Error checker for krb5_error return value"""
114
+ """Error checker for krb5_error_code return value"""
115
115
  if result != 0:
116
116
  raise KRB5Error(result, func.__name__, arguments)
117
117
 
@@ -119,14 +119,13 @@ def krb5_errcheck(result, func, arguments):
119
119
  krb5_context = ctypes.POINTER(_krb5_context)
120
120
  krb5_ccache = ctypes.POINTER(_krb5_ccache)
121
121
  krb5_data_p = ctypes.POINTER(_krb5_data)
122
- krb5_error = ctypes.c_int32
123
122
  krb5_creds = _krb5_creds
124
123
  krb5_pointer = ctypes.c_void_p
125
124
  krb5_cc_cursor = krb5_pointer
126
125
 
127
126
  krb5_init_context = LIBKRB5.krb5_init_context
128
127
  krb5_init_context.argtypes = (ctypes.POINTER(krb5_context), )
129
- krb5_init_context.restype = krb5_error
128
+ krb5_init_context.restype = krb5_error_code
130
129
  krb5_init_context.errcheck = krb5_errcheck
131
130
 
132
131
  krb5_free_context = LIBKRB5.krb5_free_context
@@ -143,30 +142,30 @@ krb5_free_data_contents.restype = None
143
142
 
144
143
  krb5_cc_default = LIBKRB5.krb5_cc_default
145
144
  krb5_cc_default.argtypes = (krb5_context, ctypes.POINTER(krb5_ccache), )
146
- krb5_cc_default.restype = krb5_error
145
+ krb5_cc_default.restype = krb5_error_code
147
146
  krb5_cc_default.errcheck = krb5_errcheck
148
147
 
149
148
  krb5_cc_close = LIBKRB5.krb5_cc_close
150
149
  krb5_cc_close.argtypes = (krb5_context, krb5_ccache, )
151
- krb5_cc_close.restype = krb5_error
150
+ krb5_cc_close.restype = krb5_error_code
152
151
  krb5_cc_close.errcheck = krb5_errcheck
153
152
 
154
153
  krb5_parse_name = LIBKRB5.krb5_parse_name
155
154
  krb5_parse_name.argtypes = (krb5_context, ctypes.c_char_p,
156
155
  ctypes.POINTER(krb5_principal), )
157
- krb5_parse_name.restype = krb5_error
156
+ krb5_parse_name.restype = krb5_error_code
158
157
  krb5_parse_name.errcheck = krb5_errcheck
159
158
 
160
159
  krb5_cc_set_config = LIBKRB5.krb5_cc_set_config
161
160
  krb5_cc_set_config.argtypes = (krb5_context, krb5_ccache, krb5_principal,
162
161
  ctypes.c_char_p, krb5_data_p, )
163
- krb5_cc_set_config.restype = krb5_error
162
+ krb5_cc_set_config.restype = krb5_error_code
164
163
  krb5_cc_set_config.errcheck = krb5_errcheck
165
164
 
166
165
  krb5_cc_get_principal = LIBKRB5.krb5_cc_get_principal
167
166
  krb5_cc_get_principal.argtypes = (krb5_context, krb5_ccache,
168
167
  ctypes.POINTER(krb5_principal), )
169
- krb5_cc_get_principal.restype = krb5_error
168
+ krb5_cc_get_principal.restype = krb5_error_code
170
169
  krb5_cc_get_principal.errcheck = krb5_errcheck
171
170
 
172
171
  # krb5_build_principal is a variadic function but that can't be expressed
@@ -177,32 +176,31 @@ krb5_build_principal.argtypes = (krb5_context, ctypes.POINTER(krb5_principal),
177
176
  ctypes.c_uint, ctypes.c_char_p,
178
177
  ctypes.c_char_p, ctypes.c_char_p,
179
178
  ctypes.c_char_p, ctypes.c_char_p, )
180
- krb5_build_principal.restype = krb5_error
179
+ krb5_build_principal.restype = krb5_error_code
181
180
  krb5_build_principal.errcheck = krb5_errcheck
182
181
 
183
182
  krb5_cc_start_seq_get = LIBKRB5.krb5_cc_start_seq_get
184
183
  krb5_cc_start_seq_get.argtypes = (krb5_context, krb5_ccache,
185
184
  ctypes.POINTER(krb5_cc_cursor), )
186
- krb5_cc_start_seq_get.restype = krb5_error
185
+ krb5_cc_start_seq_get.restype = krb5_error_code
187
186
  krb5_cc_start_seq_get.errcheck = krb5_errcheck
188
187
 
189
188
  krb5_cc_next_cred = LIBKRB5.krb5_cc_next_cred
190
189
  krb5_cc_next_cred.argtypes = (krb5_context, krb5_ccache,
191
190
  ctypes.POINTER(krb5_cc_cursor),
192
191
  ctypes.POINTER(krb5_creds), )
193
- krb5_cc_next_cred.restype = krb5_error
192
+ krb5_cc_next_cred.restype = krb5_error_code
194
193
  krb5_cc_next_cred.errcheck = krb5_errcheck
195
194
 
196
195
  krb5_cc_end_seq_get = LIBKRB5.krb5_cc_end_seq_get
197
196
  krb5_cc_end_seq_get.argtypes = (krb5_context, krb5_ccache,
198
197
  ctypes.POINTER(krb5_cc_cursor), )
199
- krb5_cc_end_seq_get.restype = krb5_error
198
+ krb5_cc_end_seq_get.restype = krb5_error_code
200
199
  krb5_cc_end_seq_get.errcheck = krb5_errcheck
201
200
 
202
201
  krb5_free_cred_contents = LIBKRB5.krb5_free_cred_contents
203
202
  krb5_free_cred_contents.argtypes = (krb5_context, ctypes.POINTER(krb5_creds))
204
- krb5_free_cred_contents.restype = krb5_error
205
- krb5_free_cred_contents.errcheck = krb5_errcheck
203
+ krb5_free_cred_contents.restype = None
206
204
 
207
205
  krb5_principal_compare = LIBKRB5.krb5_principal_compare
208
206
  krb5_principal_compare.argtypes = (krb5_context, krb5_principal,
@@ -212,7 +210,7 @@ krb5_principal_compare.restype = krb5_boolean
212
210
  krb5_unparse_name = LIBKRB5.krb5_unparse_name
213
211
  krb5_unparse_name.argtypes = (krb5_context, krb5_principal,
214
212
  ctypes.POINTER(ctypes.c_char_p), )
215
- krb5_unparse_name.restype = krb5_error
213
+ krb5_unparse_name.restype = krb5_error_code
216
214
  krb5_unparse_name.errcheck = krb5_errcheck
217
215
 
218
216
  krb5_free_unparsed_name = LIBKRB5.krb5_free_unparsed_name
@@ -314,8 +312,12 @@ def get_data(princ_name, key):
314
312
  checkcreds = krb5_creds()
315
313
  # the next function will throw an error and break out of the
316
314
  # while loop when we try to access past the last cred
317
- krb5_cc_next_cred(context, ccache, ctypes.byref(cursor),
318
- ctypes.byref(checkcreds))
315
+ try:
316
+ krb5_cc_next_cred(context, ccache, ctypes.byref(cursor),
317
+ ctypes.byref(checkcreds))
318
+ except KRB5Error:
319
+ break
320
+
319
321
  if (krb5_principal_compare(context, principal,
320
322
  checkcreds.client) == 1 and
321
323
  krb5_principal_compare(context, srv_princ,
@@ -330,8 +332,6 @@ def get_data(princ_name, key):
330
332
  else:
331
333
  krb5_free_cred_contents(context,
332
334
  ctypes.byref(checkcreds))
333
- except KRB5Error:
334
- pass
335
335
  finally:
336
336
  krb5_cc_end_seq_get(context, ccache, ctypes.byref(cursor))
337
337
 
ipapython/version.py CHANGED
@@ -17,13 +17,13 @@
17
17
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
  #
19
19
 
20
- from pkg_resources import parse_version
20
+ from packaging.version import parse as parse_version
21
21
 
22
22
  # The full version including strings
23
- VERSION = "4.10.2"
23
+ VERSION = "4.13.1"
24
24
 
25
25
  # A fuller version including the vendor tag (e.g. 3.3.3-34.fc20)
26
- VENDOR_VERSION = "4.10.2"
26
+ VENDOR_VERSION = "4.13.1"
27
27
 
28
28
 
29
29
  # Just the numeric portion of the version so one can do direct numeric
@@ -43,11 +43,11 @@ VENDOR_VERSION = "4.10.2"
43
43
  # IPA 3.2.1: NUM_VERSION=30201
44
44
  # IPA 3.2.99: NUM_VERSION=30299 (development version)
45
45
  # IPA 3.3.0: NUM_VERSION=30300
46
- NUM_VERSION = 41002
46
+ NUM_VERSION = 41301
47
47
 
48
48
 
49
49
  # The version of the API.
50
- API_VERSION = "2.252"
50
+ API_VERSION = "2.257"
51
51
 
52
52
 
53
53
  DEFAULT_PLUGINS = frozenset(l.strip() for l in """
@@ -393,6 +393,9 @@ output_show/1
393
393
  param/1
394
394
  param_find/1
395
395
  param_show/1
396
+ passkeyconfig/1
397
+ passkeyconfig_mod/1
398
+ passkeyconfig_show/1
396
399
  passwd/1
397
400
  permission/1
398
401
  permission_add/1
@@ -518,6 +521,7 @@ stageuser_add/1
518
521
  stageuser_add_cert/1
519
522
  stageuser_add_certmapdata/1
520
523
  stageuser_add_manager/1
524
+ stageuser_add_passkey/1
521
525
  stageuser_add_principal/1
522
526
  stageuser_del/1
523
527
  stageuser_find/1
@@ -525,6 +529,7 @@ stageuser_mod/1
525
529
  stageuser_remove_cert/1
526
530
  stageuser_remove_certmapdata/1
527
531
  stageuser_remove_manager/1
532
+ stageuser_remove_passkey/1
528
533
  stageuser_remove_principal/1
529
534
  stageuser_show/1
530
535
  subid/1
@@ -572,6 +577,15 @@ sudorule_remove_runasgroup/1
572
577
  sudorule_remove_runasuser/1
573
578
  sudorule_remove_user/1
574
579
  sudorule_show/1
580
+ sysaccount/1
581
+ sysaccount_add/1
582
+ sysaccount_del/1
583
+ sysaccount_disable/1
584
+ sysaccount_enable/1
585
+ sysaccount_find/1
586
+ sysaccount_mod/1
587
+ sysaccount_policy/1
588
+ sysaccount_show/1
575
589
  topic/1
576
590
  topic_find/1
577
591
  topic_show/1
@@ -613,6 +627,7 @@ user_add/1
613
627
  user_add_cert/1
614
628
  user_add_certmapdata/1
615
629
  user_add_manager/1
630
+ user_add_passkey/1
616
631
  user_add_principal/1
617
632
  user_del/1
618
633
  user_disable/1
@@ -622,6 +637,7 @@ user_mod/1
622
637
  user_remove_cert/1
623
638
  user_remove_certmapdata/1
624
639
  user_remove_manager/1
640
+ user_remove_passkey/1
625
641
  user_remove_principal/1
626
642
  user_show/1
627
643
  user_stage/1
@@ -651,4 +667,4 @@ vaultcontainer_show/1
651
667
  whoami/1
652
668
  """.strip().splitlines())
653
669
 
654
- KRB5_BUILD_VERSION = parse_version("1.19.2")
670
+ KRB5_BUILD_VERSION = parse_version("1.21.3")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ipapython
3
- Version: 4.10.2
3
+ Version: 4.13.1
4
4
  Summary: FreeIPA python support library
5
5
  Home-page: https://www.freeipa.org/
6
6
  Download-URL: https://www.freeipa.org/page/Downloads
@@ -25,17 +25,32 @@ Classifier: Topic :: Internet :: Name Service (DNS)
25
25
  Classifier: Topic :: Security
26
26
  Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP
27
27
  Requires-Python: >=3.6.0
28
- License-File: ../COPYING
28
+ License-File: COPYING
29
29
  Requires-Dist: cffi
30
- Requires-Dist: cryptography (>=1.6)
31
- Requires-Dist: dnspython (>=1.15)
32
- Requires-Dist: gssapi (>=1.2.0)
33
- Requires-Dist: ipaplatform (==4.10.2)
30
+ Requires-Dist: cryptography>=1.6
31
+ Requires-Dist: dnspython>=1.15
32
+ Requires-Dist: gssapi>=1.2.0
33
+ Requires-Dist: ipaplatform==4.13.1
34
34
  Requires-Dist: netaddr
35
35
  Requires-Dist: six
36
36
  Provides-Extra: ldap
37
- Requires-Dist: python-ldap ; extra == 'ldap'
38
- Provides-Extra: netifaces
39
- Requires-Dist: netifaces ; extra == 'netifaces'
37
+ Requires-Dist: python-ldap; extra == "ldap"
38
+ Provides-Extra: ifaddr
39
+ Requires-Dist: ifaddr; extra == "ifaddr"
40
+ Dynamic: author
41
+ Dynamic: author-email
42
+ Dynamic: classifier
43
+ Dynamic: description
44
+ Dynamic: download-url
45
+ Dynamic: home-page
46
+ Dynamic: license
47
+ Dynamic: license-file
48
+ Dynamic: maintainer
49
+ Dynamic: maintainer-email
50
+ Dynamic: platform
51
+ Dynamic: provides-extra
52
+ Dynamic: requires-dist
53
+ Dynamic: requires-python
54
+ Dynamic: summary
40
55
 
41
56
  FreeIPA python support library
@@ -0,0 +1,29 @@
1
+ ipapython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ipapython/admintool.py,sha256=d9C0S5YGW0PN0OUQPN-ZsjQcJoyySlSyiU5P2z-C9Ao,13828
3
+ ipapython/certdb.py,sha256=wV7Lu6WRLVDNC8gZ1ZNPP9UcKa5t1omNEvbAHpgH7-s,39064
4
+ ipapython/config.py,sha256=iTQDsxhBxprR5d79Zo_jbQg84Ar3POaiAO0Y1qZguLI,9721
5
+ ipapython/cookie.py,sha256=-X_UojEtLPw6vx1XxBEGX17i3aLyCylFPXDQWMBN0V4,24920
6
+ ipapython/directivesetter.py,sha256=O1t8BtQ_sUt5ac7BQffHmLwIQczU3al-WN0Z2hXNEdg,7673
7
+ ipapython/dn.py,sha256=9Lbw9cpygjvUSFqNCYvU-nFSQFqJYrSE1Dji8mr3Xws,49528
8
+ ipapython/dn_ctypes.py,sha256=ZJ5Q8ZhA8HnM5Xu4j-K62Q33amZfcTcPQY6i2rpu8CI,3905
9
+ ipapython/dnsutil.py,sha256=nxvtyaYuARFSj9Vj5GPL1HNaN2-DISf4y1zs-Bn5L3I,22125
10
+ ipapython/dogtag.py,sha256=TesFrK9Mzo_gCmFsHgfm4T5R2PE3Qg1yvyw6U6xPzv4,9693
11
+ ipapython/errors.py,sha256=VEkqUTs5JrTmKooeFxcSPCvqx48Zy3Pa5LOxPrOlCe4,1984
12
+ ipapython/fqdn.py,sha256=P6OoSODkn6hQLSN3sMpLz1U7uwzpGb97l5DaI6-azCc,1220
13
+ ipapython/graph.py,sha256=G-I5tJKQxvO3YokjolS3pgT-K5u8cUV2KVpknM6yBmI,2492
14
+ ipapython/ipa_log_manager.py,sha256=GNZV1HuTRrYSsNdgyw2oZVjZOESJpH91Jb9rsvhzj0E,3881
15
+ ipapython/ipachangeconf.py,sha256=JT5QKYgvpbVrLAR49so6XtpLoxsNDt7FEvqjmP7xcdM,20086
16
+ ipapython/ipaldap.py,sha256=e4G_jHhyzMTH0A7IKjhmbCpsdyQ86E5SF0dz-B_QE_0,73753
17
+ ipapython/ipautil.py,sha256=jCz8NYj-c11Bj7LVVuzdwjHPXBFbbj_yXHHfboWckOA,61886
18
+ ipapython/ipavalidate.py,sha256=EqKFgw4Bz3lTuT7hjRRH7_YkoGRIG1GQ-TB4oOHLKVs,3633
19
+ ipapython/kerberos.py,sha256=5JJ_4dqJAgVhY0Gi7OHDLM3-vG6owRFm-NVQsZKVcgg,6300
20
+ ipapython/kernel_keyring.py,sha256=vInQCwrH-Njknfh3U5nUkeqFdHG4ZRIUK8h0-ODu_nU,4903
21
+ ipapython/nsslib.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ ipapython/session_storage.py,sha256=t7eMnPx6OAHTCajSEsMfDKFySvvllFIDwsG1HuxzYPg,13247
23
+ ipapython/ssh.py,sha256=aEGqBooQi4osOi5rJfdAcUZs-9V-oNJf0pci5ZchKIE,6713
24
+ ipapython/version.py,sha256=hC2UszYu7S_1C2lvqkfbFzLnc4oAGOgIrplaBr_0Rx0,13116
25
+ ipapython-4.13.1.dist-info/licenses/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
26
+ ipapython-4.13.1.dist-info/METADATA,sha256=LsXxqGVj9nOa04v4n8eNvZrRKjFv2RGpVjlaPtlcff0,1816
27
+ ipapython-4.13.1.dist-info/WHEEL,sha256=Mk1ST5gDzEO5il5kYREiBnzzM469m5sI8ESPl7TRhJY,110
28
+ ipapython-4.13.1.dist-info/top_level.txt,sha256=ND3uLjnGYtcHS21Gj-Lk9jrodr1Vzxz9ebTopaAS9WI,10
29
+ ipapython-4.13.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,29 +0,0 @@
1
- ipapython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ipapython/admintool.py,sha256=0TG9COpf9AQd1RLcQ-uttksESd0Kb4Dk-9sS-aGCXd0,12258
3
- ipapython/certdb.py,sha256=Qc1kgyelWvU0k8aBTRUaRBtXzt21DXynWPH4uQW6tg8,37937
4
- ipapython/config.py,sha256=3pjybgxq42rrUmvzm4gLdboi-TmbEB5PgQ_wD9iyn-I,9406
5
- ipapython/cookie.py,sha256=pOAzewR_r-VLMGbQYY8j9kYOI3fTNpFdzabvjx1bRyY,24899
6
- ipapython/directivesetter.py,sha256=cRl2Cwk7hZ1bRWW5Mu95NFK5PF9H12XJmKrEX21HG40,7674
7
- ipapython/dn.py,sha256=zTMMW-8XpudF_6QjeU84leH2tM3vPD7Xar6jBeCjAAc,49450
8
- ipapython/dn_ctypes.py,sha256=ZJ5Q8ZhA8HnM5Xu4j-K62Q33amZfcTcPQY6i2rpu8CI,3905
9
- ipapython/dnsutil.py,sha256=nxvtyaYuARFSj9Vj5GPL1HNaN2-DISf4y1zs-Bn5L3I,22125
10
- ipapython/dogtag.py,sha256=YTmXvPuOaFe_jLbrhiETBJ45XPJEOvndXNHWy7GbgFM,9410
11
- ipapython/errors.py,sha256=VEkqUTs5JrTmKooeFxcSPCvqx48Zy3Pa5LOxPrOlCe4,1984
12
- ipapython/fqdn.py,sha256=P6OoSODkn6hQLSN3sMpLz1U7uwzpGb97l5DaI6-azCc,1220
13
- ipapython/graph.py,sha256=tgf5gZtKSlQNKDA9vX5HlQMn57zjibtDONRx8nqwUCQ,2490
14
- ipapython/ipa_log_manager.py,sha256=GNZV1HuTRrYSsNdgyw2oZVjZOESJpH91Jb9rsvhzj0E,3881
15
- ipapython/ipachangeconf.py,sha256=JT5QKYgvpbVrLAR49so6XtpLoxsNDt7FEvqjmP7xcdM,20086
16
- ipapython/ipaldap.py,sha256=xjoorpJlO55Rq-vQSlJofZMRrk3qUb0ahWL-LjEJatI,72601
17
- ipapython/ipautil.py,sha256=ImbGEicTHvfxCTCfFF1ADn9_cgbg2_4_S8opMFBD2uo,60303
18
- ipapython/ipavalidate.py,sha256=EqKFgw4Bz3lTuT7hjRRH7_YkoGRIG1GQ-TB4oOHLKVs,3633
19
- ipapython/kerberos.py,sha256=5JJ_4dqJAgVhY0Gi7OHDLM3-vG6owRFm-NVQsZKVcgg,6300
20
- ipapython/kernel_keyring.py,sha256=vInQCwrH-Njknfh3U5nUkeqFdHG4ZRIUK8h0-ODu_nU,4903
21
- ipapython/nsslib.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- ipapython/session_storage.py,sha256=mDIy9MKar_RuzRCaR8Qs-7_E2yLGNEjc21WbxtwJLr4,13223
23
- ipapython/ssh.py,sha256=aEGqBooQi4osOi5rJfdAcUZs-9V-oNJf0pci5ZchKIE,6713
24
- ipapython/version.py,sha256=Z2ib0IOq52rqjpvfWTRa8qqb99tazQBkHsHNp9B13uw,12793
25
- ipapython-4.10.2.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
26
- ipapython-4.10.2.dist-info/METADATA,sha256=koxTHUkd-FWVVhwM-llmKyh34C_aepSvYDvRKpd68bM,1530
27
- ipapython-4.10.2.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
28
- ipapython-4.10.2.dist-info/top_level.txt,sha256=ND3uLjnGYtcHS21Gj-Lk9jrodr1Vzxz9ebTopaAS9WI,10
29
- ipapython-4.10.2.dist-info/RECORD,,