From: JINMEI Tatuya Date: Thu, 24 May 2012 22:13:45 +0000 (-0700) Subject: [1458] first step for __check_update_acl: check ACL, and handle reject case. X-Git-Tag: trac2351_base~226^2~64^2^2^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=84ed05352881fca810292b827867dcd08f256cbe;p=thirdparty%2Fkea.git [1458] first step for __check_update_acl: check ACL, and handle reject case. --- diff --git a/src/lib/python/isc/ddns/libddns_messages.mes b/src/lib/python/isc/ddns/libddns_messages.mes index 4c48c4cf9a..568ec1f6bd 100644 --- a/src/lib/python/isc/ddns/libddns_messages.mes +++ b/src/lib/python/isc/ddns/libddns_messages.mes @@ -39,3 +39,6 @@ possible, you may want to check the implementation or configuration of those clients to suppress the requests. As specified in Section 3.1 of RFC2136, the receiving server will return a response with an RCODE of NOTAUTH. + +% LIBDDNS_UPDATE_REFUSED update client %1 for zone %2 denied +TBD diff --git a/src/lib/python/isc/ddns/session.py b/src/lib/python/isc/ddns/session.py index 36541d99c7..045702c5ab 100644 --- a/src/lib/python/isc/ddns/session.py +++ b/src/lib/python/isc/ddns/session.py @@ -18,6 +18,7 @@ import isc.ddns.zone_config from isc.log import * from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter from isc.log_messages.libddns_messages import * +from isc.acl.acl import ACCEPT, REJECT, DROP # Result codes for UpdateSession.handle() UPDATE_SUCCESS = 0 @@ -124,7 +125,7 @@ class UpdateSession: datasrc_client, zname, zclass = self.__get_update_zone() # conceptual code that would follow # self.__check_prerequisites() - # self.__check_update_acl() + self.__check_update_acl(zname, zclass) # self.__do_update() # self.__make_response(Rcode.NOERROR()) return UPDATE_SUCCESS, zname, zclass @@ -176,6 +177,17 @@ class UpdateSession: ZoneFormatter(zname, zclass)) raise UpdateError('notauth', zname, zclass, Rcode.NOTAUTH(), True) + def __check_update_acl(self, zname, zclass): + '''TBD''' + acl = self.__zone_config.get_update_acl(zname, zclass) + action = acl.execute(isc.acl.dns.RequestContext( + (self.__client_addr[0], self.__client_addr[1]))) + if action == REJECT: + logger.info(LIBDDNS_UPDATE_REFUSED, + ClientFormatter(self.__client_addr), + ZoneFormatter(zname, zclass)) + raise UpdateError('rejected', zname, zclass, Rcode.REFUSED(), True) + def __make_response(self, rcode): '''Transform the internal message to the update response. diff --git a/src/lib/python/isc/ddns/tests/session_tests.py b/src/lib/python/isc/ddns/tests/session_tests.py index 1bf4e40a4f..f9418a1eaa 100644 --- a/src/lib/python/isc/ddns/tests/session_tests.py +++ b/src/lib/python/isc/ddns/tests/session_tests.py @@ -53,18 +53,27 @@ def create_update_msg(zones=[TEST_ZONE_RECORD]): return renderer.get_data(), msg -class SessionTest(unittest.TestCase): - '''Session tests''' +class SesseionTestBase(unittest.TestCase): + '''Base class for all sesion related tests. + + It just initializes common test parameters in its setUp(). + + ''' def setUp(self): shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE) - self.__datasrc_client = DataSourceClient("sqlite3", - WRITE_ZONE_DB_CONFIG) - self.__update_msgdata, self.__update_msg = create_update_msg() - self.__session = UpdateSession(self.__update_msg, - self.__update_msgdata, TEST_CLIENT4, + self._datasrc_client = DataSourceClient("sqlite3", + WRITE_ZONE_DB_CONFIG) + self._update_msgdata, self._update_msg = create_update_msg() + acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS): + REQUEST_LOADER.load([{"action": "ACCEPT"}])} + self._session = UpdateSession(self._update_msg, + self._update_msgdata, TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS, - self.__datasrc_client)) + self._datasrc_client, + acl_map)) +class SessionTest(SesseionTestBase): + '''Basic session tests''' def check_response(self, msg, expected_rcode): '''Perform common checks on update resposne message.''' self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_QR)) @@ -79,7 +88,7 @@ class SessionTest(unittest.TestCase): def test_handle(self): '''Basic update case''' - result, zname, zclass = self.__session.handle() + result, zname, zclass = self._session.handle() self.assertEqual(UPDATE_SUCCESS, result) self.assertEqual(TEST_ZONE_NAME, zname) self.assertEqual(TEST_RRCLASS, zclass) @@ -123,7 +132,7 @@ class SessionTest(unittest.TestCase): session = UpdateSession(msg, msg_data, TEST_CLIENT4, ZoneConfig([(TEST_ZONE_NAME, TEST_RRCLASS)], TEST_RRCLASS, - self.__datasrc_client)) + self._datasrc_client)) self.assertEqual(UPDATE_ERROR, session.handle()[0]) self.check_response(session.get_message(), Rcode.NOTIMP()) @@ -134,7 +143,7 @@ class SessionTest(unittest.TestCase): session = UpdateSession(msg, msg_data, TEST_CLIENT4, ZoneConfig([(TEST_ZONE_NAME, TEST_RRCLASS)], TEST_RRCLASS, - self.__datasrc_client)) + self._datasrc_client)) self.assertEqual(UPDATE_ERROR, session.handle()[0]) self.check_response(session.get_message(), Rcode.NOTAUTH()) @@ -148,6 +157,24 @@ class SessionTest(unittest.TestCase): # zone class doesn't match self.check_notauth(Name('example.org'), RRClass.CH()) +class SessionACLTest(SesseionTestBase): + '''ACL related tests for update session.''' + def test_update_acl_check(self): + '''Test for various ACL checks. + + Note that accepted cases are covered in the basic tests. + + ''' + # create a separate session, with default (empty) ACL map. + session = UpdateSession(self._update_msg, self._update_msgdata, + TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS, + self._datasrc_client)) + # then the request should be rejected. + result, zname, zclass = session.handle() + self.assertEqual(UPDATE_ERROR, result) + self.assertEqual(None, zname) + self.assertEqual(None, zclass) + if __name__ == "__main__": isc.log.init("bind10") isc.log.resetUnitTestRootLogger() diff --git a/src/lib/python/isc/ddns/zone_config.py b/src/lib/python/isc/ddns/zone_config.py index 08504d1682..6fd7ea3472 100644 --- a/src/lib/python/isc/ddns/zone_config.py +++ b/src/lib/python/isc/ddns/zone_config.py @@ -34,7 +34,7 @@ class ZoneConfig: until the details are fixed. ''' - def __init__(self, secondaries, datasrc_class, datasrc_client): + def __init__(self, secondaries, datasrc_class, datasrc_client, acl_map={}): '''Constructor. Parameters: @@ -46,6 +46,11 @@ class ZoneConfig: - datasrc_client: isc.dns.DataSourceClient object. A data source class for the RR class of datasrc_class. It's expected to contain a zone that is eventually updated in the ddns package. + - acl_map: a dictionary that maps a tuple of + (isc.dns.Name, isc.dns.RRClass) to an isc.dns.dns.RequestACL + object. It defines an ACL to be applied to the zone defined + by the tuple. If unspecified, or the map is empty, the default + ACL will be applied to all zones, which is to reject any requests. ''' self.__secondaries = set() @@ -54,7 +59,7 @@ class ZoneConfig: self.__datasrc_class = datasrc_class self.__datasrc_client = datasrc_client self.__default_acl = REQUEST_LOADER.load([{"action": "REJECT"}]) - self.__acl_map = {} + self.__acl_map = acl_map def find_zone(self, zone_name, zone_class): '''Return the type and accessor client object for given zone.'''