diff options
author | Jakub Hrozek <jhrozek@redhat.com> | 2011-05-30 09:57:19 +0200 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2011-10-26 10:29:38 -0400 |
commit | 817d3fe806506c637988cf99c7ab774345292e05 (patch) | |
tree | fb8cb01d7b5a6198244bff4ee8de49c9240e23e4 /src/tests | |
parent | 9a58bc432bb9bc4ae4c452c5e600c94d4fc1c5a0 (diff) | |
download | sssd-817d3fe806506c637988cf99c7ab774345292e05.tar.gz sssd-817d3fe806506c637988cf99c7ab774345292e05.tar.xz sssd-817d3fe806506c637988cf99c7ab774345292e05.zip |
Provide python bindings for the HBAC evaluator library
Fixes for python HBAC bindings
These changes were proposed during a review:
* Change the signature of str_concat_sequence() to const char *
* use a getsetter for HbacRule.enabled to allow string true/false and
integer 1/0 in addition to bool
* fix a minor memory leak (HbacRequest.rule_name)
* remove overzealous discard consts
Fix python HBAC bindings for python <= 2.4
Several parts of the HBAC python bindings did not work with old Python
versions, such as the one shipped in RHEL5.
The changes include:
* a compatibility wrapper around python set object
* PyModule_AddIntMacro compat macro
* Py_ssize_t compat definition
* Do not use PyUnicode_FromFormat
* several function prototypes and structures used to have "char
arguments where they have "const char *" in recent versions.
This caused compilation warnings this patch mitigates by using
the discard_const hack on python 2.4
Remove dead code from python HBAC bindings
https://fedorahosted.org/sssd/ticket/935
Handle allocation error in python HBAC bindings
https://fedorahosted.org/sssd/ticket/934
HBAC rule validation Python bindings
https://fedorahosted.org/sssd/ticket/943
Diffstat (limited to 'src/tests')
-rwxr-xr-x | src/tests/pyhbac-test.py | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/src/tests/pyhbac-test.py b/src/tests/pyhbac-test.py new file mode 100755 index 000000000..5579180b0 --- /dev/null +++ b/src/tests/pyhbac-test.py @@ -0,0 +1,522 @@ +#!/usr/bin/python + +import unittest +import sys +import os +import copy + +srcdir = os.getenv('builddir') +if not srcdir: + srcdir = "." +MODPATH = srcdir + "/.libs" #FIXME - is there a way to get this from libtool? + +def compat_assertItemsEqual(this, expected_seq, actual_seq, msg=None): + return this.assertEqual(sorted(expected_seq), sorted(actual_seq)) + +def compat_assertIsInstance(this, obj, cls, msg=None): + return this.assertTrue(isinstance(obj, cls)) + +# add compat methods for old unittest.TestCase versions +# (python < 2.7, RHEL5 for instance) +if not hasattr(unittest.TestCase, "assertItemsEqual"): + setattr(unittest.TestCase, "assertItemsEqual", compat_assertItemsEqual) +if not hasattr(unittest.TestCase, "assertIsInstance"): + setattr(unittest.TestCase, "assertIsInstance", compat_assertIsInstance) + +class PyHbacImport(unittest.TestCase): + def setUp(self): + " Make sure we load the in-tree module " + self.system_path = sys.path[:] + sys.path = [ MODPATH ] + + def tearDown(self): + " Restore the system path " + sys.path = self.system_path + + def testImport(self): + " Import the module and assert it comes from tree " + try: + import pyhbac + except ImportError, e: + print >>sys.stderr, "Could not load the pyhbac module. Please check if it is compiled" + raise e + self.assertEqual(pyhbac.__file__, MODPATH + "/pyhbac.so") + +class PyHbacRuleElementTest(unittest.TestCase): + def testInstantiateEmpty(self): + el = pyhbac.HbacRuleElement() + self.assertItemsEqual(el.names, []) + self.assertItemsEqual(el.groups, []) + self.assertItemsEqual(el.category, set([pyhbac.HBAC_CATEGORY_NULL])) + + def testInit(self): + names = [ "foo", "bar" ] + el = pyhbac.HbacRuleElement(names=names) + self.assertItemsEqual(el.names, names) + + groups = [ "abc", "def" ] + el = pyhbac.HbacRuleElement(groups=groups) + self.assertItemsEqual(el.groups, groups) + + def testGetSet(self): + names = [ "foo", "bar" ] + el = pyhbac.HbacRuleElement() + self.assertItemsEqual(el.names, []) + el.names = names + self.assertItemsEqual(el.names, names) + + groups = [ "abc", "def" ] + el = pyhbac.HbacRuleElement() + self.assertItemsEqual(el.groups, []) + el.groups = groups + self.assertItemsEqual(el.groups, groups) + + # Test other iterables than list + groups = ( "abc", "def" ) + el = pyhbac.HbacRuleElement() + self.assertItemsEqual(el.groups, []) + el.groups = groups + self.assertItemsEqual(el.groups, groups) + + def testCategory(self): + el = pyhbac.HbacRuleElement() + assert pyhbac.HBAC_CATEGORY_NULL in el.category + assert pyhbac.HBAC_CATEGORY_ALL not in el.category + + el.category.add(pyhbac.HBAC_CATEGORY_ALL) + assert pyhbac.HBAC_CATEGORY_ALL in el.category + + el.category = set([pyhbac.HBAC_CATEGORY_ALL]) + assert pyhbac.HBAC_CATEGORY_ALL in el.category + + # negative tests + self.assertRaises(TypeError, el.__setattr__, "category", [pyhbac.HBAC_CATEGORY_ALL]) + self.assertRaises(TypeError, el.__setattr__, "category", None) + self.assertRaises(TypeError, el.__setattr__, "category", 1) + + def testNotIterable(self): + self.assertRaises(TypeError, pyhbac.HbacRuleElement, names=123) + self.assertRaises(TypeError, pyhbac.HbacRuleElement, names=None) + + def testRuleElementReference(self): + def _get_rule(): + users = [ "foo", "bar" ] + user_groups = [ "abc", "def" ] + return pyhbac.HbacRuleElement(names=users, groups=user_groups) + + el = _get_rule() + self.assertItemsEqual(el.names, [ "foo", "bar" ]) + self.assertItemsEqual(el.groups, [ "abc", "def" ]) + + def testRepr(self): + el = pyhbac.HbacRuleElement() + self.assertEquals(el.__repr__(), u'<category 0 names [] groups []>') + + el.category.add(pyhbac.HBAC_CATEGORY_ALL) + el.names = ['foo'] + el.groups = ['bar, baz'] + self.assertEquals(el.__repr__(), u'<category 1 names [foo] groups [bar, baz]>') + + +class PyHbacRuleTest(unittest.TestCase): + def testRuleGetSetName(self): + name = "testGetRule" + new_name = "testGetNewRule" + + rule = pyhbac.HbacRule(name) + self.assertEqual(rule.name, unicode(name)) + + rule.name = new_name + self.assertEqual(rule.name, unicode(new_name)) + + def testRuleGetSetEnabled(self): + rule = pyhbac.HbacRule("testRuleGetSetEnabled") + + rule.enabled = True + self.assertEqual(rule.enabled, True) + rule.enabled = False + self.assertEqual(rule.enabled, False) + + rule.enabled = "TRUE" + self.assertEqual(rule.enabled, True) + rule.enabled = "FALSE" + self.assertEqual(rule.enabled, False) + + rule.enabled = "true" + self.assertEqual(rule.enabled, True) + rule.enabled = "false" + self.assertEqual(rule.enabled, False) + + rule.enabled = "True" + self.assertEqual(rule.enabled, True) + rule.enabled = "False" + self.assertEqual(rule.enabled, False) + + rule.enabled = 1 + self.assertEqual(rule.enabled, True) + rule.enabled = 0 + self.assertEqual(rule.enabled, False) + + # negative test + self.assertRaises(TypeError, rule.__setattr__, "enabled", None) + self.assertRaises(TypeError, rule.__setattr__, "enabled", []) + self.assertRaises(ValueError, rule.__setattr__, "enabled", "foo") + self.assertRaises(ValueError, rule.__setattr__, "enabled", 5) + + def testRuleElementInRule(self): + users = [ "foo", "bar" ] + user_groups = [ "abc", "def" ] + + # rule should contain empty elements after instantiation + rule = pyhbac.HbacRule("testRuleElement") + self.assertIsInstance(rule.users, pyhbac.HbacRuleElement) + self.assertIsInstance(rule.services, pyhbac.HbacRuleElement) + self.assertIsInstance(rule.targethosts, pyhbac.HbacRuleElement) + self.assertIsInstance(rule.srchosts, pyhbac.HbacRuleElement) + + self.assertIsInstance(rule.users.names, list) + self.assertIsInstance(rule.users.groups, list) + self.assertItemsEqual(rule.users.names, []) + self.assertItemsEqual(rule.users.groups, []) + + # Assign by copying a HbacRuleElement + user_el = pyhbac.HbacRuleElement(names=users, groups=user_groups) + rule = pyhbac.HbacRule("testRuleElement") + rule.users = user_el + self.assertItemsEqual(rule.users.names, users) + self.assertItemsEqual(rule.users.groups, user_groups) + + # Assign directly + rule = pyhbac.HbacRule("testRuleElement") + rule.users.names = users + rule.users.groups = user_groups + self.assertItemsEqual(rule.users.names, users) + self.assertItemsEqual(rule.users.groups, user_groups) + + def testRuleElementInRuleReference(self): + " Test that references to RuleElement are kept even if element goes out of scope " + def _get_rule(): + users = [ "foo", "bar" ] + user_groups = [ "abc", "def" ] + el = pyhbac.HbacRuleElement(names=users, groups=user_groups) + rule = pyhbac.HbacRule("testRuleElement") + rule.users = el + return rule + + rule = _get_rule() + self.assertItemsEqual(rule.users.names, [ "foo", "bar" ]) + self.assertItemsEqual(rule.users.groups, [ "abc", "def" ]) + + def testRepr(self): + 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 []>>") + + name = "someuser" + service = "ssh" + srchost = "host1" + targethost = "host2" + + r.users.names = [ name ] + r.services.names = [ service ] + r.srchosts.names = [ srchost ] + r.targethosts.names = [ targethost ] + + 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 []>>" % + (name, service, targethost, srchost)) + + def testValidate(self): + r = pyhbac.HbacRule('valid_rule') + + valid, missing = r.validate() + self.assertEqual(valid, False) + self.assertItemsEqual(missing, ( pyhbac.HBAC_RULE_ELEMENT_USERS, + pyhbac.HBAC_RULE_ELEMENT_SERVICES, + pyhbac.HBAC_RULE_ELEMENT_TARGETHOSTS, + pyhbac.HBAC_RULE_ELEMENT_SOURCEHOSTS )) + + r.users.names = [ "someuser" ] + r.services.names = [ "ssh" ] + + valid, missing = r.validate() + self.assertEqual(valid, False) + self.assertItemsEqual(missing, ( pyhbac.HBAC_RULE_ELEMENT_TARGETHOSTS, + pyhbac.HBAC_RULE_ELEMENT_SOURCEHOSTS )) + + r.srchosts.names = [ "host1" ] + r.targethosts.names = [ "host2" ] + + valid, missing = r.validate() + self.assertEqual(valid, True) + +class PyHbacRequestElementTest(unittest.TestCase): + def testInstantiateEmpty(self): + el = pyhbac.HbacRequestElement() + self.assertItemsEqual(el.name, "") + self.assertItemsEqual(el.groups, []) + + def testInit(self): + name = "foo" + el = pyhbac.HbacRequestElement(name=name) + self.assertItemsEqual(el.name, name) + + groups = [ "abc", "def" ] + el = pyhbac.HbacRequestElement(groups=groups) + self.assertItemsEqual(el.groups, groups) + + def testGetSet(self): + name = "foo" + el = pyhbac.HbacRequestElement() + self.assertItemsEqual(el.name, "") + el.name = name + self.assertItemsEqual(el.name, name) + + groups = [ "abc", "def" ] + el = pyhbac.HbacRequestElement() + self.assertItemsEqual(el.groups, []) + el.groups = groups + self.assertItemsEqual(el.groups, groups) + + # Test other iterables than list + groups = ( "abc", "def" ) + el = pyhbac.HbacRequestElement() + self.assertItemsEqual(el.groups, []) + el.groups = groups + self.assertItemsEqual(el.groups, groups) + + def testGroupsNotIterable(self): + self.assertRaises(TypeError, pyhbac.HbacRequestElement, groups=None) + self.assertRaises(TypeError, pyhbac.HbacRequestElement, groups=123) + + def testRepr(self): + r = pyhbac.HbacRequestElement() + self.assertEqual(r.__repr__(), u"<name groups []>") + + r.name = 'foo' + r.groups = ['bar', 'baz'] + self.assertEqual(r.__repr__(), u"<name foo groups [bar,baz]>") + +class PyHbacRequestTest(unittest.TestCase): + def testRequestElementHandling(self): + name = "req_name" + groups = [ "g1", "g2" ] + + # The request should be empty after instantiation + req = pyhbac.HbacRequest() + self.assertIsInstance(req.user, pyhbac.HbacRequestElement) + self.assertIsInstance(req.service, pyhbac.HbacRequestElement) + self.assertIsInstance(req.targethost, pyhbac.HbacRequestElement) + self.assertIsInstance(req.srchost, pyhbac.HbacRequestElement) + + self.assertEqual(req.user.name, "") + self.assertIsInstance(req.user.groups, list) + self.assertItemsEqual(req.user.groups, []) + + # Assign by copying a HbacRequestElement + user_el = pyhbac.HbacRequestElement(name=name, groups=groups) + req = pyhbac.HbacRequest() + req.user = user_el + self.assertItemsEqual(req.user.name, name) + self.assertItemsEqual(req.user.groups, groups) + + # Assign directly + req = pyhbac.HbacRequest() + req.user.name = name + req.user.groups = groups + self.assertItemsEqual(req.user.name, name) + self.assertItemsEqual(req.user.groups, groups) + + def testRuleName(self): + req = pyhbac.HbacRequest() + self.assertEqual(req.rule_name, None) + # python 2.4 raises TypError, 2.7 raises AttributeError + self.assertRaises((TypeError, AttributeError), req.__setattr__, "rule_name", "foo") + + def testEvaluate(self): + name = "someuser" + service = "ssh" + srchost = "host1" + targethost = "host2" + + allow_rule = pyhbac.HbacRule("allowRule", enabled=True) + allow_rule.users.names = [ name ] + allow_rule.services.names = [ service ] + allow_rule.srchosts.names = [ srchost ] + allow_rule.targethosts.names = [ targethost ] + + req = pyhbac.HbacRequest() + req.user.name = name + req.service.name = service + req.srchost.name = srchost + req.targethost.name = targethost + + # Test that an allow rule on its own allows access + res = req.evaluate((allow_rule,)) + self.assertEqual(res, pyhbac.HBAC_EVAL_ALLOW) + self.assertEqual(req.rule_name, "allowRule") + + # Test that a user not in the rule is not allowed + savename = req.user.name + req.user.name = "someotheruser" + res = req.evaluate((allow_rule, )) + self.assertEqual(res, pyhbac.HBAC_EVAL_DENY) + self.assertEqual(req.rule_name, None) + + # But allows if the rule is an ALL rule + allow_rule.users.category.add(pyhbac.HBAC_CATEGORY_ALL) + res = req.evaluate((allow_rule, )) + self.assertEqual(res, pyhbac.HBAC_EVAL_ALLOW) + + def testRepr(self): + name = "someuser" + service = "ssh" + srchost = "host1" + targethost = "host2" + + req = pyhbac.HbacRequest() + + self.assertEqual(req.__repr__(), "<user <name groups []> " + "service <name groups []> " + "targethost <name groups []> " + "srchost <name groups []>>") + + req.user.name = name + req.service.name = service + req.srchost.name = srchost + req.targethost.name = targethost + + self.assertEqual(req.__repr__(), "<user <name %s groups []> " + "service <name %s groups []> " + "targethost <name %s groups []> " + "srchost <name %s groups []>>" % + (name, service, targethost, srchost)) + + def testEvaluateNegative(self): + name = "someuser" + service = "ssh" + srchost = "host1" + targethost = "host2" + + allow_rule = pyhbac.HbacRule("allowRule", enabled=True) + allow_rule.users.names = [ name ] + allow_rule.services.names = [ service ] + allow_rule.srchosts.names = [ srchost ] + allow_rule.targethosts.names = [ targethost ] + + req = pyhbac.HbacRequest() + req.service.name = service + req.srchost.name = srchost + req.targethost.name = targethost + req.user.name = name + + saveuser = req.user + req.user = None # need to catch this + + # catch invalid category value + savecat = copy.copy(allow_rule.users.category) + allow_rule.users.category.add(pyhbac.HBAC_EVAL_ERROR) + self.assertRaises(ValueError, req.evaluate, (allow_rule,)) + allow_rule.users.category = savecat + + # Test that invalid type is raised + self.assertRaises(TypeError, req.evaluate, (allow_rule,)) + + req.user = saveuser + allow_rule.users = None # need to catch this + self.assertRaises(TypeError, req.evaluate, (allow_rule,)) + + # catch invalid rule type + self.assertRaises(TypeError, req.evaluate, (allow_rule, None)) + +class PyHbacModuleTest(unittest.TestCase): + def testHasResultTypes(self): + assert hasattr(pyhbac, "HBAC_EVAL_ALLOW") + assert hasattr(pyhbac, "HBAC_EVAL_DENY") + assert hasattr(pyhbac, "HBAC_EVAL_ERROR") + + def testHasErrorTypes(self): + assert hasattr(pyhbac, "HBAC_ERROR_UNKNOWN") + assert hasattr(pyhbac, "HBAC_SUCCESS") + assert hasattr(pyhbac, "HBAC_ERROR_NOT_IMPLEMENTED") + assert hasattr(pyhbac, "HBAC_ERROR_OUT_OF_MEMORY") + assert hasattr(pyhbac, "HBAC_ERROR_UNPARSEABLE_RULE") + + def testHasCategories(self): + assert hasattr(pyhbac, "HBAC_CATEGORY_NULL") + assert hasattr(pyhbac, "HBAC_CATEGORY_ALL") + + def testHasRuleElementTypes(self): + assert hasattr(pyhbac, "HBAC_RULE_ELEMENT_USERS") + assert hasattr(pyhbac, "HBAC_RULE_ELEMENT_SERVICES") + assert hasattr(pyhbac, "HBAC_RULE_ELEMENT_TARGETHOSTS") + assert hasattr(pyhbac, "HBAC_RULE_ELEMENT_SOURCEHOSTS") + + def testHbacResultString(self): + results = [ pyhbac.HBAC_EVAL_ALLOW, pyhbac.HBAC_EVAL_DENY, + pyhbac.HBAC_EVAL_ERROR ] + for r in results: + s = pyhbac.hbac_result_string(r) + self.assertIsInstance(s, unicode) + assert len(s) > 0 + + def testHbacErrorString(self): + errors = [ pyhbac.HBAC_ERROR_UNKNOWN, + pyhbac.HBAC_SUCCESS, + pyhbac.HBAC_ERROR_NOT_IMPLEMENTED, + pyhbac.HBAC_ERROR_OUT_OF_MEMORY, + pyhbac.HBAC_ERROR_UNPARSEABLE_RULE ] + for e in errors: + s = pyhbac.hbac_error_string(e) + self.assertIsInstance(s, unicode) + assert len(s) > 0 + + +if __name__ == "__main__": + error = 0 + + suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacImport) + res = unittest.TextTestRunner().run(suite) + if not res.wasSuccessful(): + error |= 0x1 + # need to bail out here because pyhbac could not be imported + sys.exit(error) + + # import the pyhbac module into the global namespace, but make sure it's + # the one in tree + sys.path.insert(0, MODPATH) + import pyhbac + + suite = unittest.TestLoader().loadTestsFromTestCase(PyHbacRuleElementTest) + res = unittest.TextTestRunner().run(suite) + 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(PyHbacRequestElementTest) + 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(PyHbacModuleTest) + res = unittest.TextTestRunner().run(suite) + if not res.wasSuccessful(): + error |= 0x6 + + sys.exit(error) + |