support more DateTime attributes in LDAP searches in IPA API

LDAPSearch class constructs a filter from a set of attributes and their
values passed in by the command. During this construction process a
limited set of attributes gets converted to a special form, the rest is
simply taken as a string and escaped according to LDAP rules.

This means DateTime class would simply be converted to string using
str(DateTime) and that uses default formatting method. For LDAP we need
to apply a specific formatting method instead.

Following LDAP attributes now handled as datetime.datetime:

 ( 1.3.6.1.4.1.5322.21.2.5 NAME 'krbLastAdminUnlock' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE)
 ( 2.16.840.1.113719.1.301.4.6.1 NAME 'krbPrincipalExpiration' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE)
 ( 2.16.840.1.113719.1.301.4.37.1 NAME 'krbPasswordExpiration' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE)
 ( 2.16.840.1.113719.1.301.4.45.1 NAME 'krbLastPwdChange' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE)
 ( 2.16.840.1.113719.1.301.4.48.1 NAME 'krbLastSuccessfulAuth' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE)
 ( 2.16.840.1.113719.1.301.4.49.1 NAME 'krbLastFailedAuth' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE)
 ( 2.16.840.1.113730.3.8.16.1.3  NAME 'ipatokenNotBefore' DESC 'Token validity date' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE X-ORIGIN 'IPA OTP')
 ( 2.16.840.1.113730.3.8.16.1.4  NAME 'ipatokenNotAfter' DESC 'Token expiration date' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE X-ORIGIN 'IPA OTP')

Fixes: https://pagure.io/freeipa/issue/9395

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Antonio Torres <antorres@redhat.com>
This commit is contained in:
Alexander Bokovoy authored and Antonio Torres committed 2023-08-21 16:28:04 +02:00
1 parent d5ae5e1884
commit ef955c9015
2 files changed
+40 -5

No files matched your search

+17 -5
View File
@@ -23,7 +23,7 @@ import binascii
import errno
import logging
import time
import datetime
from datetime import datetime
from decimal import Decimal
from copy import deepcopy
import contextlib
@@ -689,11 +689,12 @@ class LDAPClient:
'1.3.6.1.4.1.1466.115.121.1.10' : bytes, # Certificate Pair
'1.3.6.1.4.1.1466.115.121.1.12' : DN, # Distinguished Name
'1.3.6.1.4.1.1466.115.121.1.23' : bytes, # Fax
'1.3.6.1.4.1.1466.115.121.1.24' : datetime.datetime,
'1.3.6.1.4.1.1466.115.121.1.24' : datetime, # GeneralizedTime
'1.3.6.1.4.1.1466.115.121.1.28' : bytes, # JPEG
'1.3.6.1.4.1.1466.115.121.1.40' : bytes, # OctetString (same as Binary)
'1.3.6.1.4.1.1466.115.121.1.49' : bytes, # Supported Algorithm
'1.3.6.1.4.1.1466.115.121.1.51' : bytes, # Teletext Terminal Identifier
'1.3.6.1.4.1.5322.21.2.5' : datetime, # krbLastAdminUnlock
'2.16.840.1.113730.3.8.3.3' : DN, # enrolledBy
'2.16.840.1.113730.3.8.3.18' : DN, # managedBy
@@ -706,16 +707,23 @@ class LDAPClient:
'2.16.840.1.113730.3.8.7.1' : DN, # memberAllowCmd
'2.16.840.1.113730.3.8.7.2' : DN, # memberDenyCmd
'2.16.840.1.113719.1.301.4.6.1' : datetime, # krbPrincipalExpiration
'2.16.840.1.113719.1.301.4.14.1' : DN, # krbRealmReferences
'2.16.840.1.113719.1.301.4.17.1' : DN, # krbKdcServers
'2.16.840.1.113719.1.301.4.18.1' : DN, # krbPwdServers
'2.16.840.1.113719.1.301.4.26.1' : DN, # krbPrincipalReferences
'2.16.840.1.113719.1.301.4.29.1' : DN, # krbAdmServers
'2.16.840.1.113719.1.301.4.36.1' : DN, # krbPwdPolicyReference
'2.16.840.1.113719.1.301.4.37.1' : datetime, # krbPasswordExpiration
'2.16.840.1.113719.1.301.4.40.1' : DN, # krbTicketPolicyReference
'2.16.840.1.113719.1.301.4.41.1' : DN, # krbSubTrees
'2.16.840.1.113719.1.301.4.45.1' : datetime, # krbLastPwdChange
'2.16.840.1.113719.1.301.4.48.1' : datetime, # krbLastSuccessfulAuth
'2.16.840.1.113719.1.301.4.49.1' : datetime, # krbLastFailedAuth
'2.16.840.1.113719.1.301.4.52.1' : DN, # krbObjectReferences
'2.16.840.1.113719.1.301.4.53.1' : DN, # krbPrincContainerRef
'2.16.840.1.113730.3.8.16.1.3' : datetime, # ipatokenNotBefore
'2.16.840.1.113730.3.8.16.1.4' : datetime, # ipatokenNotAfter
}
# In most cases we lookup the syntax from the schema returned by
@@ -990,7 +998,7 @@ class LDAPClient:
# key in dict must be str not bytes
dct = dict((k, self.encode(v)) for k, v in val.items())
return dct
elif isinstance(val, datetime.datetime):
elif isinstance(val, datetime):
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT).encode('utf-8')
elif isinstance(val, crypto_x509.Certificate):
return val.public_bytes(x509.Encoding.DER)
@@ -1012,8 +1020,8 @@ class LDAPClient:
return val.decode('utf-8')
elif target_type is bool:
return val.decode('utf-8') == 'TRUE'
elif target_type is datetime.datetime:
return datetime.datetime.strptime(
elif target_type is datetime:
return datetime.strptime(
val.decode('utf-8'), LDAP_GENERALIZED_TIME_FORMAT)
elif target_type is DNSName:
return DNSName.from_text(val.decode('utf-8'))
@@ -1372,6 +1380,10 @@ class LDAPClient:
# value[-2:0] is empty string for the initial '\\'
value = u'\\'.join(
value[i:i+2] for i in six.moves.range(-2, len(value), 2))
elif isinstance(value, datetime):
value = value.strftime(
LDAP_GENERALIZED_TIME_FORMAT)
value = ldap.filter.escape_filter_chars(value)
else:
value = str(value)
value = ldap.filter.escape_filter_chars(value)
+23
View File
@@ -27,6 +27,7 @@
from __future__ import absolute_import
from datetime import datetime, timedelta
import os
import sys
@@ -140,6 +141,28 @@ class test_ldap:
cert = entry_attrs.get('usercertificate')[0]
assert cert.serial_number is not None
def test_generalized_time(self):
"""
Test that LDAP generalized time is converted to/from datetime
"""
self.conn = ldap2(api)
try:
self.conn.connect(autobind=True)
except errors.ACIError:
pytest.skip("Only executed as root")
if not api.Backend.rpcclient.isconnected():
api.Backend.rpcclient.connect()
newdate = datetime.now() + timedelta(days=365)
lastdate = api.Backend.ldap2.encode(newdate).decode('utf-8')
api.Command["user_mod"](
"admin",
**dict(setattr=("krbprincipalexpiration=%s" % lastdate))
)
result = api.Command["user_find"](
**dict(krbprincipalexpiration=lastdate)
)
assert result['count'] == 1
@pytest.mark.tier0
@pytest.mark.needs_ipaapi