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 +41 -2
- ipapython/certdb.py +79 -51
- ipapython/config.py +11 -7
- ipapython/cookie.py +1 -1
- ipapython/directivesetter.py +4 -1
- ipapython/dn.py +1 -0
- ipapython/dogtag.py +8 -0
- ipapython/graph.py +1 -1
- ipapython/ipaldap.py +35 -16
- ipapython/ipautil.py +81 -30
- ipapython/session_storage.py +19 -19
- ipapython/version.py +22 -6
- {ipapython-4.10.2.dist-info → ipapython-4.13.1.dist-info}/METADATA +25 -10
- ipapython-4.13.1.dist-info/RECORD +29 -0
- {ipapython-4.10.2.dist-info → ipapython-4.13.1.dist-info}/WHEEL +1 -1
- ipapython-4.10.2.dist-info/RECORD +0 -29
- {ipapython-4.10.2.dist-info → ipapython-4.13.1.dist-info/licenses}/COPYING +0 -0
- {ipapython-4.10.2.dist-info → ipapython-4.13.1.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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,
|
|
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.
|
|
947
|
-
if cert.
|
|
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.
|
|
950
|
-
"future."
|
|
971
|
+
f"not valid before {cert.not_valid_before_utc} UTC is in "
|
|
972
|
+
"the future."
|
|
951
973
|
)
|
|
952
|
-
if cert.
|
|
974
|
+
if cert.not_valid_after_utc < utcnow:
|
|
953
975
|
raise ValueError(
|
|
954
|
-
f"has expired {cert.
|
|
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.
|
|
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.
|
|
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
|
|
992
|
-
|
|
1014
|
+
def verify_ca_cert(cert, nickname, minpathlen):
|
|
1015
|
+
self._verify_cert_validity(cert)
|
|
993
1016
|
|
|
994
|
-
|
|
995
|
-
|
|
1017
|
+
if not cert.subject:
|
|
1018
|
+
raise ValueError("has empty subject")
|
|
996
1019
|
|
|
997
|
-
|
|
998
|
-
|
|
1020
|
+
try:
|
|
1021
|
+
bc = cert.extensions.get_extension_for_class(
|
|
999
1022
|
cryptography.x509.BasicConstraints)
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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
|
-
|
|
1016
|
-
|
|
1039
|
+
try:
|
|
1040
|
+
ski = cert.extensions.get_extension_for_class(
|
|
1017
1041
|
cryptography.x509.SubjectKeyIdentifier)
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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.
|
|
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,
|
ipapython/directivesetter.py
CHANGED
|
@@ -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
|
-
|
|
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
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
|
|
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':
|
|
744
|
-
'usercertificate;binary':
|
|
745
|
-
'cACertificate':
|
|
746
|
-
'cACertificate;binary':
|
|
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
|
|
1000
|
+
elif isinstance(val, datetime):
|
|
994
1001
|
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT).encode('utf-8')
|
|
995
|
-
elif isinstance(val,
|
|
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
|
|
1016
|
-
return datetime.
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
51
|
+
import ifaddr
|
|
52
52
|
except ImportError:
|
|
53
|
-
|
|
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=
|
|
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
|
|
207
|
-
raise ImportError("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
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)
|
ipapython/session_storage.py
CHANGED
|
@@ -111,7 +111,7 @@ class KRB5Error(Exception):
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
def krb5_errcheck(result, func, arguments):
|
|
114
|
-
"""Error checker for
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
318
|
-
|
|
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
|
|
20
|
+
from packaging.version import parse as parse_version
|
|
21
21
|
|
|
22
22
|
# The full version including strings
|
|
23
|
-
VERSION = "4.
|
|
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.
|
|
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 =
|
|
46
|
+
NUM_VERSION = 41301
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
# The version of the API.
|
|
50
|
-
API_VERSION = "2.
|
|
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.
|
|
670
|
+
KRB5_BUILD_VERSION = parse_version("1.21.3")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: ipapython
|
|
3
|
-
Version: 4.
|
|
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:
|
|
28
|
+
License-File: COPYING
|
|
29
29
|
Requires-Dist: cffi
|
|
30
|
-
Requires-Dist: cryptography
|
|
31
|
-
Requires-Dist: dnspython
|
|
32
|
-
Requires-Dist: gssapi
|
|
33
|
-
Requires-Dist: ipaplatform
|
|
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
|
|
38
|
-
Provides-Extra:
|
|
39
|
-
Requires-Dist:
|
|
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,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,,
|
|
File without changes
|
|
File without changes
|