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: permissions2 2.69
|
||||
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_MINOR=83
|
||||
# Last change: jcholast - add 'primary_key_types' capability
|
||||
IPA_API_VERSION_MINOR=84
|
||||
# Last change: tbabej - added datetime value support
|
||||
|
||||
@@ -886,7 +886,7 @@ from frontend import Command, LocalOrRemote, Updater, Advice
|
||||
from frontend import Object, Method
|
||||
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 BytesEnum, StrEnum, IntEnum, AccessTime, File
|
||||
from parameters import BytesEnum, StrEnum, IntEnum, AccessTime, File, DateTime
|
||||
from errors import SkipPluginModule
|
||||
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=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,
|
||||
NoSuchNamespaceError, ValidationError, NotFound,
|
||||
NotConfiguredError, PromptFailed)
|
||||
from constants import CLI_TAB
|
||||
from constants import CLI_TAB, LDAP_GENERALIZED_TIME_FORMAT
|
||||
from parameters import File, Str, Enum, Any
|
||||
from text import _
|
||||
from ipapython.version import API_VERSION
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
def to_cli(name):
|
||||
"""
|
||||
@@ -155,6 +157,8 @@ class textui(backend.Backend):
|
||||
"""
|
||||
if type(value) is str:
|
||||
return base64.b64encode(value)
|
||||
elif type(value) is datetime.datetime:
|
||||
return value.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
@@ -206,3 +206,5 @@ DEFAULT_CONFIG = (
|
||||
('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 decimal
|
||||
import base64
|
||||
import datetime
|
||||
from xmlrpclib import MAXINT, MININT
|
||||
|
||||
from types import NoneType
|
||||
@@ -109,7 +110,7 @@ from text import _ as ugettext
|
||||
from plugable import ReadOnly, lock, check_name
|
||||
from errors import ConversionError, RequirementError, ValidationError
|
||||
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 util import json_serialize
|
||||
from ipapython.dn import DN
|
||||
@@ -1609,6 +1610,55 @@ class File(Str):
|
||||
('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):
|
||||
"""
|
||||
|
||||
@@ -33,6 +33,7 @@ Also see the `ipaserver.rpcserver` module.
|
||||
from types import NoneType
|
||||
from decimal import Decimal
|
||||
import sys
|
||||
import datetime
|
||||
import os
|
||||
import locale
|
||||
import base64
|
||||
@@ -41,17 +42,18 @@ import json
|
||||
import socket
|
||||
from urllib2 import urlparse
|
||||
|
||||
from xmlrpclib import (Binary, Fault, dumps, loads, ServerProxy, Transport,
|
||||
ProtocolError, MININT, MAXINT)
|
||||
from xmlrpclib import (Binary, Fault, DateTime, dumps, loads, ServerProxy,
|
||||
Transport, ProtocolError, MININT, MAXINT)
|
||||
import kerberos
|
||||
from dns import resolver, rdatatype
|
||||
from dns.exception import DNSException
|
||||
from nss.error import NSPRError
|
||||
|
||||
from ipalib.backend import Connectible
|
||||
from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT
|
||||
from ipalib.errors import (public_errors, UnknownError, NetworkError,
|
||||
KerberosError, XMLRPCMarshallError, JSONError, ConversionError)
|
||||
from ipalib import errors
|
||||
from ipalib import errors, capabilities
|
||||
from ipalib.request import context, Connection
|
||||
from ipalib.util import get_current_principal
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
@@ -163,6 +165,14 @@ def xml_wrap(value, version):
|
||||
return unicode(value)
|
||||
if isinstance(value, DN):
|
||||
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)
|
||||
return value
|
||||
|
||||
@@ -196,6 +206,9 @@ def xml_unwrap(value, encoding='UTF-8'):
|
||||
if isinstance(value, Binary):
|
||||
assert type(value.data) is str
|
||||
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)
|
||||
return value
|
||||
|
||||
@@ -266,6 +279,11 @@ def json_encode_binary(val, version):
|
||||
return {'__base64__': base64.b64encode(str(val))}
|
||||
elif isinstance(val, DN):
|
||||
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:
|
||||
return val
|
||||
|
||||
@@ -293,6 +311,9 @@ def json_decode_binary(val):
|
||||
if isinstance(val, dict):
|
||||
if '__base64__' in val:
|
||||
return base64.b64decode(val['__base64__'])
|
||||
elif '__datetime__' in val:
|
||||
return datetime.datetime.strptime(val['__datetime__'],
|
||||
LDAP_GENERALIZED_TIME_FORMAT)
|
||||
else:
|
||||
return dict((k, json_decode_binary(v)) for k, v in val.items())
|
||||
elif isinstance(val, list):
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
import string
|
||||
import time
|
||||
import datetime
|
||||
import shutil
|
||||
from decimal import Decimal
|
||||
from copy import deepcopy
|
||||
@@ -35,6 +36,7 @@ from ldap.controls import SimplePagedResultsControl
|
||||
import ldapurl
|
||||
|
||||
from ipalib import errors, _
|
||||
from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT
|
||||
from ipapython import ipautil
|
||||
from ipapython.ipautil import (
|
||||
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.52.1' : DN, # krbObjectReferences
|
||||
'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
|
||||
@@ -408,6 +411,8 @@ class IPASimpleLDAPObject(object):
|
||||
elif isinstance(val, dict):
|
||||
dct = dict((self.encode(k), self.encode(v)) for k, v in val.iteritems())
|
||||
return dct
|
||||
elif isinstance(val, datetime.datetime):
|
||||
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||
elif val is None:
|
||||
return None
|
||||
else:
|
||||
@@ -426,6 +431,8 @@ class IPASimpleLDAPObject(object):
|
||||
return val
|
||||
elif target_type is unicode:
|
||||
return val.decode('utf-8')
|
||||
elif target_type is datetime.datetime:
|
||||
return datetime.datetime.strptime(val, LDAP_GENERALIZED_TIME_FORMAT)
|
||||
else:
|
||||
return target_type(val)
|
||||
except Exception, e:
|
||||
|
||||
Reference in New Issue
Block a user