From 81fbfd9a61c3c9d31689745da2b9669b1a5c3eb0 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slaznick@redhat.com>
Date: Wed, 15 Jul 2015 11:06:53 +0200
Subject: [PATCH 3/3] Added Python bindings for FreeIPA time policies.

https://fedorahosted.org/freeipa/ticket/547
---
 src/python/pyhbac.c      | 370 +++++++++++++++++++++++++++++++++++++++++++++--
 src/tests/pyhbac-test.py | 174 +++++++++++++++++++---
 2 files changed, 515 insertions(+), 29 deletions(-)

diff --git a/src/python/pyhbac.c b/src/python/pyhbac.c
index fca476c56ba697693c793ecad7945ae7edf3dffd..75cd66dae8ef3a846ac13352dbc18efce8f60d08 100644
--- a/src/python/pyhbac.c
+++ b/src/python/pyhbac.c
@@ -622,6 +622,329 @@ fail:
     return NULL;
 }
 
+/* ==================== HBAC Time Rules Element ==========*/
+typedef struct {
+    PyObject_HEAD
+
+    PyObject *timezone;
+    PyObject *accesstimes;
+    PyObject *exceptions;
+} HbacTimeRules;
+
+static PyObject *
+HbacTimeRules_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    HbacTimeRules *self;
+
+    self = (HbacTimeRules *) type->tp_alloc(type, 0);
+    if(self == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    self->timezone = PyUnicode_FromString("");
+    self->accesstimes = PyList_New(0);
+    self->exceptions = PyList_New(0);
+    if(!self->timezone || !self->accesstimes || !self->exceptions) {
+        Py_DECREF(self);
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    return (PyObject *) self;
+}
+
+static int
+HbacTimeRules_clear(HbacTimeRules *self)
+{
+    Py_CLEAR(self->timezone);
+    Py_CLEAR(self->accesstimes);
+    Py_CLEAR(self->exceptions);
+    return 0;
+}
+
+static void
+HbacTimeRules_dealloc(HbacTimeRules *self)
+{
+    HbacTimeRules_clear(self);
+    Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static int
+HbacTimeRules_traverse(HbacTimeRules *self, visitproc visit, void *arg)
+{
+    Py_VISIT(self->timezone);
+    Py_VISIT(self->accesstimes);
+    Py_VISIT(self->exceptions);
+    return 0;
+}
+
+static int
+hbac_time_rules_set_timezone(HbacTimeRules *self, PyObject *timezone,
+                             void *closure);
+
+static int
+hbac_time_rules_set_accesstimes(HbacTimeRules *self, PyObject *accesstimes,
+                             void *closure);
+
+ static int
+ hbac_time_rules_set_exceptions(HbacTimeRules *self, PyObject *exceptions,
+                                 void *closure);
+
+static int
+HbacTimeRules_init(HbacTimeRules *self, PyObject *args, PyObject *kwargs)
+{
+    const char * const kwlist[] = { "timezone", "accesstimes",
+                                    "exceptions", NULL};
+    PyObject *timezone = NULL;
+    PyObject *accesstimes = NULL;
+    PyObject *exceptions = NULL;
+
+    if(!PyArg_ParseTupleAndKeywords(args, kwargs,
+                                    sss_py_const_p(char, "|OOO"),
+                                    discard_const_p(char *, kwlist),
+                                    &timezone, &accesstimes, &exceptions)) {
+        return -1;
+    }
+
+    if (timezone) {
+        if(hbac_time_rules_set_timezone(self, timezone, NULL) != 0) {
+            return -1;
+        }
+    }
+
+    if(accesstimes) {
+        if(hbac_time_rules_set_accesstimes(self, accesstimes, NULL) != 0) {
+            return -1;
+        }
+    }
+
+    if(exceptions) {
+        if(hbac_time_rules_set_exceptions(self, exceptions, NULL) != 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int
+hbac_time_rules_set_timezone(HbacTimeRules *self,
+                             PyObject *timezone,
+                             void *closure)
+{
+    CHECK_ATTRIBUTE_DELETE(timezone, "timezone");
+
+    if(!PyBytes_Check(timezone) && !PyUnicode_Check(timezone)) {
+        PyErr_Format(PyExc_TypeError, "timezone must be a string or Unicode");
+        return -1;
+    }
+
+    SAFE_SET(self->timezone, timezone);
+    return 0;
+}
+
+static PyObject *
+hbac_time_rules_get_timezone(HbacTimeRules *self, void *closure)
+{
+    if(PyUnicode_Check(self->timezone)) {
+        Py_INCREF(self->timezone);
+        return self->timezone;
+    }
+    else if (PyBytes_Check(self->timezone)) {
+        return PyUnicode_FromEncodedObject(self->timezone,
+                                           PYHBAC_ENCODING, PYHBAC_ENCODING_ERRORS);
+    }
+
+    PyErr_Format(PyExc_TypeError, "timezone must be a string or Unicode");
+    return NULL;
+}
+
+static int
+hbac_time_rules_set_accesstimes(HbacTimeRules *self,
+                                PyObject *accesstimes,
+                                void *closure)
+{
+    CHECK_ATTRIBUTE_DELETE(accesstimes, "accesstimes");
+
+    if(!verify_sequence(accesstimes, "accesstimes")) {
+        return -1;
+    }
+
+    SAFE_SET(self->accesstimes, accesstimes);
+    return 0;
+
+}
+
+static PyObject *
+hbac_time_rules_get_accesstimes(HbacTimeRules *self, void *closure)
+{
+    Py_INCREF(self->accesstimes);
+    return self->accesstimes;
+}
+
+static int
+hbac_time_rules_set_exceptions(HbacTimeRules *self,
+                               PyObject *exceptions,
+                               void *closure)
+{
+    CHECK_ATTRIBUTE_DELETE(exceptions, "exceptions");
+
+    if(!verify_sequence(exceptions, "exceptions")) {
+        return -1;
+    }
+
+    SAFE_SET(self->exceptions, exceptions);
+    return 0;
+}
+
+static PyObject *
+hbac_time_rules_get_exceptions(HbacTimeRules *self, void *closure)
+{
+    Py_INCREF(self->exceptions);
+    return self->exceptions;
+}
+
+static PyObject *
+HbacTimeRules_repr(HbacTimeRules *self)
+{
+    char *straccesstimes;
+    char *strexceptions;
+    PyObject *o, *format, *args;
+
+    format = PyUnicode_FromString("<timezone '%s' accesstimes [%s] exceptions [%s]>");
+    if(format == NULL) {
+        return NULL;
+    }
+
+    straccesstimes = str_concat_sequence(self->accesstimes,
+                                         discard_const_p(char, ","));
+    strexceptions = str_concat_sequence(self->exceptions,
+                                        discard_const_p(char, ","));
+
+    if(straccesstimes == NULL || strexceptions == NULL) {
+        PyMem_Free(straccesstimes);
+        PyMem_Free(strexceptions);
+        Py_DECREF(format);
+        return NULL;
+    }
+
+    args = Py_BuildValue(sss_py_const_p(char, "Oss"),
+                                        self->timezone, straccesstimes,
+                                        strexceptions);
+    if(args == NULL) {
+        PyMem_Free(straccesstimes);
+        PyMem_Free(strexceptions);
+        Py_DECREF(format);
+        return NULL;
+    }
+
+    o = PyUnicode_Format(format, args);
+    PyMem_Free(straccesstimes);
+    PyMem_Free(strexceptions);
+    Py_DECREF(format);
+    Py_DECREF(args);
+    return o;
+}
+
+PyDoc_STRVAR(HbacTimeRules_timezone__doc__,
+"(string) describing the time zone of the rule");
+PyDoc_STRVAR(HbacTimeRules_accesstimes__doc__,
+"(sequence of strings) describing when the rule should be applied");
+PyDoc_STRVAR(HbacTimeRules_exceptions__doc__,
+"(sequence of strings) describing time exceptions from accesstimes");
+
+static PyGetSetDef py_hbac_time_rules_getset[] = {
+    { discard_const_p(char, "timezone"),
+      (getter) hbac_time_rules_get_timezone,
+      (setter) hbac_time_rules_set_timezone,
+      HbacTimeRules_timezone__doc__,
+      NULL },
+
+    { discard_const_p(char, "accesstimes"),
+      (getter) hbac_time_rules_get_accesstimes,
+      (setter) hbac_time_rules_set_accesstimes,
+      HbacTimeRules_accesstimes__doc__,
+      NULL },
+
+    { discard_const_p(char, "exceptions"),
+      (getter) hbac_time_rules_get_exceptions,
+      (setter) hbac_time_rules_set_exceptions,
+      HbacTimeRules_exceptions__doc__,
+      NULL },
+
+    { NULL, 0, 0, 0, NULL } /* Sentinel */
+};
+
+PyDoc_STRVAR(HbacTimeRules__doc__,
+"IPA HBAC Time Rules\n\n"
+"HbacTimeRules() -> new empty time policies element\n"
+"HbacTimeRules([timezone], [accessTime], [accessTimeExclude])\n");
+
+static PyTypeObject pyhbac_hbactime_rules_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = sss_py_const_p(char, "pyhbac.HbacTimeRules"),
+    .tp_basicsize = sizeof(HbacTimeRules),
+    .tp_new = HbacTimeRules_new,
+    .tp_dealloc = (destructor) HbacTimeRules_dealloc,
+    .tp_traverse = (traverseproc) HbacTimeRules_traverse,
+    .tp_clear = (inquiry) HbacTimeRules_clear,
+    .tp_init = (initproc) HbacTimeRules_init,
+    .tp_repr = (reprfunc) HbacTimeRules_repr,
+    .tp_getset = py_hbac_time_rules_getset,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+    .tp_doc = HbacTimeRules__doc__
+};
+
+static void
+free_hbac_time_rules(struct hbac_time_rules *el)
+{
+    if(!el) return;
+
+    free_string_list(el->accesstimes);
+    free_string_list(el->exceptions);
+
+    PyMem_Free(discard_const_p(char, el->timezone));
+    PyMem_Free(el);
+}
+
+struct hbac_time_rules *
+HbacTimeRules_to_native(HbacTimeRules *pyel)
+{
+    struct hbac_time_rules *tr = NULL;
+    PyObject *tmp;
+
+    if(!PyObject_IsInstance((PyObject *) pyel,
+                            (PyObject *) &pyhbac_hbactime_rules_type)) {
+        PyErr_Format(PyExc_TypeError,
+                     "The element must be of type HbacTimeRules\n");
+        goto fail;
+    }
+
+    tr = PyMem_Malloc(sizeof(struct hbac_time_rules));
+    if(!tr) {
+        PyErr_NoMemory();
+        goto fail;
+    }
+
+    tmp = get_utf8_string(pyel->timezone, "timezone");
+    tr->timezone = py_strdup(PyBytes_AsString(tmp));
+    tr->accesstimes = sequence_as_string_list(pyel->accesstimes, "accesstimes");
+    tr->exceptions = sequence_as_string_list(pyel->exceptions, "exceptions");
+
+    Py_DECREF(tmp);
+    if(!tr->timezone || !tr->accesstimes || !tr->exceptions) {
+        goto fail;
+    }
+
+    return tr;
+
+fail:
+    free_hbac_time_rules(tr);
+    return NULL;
+}
+
+
 /* ==================== HBAC Rule ========================*/
 typedef struct {
     PyObject_HEAD
@@ -633,6 +956,7 @@ typedef struct {
     HbacRuleElement *services;
     HbacRuleElement *targethosts;
     HbacRuleElement *srchosts;
+    HbacTimeRules *timerules;
 } HbacRuleObject;
 
 static void
@@ -672,12 +996,19 @@ HbacRule_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     self->srchosts = (HbacRuleElement *) HbacRuleElement_new(
                                                 &pyhbac_hbacrule_element_type,
                                                 NULL, NULL);
+    self->timerules = (HbacTimeRules *) HbacTimeRules_new(
+                                                &pyhbac_hbactime_rules_type,
+                                                NULL, NULL);
+
     if (self->services == NULL || self->users == NULL ||
-        self->targethosts == NULL || self->srchosts == NULL) {
+        self->targethosts == NULL || self->srchosts == NULL
+        || self->timerules == NULL
+        ) {
         Py_XDECREF(self->services);
         Py_XDECREF(self->users);
         Py_XDECREF(self->targethosts);
         Py_XDECREF(self->srchosts);
+        Py_XDECREF(self->timerules);
         Py_DECREF(self->name);
         Py_DECREF(self);
         PyErr_NoMemory();
@@ -695,6 +1026,7 @@ HbacRule_clear(HbacRuleObject *self)
     Py_CLEAR(self->users);
     Py_CLEAR(self->targethosts);
     Py_CLEAR(self->srchosts);
+    Py_CLEAR(self->timerules);
     return 0;
 }
 
@@ -713,6 +1045,7 @@ HbacRule_traverse(HbacRuleObject *self, visitproc visit, void *arg)
     Py_VISIT((PyObject *) self->users);
     Py_VISIT((PyObject *) self->targethosts);
     Py_VISIT((PyObject *) self->srchosts);
+    Py_VISIT((PyObject *) self->timerules);
     return 0;
 }
 
@@ -754,7 +1087,8 @@ HbacRule_init(HbacRuleObject *self, PyObject *args, PyObject *kwargs)
     if (HbacRuleElement_init(self->users, empty_tuple, NULL) == -1 ||
         HbacRuleElement_init(self->services, empty_tuple, NULL) == -1 ||
         HbacRuleElement_init(self->targethosts, empty_tuple, NULL) == -1 ||
-        HbacRuleElement_init(self->srchosts, empty_tuple, NULL) == -1) {
+        HbacRuleElement_init(self->srchosts, empty_tuple, NULL) == -1 ||
+        HbacTimeRules_init(self->timerules, empty_tuple, NULL) == -1) {
         Py_DECREF(empty_tuple);
         return -1;
     }
@@ -867,11 +1201,13 @@ HbacRule_repr(HbacRuleObject *self)
     PyObject *services_repr;
     PyObject *targethosts_repr;
     PyObject *srchosts_repr;
+    PyObject *timerules_repr;
     PyObject *o, *format, *args;
 
     format = PyUnicode_FromString("<name %s enabled %d "
                                             "users %s services %s "
-                                            "targethosts %s srchosts %s>");
+                                            "targethosts %s srchosts %s "
+                                            "timerules %s>");
     if (format == NULL) {
         return NULL;
     }
@@ -880,25 +1216,30 @@ HbacRule_repr(HbacRuleObject *self)
     services_repr = HbacRuleElement_repr(self->services);
     targethosts_repr = HbacRuleElement_repr(self->targethosts);
     srchosts_repr = HbacRuleElement_repr(self->srchosts);
+    timerules_repr = HbacTimeRules_repr(self->timerules);
     if (users_repr == NULL || services_repr == NULL ||
-        targethosts_repr == NULL || srchosts_repr == NULL) {
+        targethosts_repr == NULL || srchosts_repr == NULL ||
+        timerules_repr == NULL) {
         Py_XDECREF(users_repr);
         Py_XDECREF(services_repr);
         Py_XDECREF(targethosts_repr);
         Py_XDECREF(srchosts_repr);
+        Py_XDECREF(timerules_repr);
         Py_DECREF(format);
         return NULL;
     }
 
-    args = Py_BuildValue(sss_py_const_p(char, "OiOOOO"),
+    args = Py_BuildValue(sss_py_const_p(char, "OiOOOOO"),
                          self->name, self->enabled,
                          users_repr, services_repr,
-                         targethosts_repr, srchosts_repr);
+                         targethosts_repr, srchosts_repr,
+                         timerules_repr);
     if (args == NULL) {
         Py_DECREF(users_repr);
         Py_DECREF(services_repr);
         Py_DECREF(targethosts_repr);
         Py_DECREF(srchosts_repr);
+        Py_XDECREF(timerules_repr);
         Py_DECREF(format);
         return NULL;
     }
@@ -908,6 +1249,7 @@ HbacRule_repr(HbacRuleObject *self)
     Py_DECREF(services_repr);
     Py_DECREF(targethosts_repr);
     Py_DECREF(srchosts_repr);
+    Py_XDECREF(timerules_repr);
     Py_DECREF(format);
     Py_DECREF(args);
     return o;
@@ -950,9 +1292,9 @@ py_hbac_rule_validate(HbacRuleObject *self, PyObject *args)
         PyErr_NoMemory();
         goto fail;
     }
-
+    /* TODO: !for() should count with the timerules also! */
     for (attr = HBAC_RULE_ELEMENT_USERS;
-         attr <= HBAC_RULE_ELEMENT_SOURCEHOSTS;
+         attr <= HBAC_RULE_TIMERULES;
          attr <<= 1) {
         if (!(missing & attr)) continue;
 
@@ -1003,6 +1345,8 @@ PyDoc_STRVAR(HbacRuleObject_targethosts__doc__,
 "(HbacRuleElement) Target hosts for which this rule applies");
 PyDoc_STRVAR(HbacRuleObject_srchosts__doc__,
 "(HbacRuleElement) Source hosts for which this rule applies");
+PyDoc_STRVAR(HbacRuleObject_timerules__doc__,
+"(HbacTimeRules) Time policies for the rule");
 
 static PyMemberDef py_hbac_rule_members[] = {
     { discard_const_p(char, "users"), T_OBJECT_EX,
@@ -1021,6 +1365,10 @@ static PyMemberDef py_hbac_rule_members[] = {
       offsetof(HbacRuleObject, srchosts), 0,
       HbacRuleObject_srchosts__doc__},
 
+     { discard_const_p(char, "timerules"), T_OBJECT_EX,
+       offsetof(HbacRuleObject, timerules), 0,
+       HbacRuleObject_timerules__doc__},
+
     { NULL, 0, 0, 0, NULL } /* Sentinel */
 };
 
@@ -1078,6 +1426,7 @@ free_hbac_rule(struct hbac_rule *rule)
     free_hbac_rule_element(rule->users);
     free_hbac_rule_element(rule->targethosts);
     free_hbac_rule_element(rule->srchosts);
+    free_hbac_time_rules(rule->timerules);
 
     PyMem_Free(discard_const_p(char, rule->name));
     PyMem_Free(rule);
@@ -1117,8 +1466,10 @@ HbacRule_to_native(HbacRuleObject *pyrule)
     rule->users = HbacRuleElement_to_native(pyrule->users);
     rule->targethosts = HbacRuleElement_to_native(pyrule->targethosts);
     rule->srchosts =  HbacRuleElement_to_native(pyrule->srchosts);
+    rule->timerules = HbacTimeRules_to_native(pyrule->timerules);
     if (!rule->services || !rule->users ||
-        !rule->targethosts || !rule->srchosts) {
+        !rule->targethosts || !rule->srchosts ||
+        !rule->timerules) {
         goto fail;
     }
 
@@ -1982,6 +2333,7 @@ initpyhbac(void)
 
     TYPE_READY(m, pyhbac_hbacrule_type, "HbacRule");
     TYPE_READY(m, pyhbac_hbacrule_element_type, "HbacRuleElement");
+    TYPE_READY(m, pyhbac_hbactime_rules_type, "HbacTimeRules");
     TYPE_READY(m, pyhbac_hbacrequest_element_type, "HbacRequestElement");
     TYPE_READY(m, pyhbac_hbacrequest_type, "HbacRequest");
 
diff --git a/src/tests/pyhbac-test.py b/src/tests/pyhbac-test.py
index 151cbd31f895c00b170106d959f97b8b51c0bf8a..7a3dfe0de32c55749fcb24a2258c270c86d494a8 100755
--- a/src/tests/pyhbac-test.py
+++ b/src/tests/pyhbac-test.py
@@ -137,7 +137,96 @@ class PyHbacRuleElementTest(unittest.TestCase):
         el.names = ['foo']
         el.groups = ['bar, baz']
         self.assertEquals(el.__repr__(), u'<category 1 names [foo] groups [bar, baz]>')
-        
+
+
+class PyHbacTimeRulesTest(unittest.TestCase):
+    def testInstantiateEmpty(self):
+        tr = pyhbac.HbacTimeRules()
+        self.assertEqual(tr.timezone, '')
+        self.assertItemsEqual(tr.accesstimes, [])
+        self.assertItemsEqual(tr.exceptions, [])
+
+    def testInit(self):
+        tz = "Europe/Prague"
+        tr = pyhbac.HbacTimeRules(timezone=tz)
+        self.assertEqual(tr.timezone, tz)
+
+        access = ["weekofmonth=5", "timeofday=2000-2200"]
+        tr = pyhbac.HbacTimeRules(accesstimes=access)
+        self.assertItemsEqual(tr.accesstimes, access)
+
+        expts = ["dayofweek=1-7", "year=2012-2022"]
+        tr = pyhbac.HbacTimeRules(exceptions=expts)
+        self.assertItemsEqual(tr.exceptions, expts)
+
+    def testGetSet(self):
+        tz = "Europe/Oslo"
+        tr = pyhbac.HbacTimeRules()
+        self.assertEqual(tr.timezone, "")
+        tr.timezone = tz
+        self.assertEqual(tr.timezone, tz)
+
+        access = ["dayofweek=1-7", "year=2012-2022"]
+        tr = pyhbac.HbacTimeRules()
+        self.assertItemsEqual(tr.accesstimes, [])
+        tr.accesstimes = access
+        self.assertItemsEqual(tr.accesstimes, access)
+
+        expts = ["weekofmonth=5", "timeofday=2000-2200"]
+        tr = pyhbac.HbacTimeRules()
+        self.assertItemsEqual(tr.exceptions, [])
+        tr.exceptions = expts
+        self.assertItemsEqual(tr.exceptions, expts)
+
+        # non-list iterables
+        access = ("weekofmonth=5", "timeofday=2000-2200")
+        tr = pyhbac.HbacTimeRules()
+        self.assertItemsEqual(tr.accesstimes, [])
+        tr.accesstimes = access
+        self.assertItemsEqual(tr.accesstimes, access)
+
+        expts = ("dayofweek=1-7", "year=2012-2022")
+        tr = pyhbac.HbacTimeRules()
+        self.assertItemsEqual(tr.exceptions, [])
+        tr.exceptions = expts
+        self.assertItemsEqual(tr.exceptions, expts)
+
+    def testTimezone(self):
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, timezone=123)
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, timezone=None)
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, timezone=[1,2,3])
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, timezone=["a", "b"])
+
+    def testNonIterable(self):
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, accesstimes=123)
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, accesstimes=None)
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, exceptions=123)
+        self.assertRaises(TypeError, pyhbac.HbacTimeRules, exceptions=None)
+
+    def testTimeRulesReference(self):
+        def _get_timerules():
+            tz = "utc"
+            access = ["dayofweek=1-7", "year=2012-2022"]
+            expts = ["weekofmonth=5", "timeofday=2000-2200"]
+            return pyhbac.HbacTimeRules(timezone=tz,
+                                        accesstimes=access,
+                                        exceptions=expts)
+        tr = _get_timerules()
+        self.assertEqual(tr.timezone, "utc")
+        self.assertItemsEqual(tr.accesstimes, ["dayofweek=1-7", "year=2012-2022"])
+        self.assertItemsEqual(tr.exceptions, ["weekofmonth=5", "timeofday=2000-2200"])
+
+    def testRepr(self):
+        tr = pyhbac.HbacTimeRules()
+        self.assertEquals(tr.__repr__(), u'<timezone \'\' accesstimes [] exceptions []>')
+
+        tr.timezone = "utc"
+        tr.accesstimes = ["timeofday=1000"]
+        tr.exceptions = ["timeofday=2000", "year=2000"]
+        self.assertEquals(tr.__repr__(), u"<timezone \'utc\' "
+                          "accesstimes [timeofday=1000] "
+                          "exceptions [timeofday=2000,year=2000]>")
+
 
 class PyHbacRuleTest(unittest.TestCase):
     def testRuleGetSetName(self):
@@ -214,6 +303,39 @@ class PyHbacRuleTest(unittest.TestCase):
         self.assertItemsEqual(rule.users.names, users)
         self.assertItemsEqual(rule.users.groups, user_groups)
 
+    def testTimeRulesInRule(self):
+        tz = "Africa/Kigali"
+        times = ["timeofday=0000", "year=2000"]
+        ex_times = ["timeofday=2359","monthofyear=5"]
+
+        rule = pyhbac.HbacRule("testTimeRules")
+        self.assertIsInstance(rule.timerules, pyhbac.HbacTimeRules)
+
+        self.assertIsInstance(rule.timerules.accesstimes, list)
+        self.assertIsInstance(rule.timerules.exceptions, list)
+        self.assertItemsEqual(rule.timerules.accesstimes, [])
+        self.assertItemsEqual(rule.timerules.exceptions, [])
+
+        # Assign by copying HbacTimeRules object
+        time_el = pyhbac.HbacTimeRules(timezone=tz,
+                                       accesstimes=times,
+                                       exceptions=ex_times)
+        rule = pyhbac.HbacRule('testTimeRules')
+        rule.timerules = time_el
+        self.assertEqual(rule.timerules.timezone, tz)
+        self.assertItemsEqual(rule.timerules.accesstimes, times)
+        self.assertItemsEqual(rule.timerules.exceptions, ex_times)
+
+        # Assign directly
+        rule = pyhbac.HbacRule("testTimeRules")
+        rule.timerules.timezone = tz
+        rule.timerules.accesstimes = times
+        rule.timerules.exceptions = ex_times
+        self.assertEqual(rule.timerules.timezone, tz)
+        self.assertItemsEqual(rule.timerules.accesstimes, times)
+        self.assertItemsEqual(rule.timerules.exceptions, ex_times)
+
+
     def testRuleElementInRuleReference(self):
         " Test that references to RuleElement are kept even if element goes out of scope "
         def _get_rule():
@@ -229,33 +351,40 @@ class PyHbacRuleTest(unittest.TestCase):
         self.assertItemsEqual(rule.users.groups, [ "abc", "def" ])
 
     def testRepr(self):
+        self.maxDiff = None
         r = pyhbac.HbacRule('foo')
         self.assertEqual(r.__repr__(), u"<name foo enabled 0 "
                                         "users <category 0 names [] groups []> "
                                         "services <category 0 names [] groups []> "
                                         "targethosts <category 0 names [] groups []> "
-                                        "srchosts <category 0 names [] groups []>>")
+                                        "srchosts <category 0 names [] groups []> "
+                                        "timerules <timezone '' accesstimes [] exceptions []>>")
 
         name = "someuser"
         service = "ssh"
         srchost = "host1"
         targethost = "host2"
+        timezone = "Europe/Prague"
 
         r.users.names = [ name ]
         r.services.names = [ service ]
         r.srchosts.names = [ srchost ]
         r.targethosts.names = [ targethost ]
+        r.timerules.timezone = timezone
 
         self.assertEqual(r.__repr__(), u"<name foo enabled 0 "
                                         "users <category 0 names [%s] groups []> "
                                         "services <category 0 names [%s] groups []> "
                                         "targethosts <category 0 names [%s] groups []> "
-                                        "srchosts <category 0 names [%s] groups []>>" %
+                                        "srchosts <category 0 names [%s] groups []> "
+                                        "timerules <timezone 'Europe/Prague' accesstimes [] exceptions []>>" %
                                         (name, service, targethost, srchost))
 
     def testValidate(self):
         r = pyhbac.HbacRule('valid_rule')
 
+        # Currently, time rules would be missing only if they were null
+        #       ==> No need to test Python whether they are missing
         valid, missing = r.validate()
         self.assertEqual(valid, False)
         self.assertItemsEqual(missing, ( pyhbac.HBAC_RULE_ELEMENT_USERS,
@@ -361,6 +490,7 @@ class PyHbacRequestTest(unittest.TestCase):
         self.assertRaises((TypeError, AttributeError), req.__setattr__, "rule_name", "foo")
 
     def testEvaluate(self):
+        # TODO: Add timerules testing
         name = "someuser"
         service = "ssh"
         srchost = "host1"
@@ -519,25 +649,29 @@ if __name__ == "__main__":
     if not res.wasSuccessful():
         error |= 0x2
 
-#    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRuleTest)
-#    res = unittest.TextTestRunner().run(suite)
-#    if not res.wasSuccessful():
-#        error |= 0x3
+    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacTimeRulesTest)
+    res = unittest.TextTestRunner().run(suite)
+    if not res.wasSuccessful():
+        error |= 0x3
 
-#    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRequestElementTest)
-#    res = unittest.TextTestRunner().run(suite)
-#    if not res.wasSuccessful():
-#        error |= 0x4
+    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRuleTest)
+    res = unittest.TextTestRunner().run(suite)
+    if not res.wasSuccessful():
+        error |= 0x4
 
-#    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRequestTest)
-#    res = unittest.TextTestRunner().run(suite)
-#    if not res.wasSuccessful():
-#        error |= 0x5
+    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRequestElementTest)
+    res = unittest.TextTestRunner().run(suite)
+    if not res.wasSuccessful():
+        error |= 0x5
 
-#    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacModuleTest)
-#    res = unittest.TextTestRunner().run(suite)
-#    if not res.wasSuccessful():
-#        error |= 0x6
+    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRequestTest)
+    res = unittest.TextTestRunner().run(suite)
+    if not res.wasSuccessful():
+        error |= 0x6
+
+    suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacModuleTest)
+    res = unittest.TextTestRunner().run(suite)
+    if not res.wasSuccessful():
+        error |= 0x7
 
     sys.exit(error)
-
-- 
2.4.3

