| 
 # Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")  
#  
# Permission to use, copy, modify, and distribute this software for any  
# purpose with or without fee is hereby granted, provided that the above  
# copyright notice and this permission notice appear in all copies.  
#  
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM  
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL  
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,  
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING  
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,  
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  
  
"""  
Tests for isc.server_common.tsig_keyring.  
"""  
  
import unittest  
import isc.log  
from isc.server_common.tsig_keyring import *  
import isc.dns  
from isc.testutils.ccsession_mock import MockModuleCCSession  
  
class Session(MockModuleCCSession):  
    """  
    A class pretending to be the config session.  
    """  
    def __init__(self):  
        MockModuleCCSession.__init__(self)  
        self._name = None  
        self._callback = None  
        self._remove_name = None  
        self._data = None  
  
    def add_remote_config_by_name(self, name, callback):  
        self._name = name  
        self._callback = callback  
  
    def remove_remote_config(self, name):  
        self._remove_name = name  
  
    def get_remote_config_value(self, module, name):  
46        if module != 'tsig_keys' or name != 'keys':  
            raise Exception("Asked for bad data element")  
        return (self._data, False)  
  
class TSIGKeyRingTest(unittest.TestCase):  
    """  
    Tests for the isc.server_common.tsig_keyring module.  
    """  
    def setUp(self):  
        self.__session = Session()  
        self.__sha1name = isc.dns.Name('hmac-sha1')  
        self.__md5name = isc.dns.Name('hmac-md5.sig-alg.reg.int')  
  
    def tearDown(self):  
        deinit_keyring()  
  
    def __do_init(self):  
        init_keyring(self.__session)  
        # Some initialization happened  
        self.assertEqual('tsig_keys', self.__session._name)  
  
    def test_initialization(self):  
        """  
        Test we can initialize and deintialize the keyring. It also  
        tests the interaction with the keyring() function.  
        """  
        # The keyring function raises until initialized  
        self.assertRaises(Unexpected, get_keyring)  
        self.__do_init()  
        current_keyring = get_keyring()  
        self.assertTrue(isinstance(current_keyring, isc.dns.TSIGKeyRing))  
        # Another initialization does nothing  
        self.__do_init()  
        self.assertEqual(current_keyring, get_keyring())  
        # When we deinitialize it, it no longer provides the keyring  
        deinit_keyring()  
        self.assertEqual('tsig_keys', self.__session._remove_name)  
        self.__session._remove_name = None  
        self.assertRaises(Unexpected, get_keyring)  
        # Another deinitialization doesn't change anything  
        deinit_keyring()  
        self.assertRaises(Unexpected, get_keyring)  
        self.assertIsNone(self.__session._remove_name)  
        # Test we can init it again (not expected, but not forbidden)  
        self.__do_init()  
        self.assertTrue(isinstance(get_keyring(), isc.dns.TSIGKeyRing))  
  
    def test_load(self):  
        """  
        Test it can load the keys from the configuration and reload them  
        when the data change.  
        """  
        # Initial load  
        self.__session._data = ['key:MTIzNAo=:hmac-sha1']  
        self.__do_init()  
        keys = get_keyring()  
        self.assertEqual(1, keys.size())  
        (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)  
        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)  
        self.assertEqual(isc.dns.Name('key'), key.get_key_name())  
        # There's a change in the configuration  
        # (The key has a different name)  
        self.__session._data = ['key.example:MTIzNAo=:hmac-sha1']  
        self.__session._callback()  
        orig_keys = keys  
        keys = get_keyring()  
        self.assertNotEqual(keys, orig_keys)  
        self.assertEqual(1, keys.size())  
        # The old key is not here  
        (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)  
        self.assertEqual(isc.dns.TSIGKeyRing.NOTFOUND, rcode)  
        self.assertIsNone(key)  
        # But the new one is  
        (rcode, key) = keys.find(isc.dns.Name('key.example'), self.__sha1name)  
        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)  
        self.assertEqual(isc.dns.Name('key.example'), key.get_key_name())  
  
    def test_empty_update(self):  
        """  
        Test an update that doesn't carry the correct element doesn't change  
        anything.  
        """  
        self.__session._data = ['key:MTIzNAo=:hmac-sha1']  
        self.__do_init()  
        keys = get_keyring()  
        self.__session._data = None  
        self.__session._callback()  
        self.assertEqual(keys, get_keyring())  
  
    def test_no_keys_update(self):  
        """  
        Test we can update the keyring to be empty.  
        """  
        self.__session._data = ['key:MTIzNAo=:hmac-sha1']  
        self.__do_init()  
        keys = get_keyring()  
        self.assertEqual(1, keys.size())  
        self.__session._data = []  
        self.__session._callback()  
        keys = get_keyring()  
        self.assertEqual(0, keys.size())  
  
    def test_update_multi(self):  
        """  
        Test we can handle multiple keys in startup/update.  
        """  
        # Init  
        self.__session._data = ['key:MTIzNAo=:hmac-sha1', 'key2:MTIzNAo=']  
        self.__do_init()  
        keys = get_keyring()  
        self.assertEqual(2, keys.size())  
        (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)  
        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)  
        self.assertEqual(isc.dns.Name('key'), key.get_key_name())  
        (rcode, key) = keys.find(isc.dns.Name('key2'), self.__md5name)  
        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)  
        self.assertEqual(isc.dns.Name('key2'), key.get_key_name())  
        # Update  
        self.__session._data = ['key1:MTIzNAo=:hmac-sha1', 'key3:MTIzNAo=']  
        self.__session._callback()  
        keys = get_keyring()  
        self.assertEqual(2, keys.size())  
        (rcode, key) = keys.find(isc.dns.Name('key1'), self.__sha1name)  
        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)  
        self.assertEqual(isc.dns.Name('key1'), key.get_key_name())  
        (rcode, key) = keys.find(isc.dns.Name('key3'), self.__md5name)  
        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)  
        self.assertEqual(isc.dns.Name('key3'), key.get_key_name())  
  
    def test_update_bad(self):  
        """  
        Test it raises on bad updates and doesn't change anything.  
        """  
        self.__session._data = ['key:MTIzNAo=:hmac-sha1']  
        self.__do_init()  
        keys = get_keyring()  
        # Bad TSIG string  
        self.__session._data = ['key:this makes no sense:really']  
        self.assertRaises(isc.dns.InvalidParameter, self.__session._callback)  
        self.assertEqual(keys, get_keyring())  
        # A duplicity  
        self.__session._data = ['key:MTIzNAo=:hmac-sha1', 'key:MTIzNAo=:hmac-sha1']  
        self.assertRaises(AddError, self.__session._callback)  
        self.assertEqual(keys, get_keyring())  
  
exitif __name__ == "__main__":  
    isc.log.init("bind10") # FIXME Should this be needed?  
    isc.log.resetUnitTestRootLogger()  
    unittest.main()  
                
             |