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
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
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.
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))
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)
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())
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())
# 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()
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:
- 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()
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.'''