mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipalib: Add DateTime parameter
Adds a parameter that represents a DateTime format using datetime.datetime
object from python's native datetime library.
In the CLI, accepts one of the following formats:
Accepts LDAP Generalized time without in the following format:
'%Y%m%d%H%M%SZ'
Accepts subset of values defined by ISO 8601:
'%Y-%m-%dT%H:%M:%SZ'
'%Y-%m-%dT%H:%MZ'
'%Y-%m-%dZ'
Also accepts above formats using ' ' (space) as a separator instead of 'T'.
As a simplification, it does not deal with timezone info and ISO 8601
values with timezone info (+-hhmm) are rejected. Values are expected
to be in the UTC timezone.
Values are saved to LDAP as LDAP Generalized time values in the format
'%Y%m%d%H%SZ' (no time fractions and UTC timezone is assumed). To avoid
confusion, in addition to subset of ISO 8601 values, the LDAP generalized
time in the format '%Y%m%d%H%M%SZ' is also accepted as an input (as this is the
format user will see on the output).
Part of: https://fedorahosted.org/freeipa/ticket/3306
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
committed by
Alexander Bokovoy
parent
093c72d60e
commit
1df696f543
1
API.txt
1
API.txt
@@ -4007,3 +4007,4 @@ capability: messages 2.52
|
|||||||
capability: optional_uid_params 2.54
|
capability: optional_uid_params 2.54
|
||||||
capability: permissions2 2.69
|
capability: permissions2 2.69
|
||||||
capability: primary_key_types 2.83
|
capability: primary_key_types 2.83
|
||||||
|
capability: datetime_values 2.84
|
||||||
|
|||||||
4
VERSION
4
VERSION
@@ -89,5 +89,5 @@ IPA_DATA_VERSION=20100614120000
|
|||||||
# #
|
# #
|
||||||
########################################################
|
########################################################
|
||||||
IPA_API_VERSION_MAJOR=2
|
IPA_API_VERSION_MAJOR=2
|
||||||
IPA_API_VERSION_MINOR=83
|
IPA_API_VERSION_MINOR=84
|
||||||
# Last change: jcholast - add 'primary_key_types' capability
|
# Last change: tbabej - added datetime value support
|
||||||
|
|||||||
@@ -886,7 +886,7 @@ from frontend import Command, LocalOrRemote, Updater, Advice
|
|||||||
from frontend import Object, Method
|
from frontend import Object, Method
|
||||||
from crud import Create, Retrieve, Update, Delete, Search
|
from crud import Create, Retrieve, Update, Delete, Search
|
||||||
from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password, DNParam, DeprecatedParam
|
from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password, DNParam, DeprecatedParam
|
||||||
from parameters import BytesEnum, StrEnum, IntEnum, AccessTime, File
|
from parameters import BytesEnum, StrEnum, IntEnum, AccessTime, File, DateTime
|
||||||
from errors import SkipPluginModule
|
from errors import SkipPluginModule
|
||||||
from text import _, ngettext, GettextFactory, NGettextFactory
|
from text import _, ngettext, GettextFactory, NGettextFactory
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ capabilities = dict(
|
|||||||
|
|
||||||
# primary_key_types: Non-unicode primary keys in command output
|
# primary_key_types: Non-unicode primary keys in command output
|
||||||
primary_key_types=u'2.83',
|
primary_key_types=u'2.83',
|
||||||
|
|
||||||
|
# support for datetime values on the client
|
||||||
|
datetime_values=u'2.84'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,13 @@ import plugable
|
|||||||
from errors import (PublicError, CommandError, HelpError, InternalError,
|
from errors import (PublicError, CommandError, HelpError, InternalError,
|
||||||
NoSuchNamespaceError, ValidationError, NotFound,
|
NoSuchNamespaceError, ValidationError, NotFound,
|
||||||
NotConfiguredError, PromptFailed)
|
NotConfiguredError, PromptFailed)
|
||||||
from constants import CLI_TAB
|
from constants import CLI_TAB, LDAP_GENERALIZED_TIME_FORMAT
|
||||||
from parameters import File, Str, Enum, Any
|
from parameters import File, Str, Enum, Any
|
||||||
from text import _
|
from text import _
|
||||||
from ipapython.version import API_VERSION
|
from ipapython.version import API_VERSION
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
def to_cli(name):
|
def to_cli(name):
|
||||||
"""
|
"""
|
||||||
@@ -155,6 +157,8 @@ class textui(backend.Backend):
|
|||||||
"""
|
"""
|
||||||
if type(value) is str:
|
if type(value) is str:
|
||||||
return base64.b64encode(value)
|
return base64.b64encode(value)
|
||||||
|
elif type(value) is datetime.datetime:
|
||||||
|
return value.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|||||||
@@ -206,3 +206,5 @@ DEFAULT_CONFIG = (
|
|||||||
('jsonrpc_uri', object), # derived from xmlrpc_uri in Env._finalize_core()
|
('jsonrpc_uri', object), # derived from xmlrpc_uri in Env._finalize_core()
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
LDAP_GENERALIZED_TIME_FORMAT = "%Y%m%d%H%M%SZ"
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ a more detailed description for clarity.
|
|||||||
import re
|
import re
|
||||||
import decimal
|
import decimal
|
||||||
import base64
|
import base64
|
||||||
|
import datetime
|
||||||
from xmlrpclib import MAXINT, MININT
|
from xmlrpclib import MAXINT, MININT
|
||||||
|
|
||||||
from types import NoneType
|
from types import NoneType
|
||||||
@@ -109,7 +110,7 @@ from text import _ as ugettext
|
|||||||
from plugable import ReadOnly, lock, check_name
|
from plugable import ReadOnly, lock, check_name
|
||||||
from errors import ConversionError, RequirementError, ValidationError
|
from errors import ConversionError, RequirementError, ValidationError
|
||||||
from errors import PasswordMismatch, Base64DecodeError
|
from errors import PasswordMismatch, Base64DecodeError
|
||||||
from constants import TYPE_ERROR, CALLABLE_ERROR
|
from constants import TYPE_ERROR, CALLABLE_ERROR, LDAP_GENERALIZED_TIME_FORMAT
|
||||||
from text import Gettext, FixMe
|
from text import Gettext, FixMe
|
||||||
from util import json_serialize
|
from util import json_serialize
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
@@ -1609,6 +1610,55 @@ class File(Str):
|
|||||||
('noextrawhitespace', bool, False),
|
('noextrawhitespace', bool, False),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class DateTime(Param):
|
||||||
|
"""
|
||||||
|
DateTime parameter type.
|
||||||
|
|
||||||
|
Accepts LDAP Generalized time without in the following format:
|
||||||
|
'%Y%m%d%H%M%SZ'
|
||||||
|
|
||||||
|
Accepts subset of values defined by ISO 8601:
|
||||||
|
'%Y-%m-%dT%H:%M:%SZ'
|
||||||
|
'%Y-%m-%dT%H:%MZ'
|
||||||
|
'%Y-%m-%dZ'
|
||||||
|
|
||||||
|
Also accepts above formats using ' ' (space) as a separator instead of 'T'.
|
||||||
|
|
||||||
|
Refer to the `man strftime` for the explanations for the %Y,%m,%d,%H.%M,%S.
|
||||||
|
"""
|
||||||
|
|
||||||
|
accepted_formats = [LDAP_GENERALIZED_TIME_FORMAT, # generalized time
|
||||||
|
'%Y-%m-%dT%H:%M:%SZ', # ISO 8601, second precision
|
||||||
|
'%Y-%m-%dT%H:%MZ', # ISO 8601, minute precision
|
||||||
|
'%Y-%m-%dZ', # ISO 8601, date only
|
||||||
|
'%Y-%m-%d %H:%M:%SZ', # non-ISO 8601, second precision
|
||||||
|
'%Y-%m-%d %H:%MZ'] # non-ISO 8601, minute precision
|
||||||
|
|
||||||
|
|
||||||
|
type = datetime.datetime
|
||||||
|
type_error = _('must be datetime value')
|
||||||
|
|
||||||
|
def _convert_scalar(self, value, index=None):
|
||||||
|
if isinstance(value, basestring):
|
||||||
|
for date_format in self.accepted_formats:
|
||||||
|
try:
|
||||||
|
time = datetime.datetime.strptime(value, date_format)
|
||||||
|
return time
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# If we get here, the strptime call did not succeed for any
|
||||||
|
# the accepted formats, therefore raise error
|
||||||
|
|
||||||
|
error = (_("does not match any of accepted formats: ") +
|
||||||
|
(', '.join(self.accepted_formats)))
|
||||||
|
|
||||||
|
raise ConversionError(name=self.get_param_name(),
|
||||||
|
index=index,
|
||||||
|
error=error)
|
||||||
|
|
||||||
|
return super(DateTime, self)._convert_scalar(value, index)
|
||||||
|
|
||||||
|
|
||||||
class AccessTime(Str):
|
class AccessTime(Str):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ Also see the `ipaserver.rpcserver` module.
|
|||||||
from types import NoneType
|
from types import NoneType
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import sys
|
import sys
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
import base64
|
import base64
|
||||||
@@ -41,17 +42,18 @@ import json
|
|||||||
import socket
|
import socket
|
||||||
from urllib2 import urlparse
|
from urllib2 import urlparse
|
||||||
|
|
||||||
from xmlrpclib import (Binary, Fault, dumps, loads, ServerProxy, Transport,
|
from xmlrpclib import (Binary, Fault, DateTime, dumps, loads, ServerProxy,
|
||||||
ProtocolError, MININT, MAXINT)
|
Transport, ProtocolError, MININT, MAXINT)
|
||||||
import kerberos
|
import kerberos
|
||||||
from dns import resolver, rdatatype
|
from dns import resolver, rdatatype
|
||||||
from dns.exception import DNSException
|
from dns.exception import DNSException
|
||||||
from nss.error import NSPRError
|
from nss.error import NSPRError
|
||||||
|
|
||||||
from ipalib.backend import Connectible
|
from ipalib.backend import Connectible
|
||||||
|
from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT
|
||||||
from ipalib.errors import (public_errors, UnknownError, NetworkError,
|
from ipalib.errors import (public_errors, UnknownError, NetworkError,
|
||||||
KerberosError, XMLRPCMarshallError, JSONError, ConversionError)
|
KerberosError, XMLRPCMarshallError, JSONError, ConversionError)
|
||||||
from ipalib import errors
|
from ipalib import errors, capabilities
|
||||||
from ipalib.request import context, Connection
|
from ipalib.request import context, Connection
|
||||||
from ipalib.util import get_current_principal
|
from ipalib.util import get_current_principal
|
||||||
from ipapython.ipa_log_manager import root_logger
|
from ipapython.ipa_log_manager import root_logger
|
||||||
@@ -163,6 +165,14 @@ def xml_wrap(value, version):
|
|||||||
return unicode(value)
|
return unicode(value)
|
||||||
if isinstance(value, DN):
|
if isinstance(value, DN):
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
# Encode datetime.datetime objects as xmlrpclib.DateTime objects
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
if capabilities.client_has_capability(version, 'datetime_values'):
|
||||||
|
return DateTime(value)
|
||||||
|
else:
|
||||||
|
return value.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||||
|
|
||||||
assert type(value) in (unicode, int, long, float, bool, NoneType)
|
assert type(value) in (unicode, int, long, float, bool, NoneType)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -196,6 +206,9 @@ def xml_unwrap(value, encoding='UTF-8'):
|
|||||||
if isinstance(value, Binary):
|
if isinstance(value, Binary):
|
||||||
assert type(value.data) is str
|
assert type(value.data) is str
|
||||||
return value.data
|
return value.data
|
||||||
|
if isinstance(value, DateTime):
|
||||||
|
# xmlprc DateTime is converted to string of %Y%m%dT%H:%M:%S format
|
||||||
|
return datetime.datetime.strptime(str(value), "%Y%m%dT%H:%M:%S")
|
||||||
assert type(value) in (unicode, int, float, bool, NoneType)
|
assert type(value) in (unicode, int, float, bool, NoneType)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -266,6 +279,11 @@ def json_encode_binary(val, version):
|
|||||||
return {'__base64__': base64.b64encode(str(val))}
|
return {'__base64__': base64.b64encode(str(val))}
|
||||||
elif isinstance(val, DN):
|
elif isinstance(val, DN):
|
||||||
return str(val)
|
return str(val)
|
||||||
|
elif isinstance(val, datetime.datetime):
|
||||||
|
if capabilities.client_has_capability(version, 'datetime_values'):
|
||||||
|
return {'__datetime__': val.strftime(LDAP_GENERALIZED_TIME_FORMAT)}
|
||||||
|
else:
|
||||||
|
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||||
else:
|
else:
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@@ -293,6 +311,9 @@ def json_decode_binary(val):
|
|||||||
if isinstance(val, dict):
|
if isinstance(val, dict):
|
||||||
if '__base64__' in val:
|
if '__base64__' in val:
|
||||||
return base64.b64decode(val['__base64__'])
|
return base64.b64decode(val['__base64__'])
|
||||||
|
elif '__datetime__' in val:
|
||||||
|
return datetime.datetime.strptime(val['__datetime__'],
|
||||||
|
LDAP_GENERALIZED_TIME_FORMAT)
|
||||||
else:
|
else:
|
||||||
return dict((k, json_decode_binary(v)) for k, v in val.items())
|
return dict((k, json_decode_binary(v)) for k, v in val.items())
|
||||||
elif isinstance(val, list):
|
elif isinstance(val, list):
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
import shutil
|
import shutil
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@@ -35,6 +36,7 @@ from ldap.controls import SimplePagedResultsControl
|
|||||||
import ldapurl
|
import ldapurl
|
||||||
|
|
||||||
from ipalib import errors, _
|
from ipalib import errors, _
|
||||||
|
from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipapython.ipautil import (
|
from ipapython.ipautil import (
|
||||||
format_netloc, wait_for_open_socket, wait_for_open_ports, CIDict)
|
format_netloc, wait_for_open_socket, wait_for_open_ports, CIDict)
|
||||||
@@ -239,6 +241,7 @@ class IPASimpleLDAPObject(object):
|
|||||||
'2.16.840.1.113719.1.301.4.41.1' : DN, # krbSubTrees
|
'2.16.840.1.113719.1.301.4.41.1' : DN, # krbSubTrees
|
||||||
'2.16.840.1.113719.1.301.4.52.1' : DN, # krbObjectReferences
|
'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.113719.1.301.4.53.1' : DN, # krbPrincContainerRef
|
||||||
|
'1.3.6.1.4.1.1466.115.121.1.24' : datetime.datetime,
|
||||||
}
|
}
|
||||||
|
|
||||||
# In most cases we lookup the syntax from the schema returned by
|
# In most cases we lookup the syntax from the schema returned by
|
||||||
@@ -408,6 +411,8 @@ class IPASimpleLDAPObject(object):
|
|||||||
elif isinstance(val, dict):
|
elif isinstance(val, dict):
|
||||||
dct = dict((self.encode(k), self.encode(v)) for k, v in val.iteritems())
|
dct = dict((self.encode(k), self.encode(v)) for k, v in val.iteritems())
|
||||||
return dct
|
return dct
|
||||||
|
elif isinstance(val, datetime.datetime):
|
||||||
|
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||||
elif val is None:
|
elif val is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -426,6 +431,8 @@ class IPASimpleLDAPObject(object):
|
|||||||
return val
|
return val
|
||||||
elif target_type is unicode:
|
elif target_type is unicode:
|
||||||
return val.decode('utf-8')
|
return val.decode('utf-8')
|
||||||
|
elif target_type is datetime.datetime:
|
||||||
|
return datetime.datetime.strptime(val, LDAP_GENERALIZED_TIME_FORMAT)
|
||||||
else:
|
else:
|
||||||
return target_type(val)
|
return target_type(val)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
|||||||
Reference in New Issue
Block a user