summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/__init__.py2
-rw-r--r--ipalib/capabilities.py3
-rw-r--r--ipalib/cli.py6
-rw-r--r--ipalib/constants.py2
-rw-r--r--ipalib/parameters.py52
-rw-r--r--ipalib/rpc.py27
6 files changed, 86 insertions, 6 deletions
diff --git a/ipalib/__init__.py b/ipalib/__init__.py
index 553c07197..2a87103b8 100644
--- a/ipalib/__init__.py
+++ b/ipalib/__init__.py
@@ -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
diff --git a/ipalib/capabilities.py b/ipalib/capabilities.py
index 3dd93294f..f2e45a0f6 100644
--- a/ipalib/capabilities.py
+++ b/ipalib/capabilities.py
@@ -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'
)
diff --git a/ipalib/cli.py b/ipalib/cli.py
index 4250aaf54..f03db9c61 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -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
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 6cc50eacf..e98eee6f8 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -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"
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index fc5e64981..33ae182b5 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -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):
"""
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 73ae115b3..c44ffb6e1 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -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):