mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
Merge branch 'work/authtsig'
This commit is contained in:
@@ -686,6 +686,7 @@ AC_CONFIG_FILES([Makefile
|
||||
src/bin/bindctl/tests/Makefile
|
||||
src/bin/cfgmgr/Makefile
|
||||
src/bin/cfgmgr/plugins/Makefile
|
||||
src/bin/cfgmgr/plugins/tests/Makefile
|
||||
src/bin/cfgmgr/tests/Makefile
|
||||
src/bin/host/Makefile
|
||||
src/bin/loadzone/Makefile
|
||||
|
@@ -18,6 +18,7 @@
|
||||
import sys; sys.path.append ('@@PYTHONPATH@@')
|
||||
|
||||
from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError
|
||||
import bind10_config
|
||||
from isc.cc import SessionError
|
||||
import isc.util.process
|
||||
import signal
|
||||
@@ -28,24 +29,10 @@ import os.path
|
||||
|
||||
isc.util.process.rename()
|
||||
|
||||
# If B10_FROM_SOURCE is set in the environment, we use data files
|
||||
# from a directory relative to the value of that variable, or, if defined,
|
||||
# relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR. Otherwise
|
||||
# we use the ones installed on the system.
|
||||
# B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for
|
||||
# tests where we want to use variuos types of configuration within the test
|
||||
# environment. (We may want to make it even more generic so that the path is
|
||||
# passed from the boss process)
|
||||
if "B10_FROM_SOURCE" in os.environ:
|
||||
if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
|
||||
DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
|
||||
else:
|
||||
DATA_PATH = os.environ["B10_FROM_SOURCE"]
|
||||
PLUGIN_PATHS = [DATA_PATH + '/src/bin/cfgmgr/plugins']
|
||||
else:
|
||||
PREFIX = "@prefix@"
|
||||
DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
|
||||
PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"]
|
||||
# Import some paths from our configuration
|
||||
DATA_PATH = bind10_config.DATA_PATH
|
||||
PLUGIN_PATHS = bind10_config.PLUGIN_PATHS
|
||||
PREFIX = bind10_config.PREFIX
|
||||
DEFAULT_CONFIG_FILE = "b10-config.db"
|
||||
|
||||
cm = None
|
||||
|
@@ -1 +1,5 @@
|
||||
EXTRA_DIST = README
|
||||
SUBDIRS = tests
|
||||
EXTRA_DIST = README tsig_keys.py tsig_keys.spec
|
||||
|
||||
config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
|
||||
config_plugin_DATA = tsig_keys.py tsig_keys.spec
|
||||
|
19
src/bin/cfgmgr/plugins/tests/Makefile.am
Normal file
19
src/bin/cfgmgr/plugins/tests/Makefile.am
Normal file
@@ -0,0 +1,19 @@
|
||||
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
|
||||
PYTESTS = tsig_keys_test.py
|
||||
|
||||
EXTRA_DIST = $(PYTESTS)
|
||||
|
||||
# test using command-line arguments, so use check-local target instead of TESTS
|
||||
check-local:
|
||||
if ENABLE_PYTHON_COVERAGE
|
||||
touch $(abs_top_srcdir)/.coverage
|
||||
rm -f .coverage
|
||||
${LN_S} $(abs_top_srcdir)/.coverage .coverage
|
||||
endif
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
|
||||
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
|
||||
done
|
||||
|
103
src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
Normal file
103
src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# Copyright (C) 2011 Internet Systems Consortium.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Make sure we can load the module, put it into path
|
||||
import sys
|
||||
import os
|
||||
sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
|
||||
|
||||
import tsig_keys
|
||||
import unittest
|
||||
import isc.config.module_spec
|
||||
|
||||
class TSigKeysTest(unittest.TestCase):
|
||||
def test_load(self):
|
||||
"""
|
||||
Checks the entry point returns the correct values.
|
||||
"""
|
||||
(spec, check) = tsig_keys.load()
|
||||
# It returns the checking function
|
||||
self.assertEqual(check, tsig_keys.check)
|
||||
# The plugin stores it's spec
|
||||
self.assertEqual(spec, tsig_keys.spec)
|
||||
|
||||
def test_spec(self):
|
||||
"""
|
||||
Checks the spec is looking sane (doesn't do really deep check here).
|
||||
"""
|
||||
spec = tsig_keys.spec
|
||||
# In python, we don't generally check the type of something, because
|
||||
# of the duck typing.
|
||||
# But this is unittest, so we check it does what we intend and
|
||||
# supplying that's behaving the same but is different is not our
|
||||
# intention
|
||||
self.assertTrue(isinstance(spec, isc.config.module_spec.ModuleSpec))
|
||||
# Correct name
|
||||
self.assertEqual("tsig_keys", spec.get_module_name())
|
||||
# There are no commands, nobody would handle them anyway
|
||||
self.assertEqual([], spec.get_commands_spec())
|
||||
# There's some nonempty configuration
|
||||
self.assertNotEqual({}, spec.get_config_spec())
|
||||
|
||||
def test_missing_keys(self):
|
||||
"""
|
||||
Test that missing keys doesn't kill us. There are just no keys there.
|
||||
"""
|
||||
self.assertEqual(None, tsig_keys.check({}))
|
||||
|
||||
def test_data_empty(self):
|
||||
"""Check we accept valid config with empty set of tsig keys."""
|
||||
self.assertEqual(None, tsig_keys.check({'keys': []}))
|
||||
|
||||
def test_keys_valid(self):
|
||||
"""
|
||||
Check we accept some valid keys (we don't check all the algorithms,
|
||||
that's the job of isc.dns.TSIGKey).
|
||||
"""
|
||||
self.assertEqual(None, tsig_keys.check({'keys':
|
||||
['testkey:QklORCAxMCBpcyBjb29sCg==',
|
||||
'test.key:QklORCAxMCBpcyBjb29sCg==:hmac-sha1']}))
|
||||
|
||||
def test_keys_same_name(self):
|
||||
"""
|
||||
Test we reject when we have multiple keys with the same name.
|
||||
"""
|
||||
self.assertEqual("Multiple TSIG keys with name 'test.key.'",
|
||||
tsig_keys.check({'keys':
|
||||
['test.key:QklORCAxMCBpcyBjb29sCg==',
|
||||
'test.key:b3RoZXIK']}))
|
||||
|
||||
def test_invalid_key(self):
|
||||
"""
|
||||
Test we reject invalid key.
|
||||
"""
|
||||
self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
|
||||
tsig_keys.check({'keys': ['invalid.key']}))
|
||||
self.assertEqual(
|
||||
"TSIG: attempt to decode a value not in base64 char set",
|
||||
tsig_keys.check({'keys': ['invalid.key:123']}))
|
||||
|
||||
def test_bad_format(self):
|
||||
"""
|
||||
Test we fail on bad format. We don't really care much how here, though,
|
||||
as this should not get in trough config manager anyway.
|
||||
"""
|
||||
self.assertNotEqual(None, tsig_keys.check({'bad_name': {}}))
|
||||
self.assertNotEqual(None, tsig_keys.check({'keys': 'not_list'}))
|
||||
self.assertNotEqual(None, tsig_keys.check({'keys': 42}))
|
||||
self.assertNotEqual(None, tsig_keys.check({'keys': {}}))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
50
src/bin/cfgmgr/plugins/tsig_keys.py
Normal file
50
src/bin/cfgmgr/plugins/tsig_keys.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2011 Internet Systems Consortium.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# This is the plugin for tsig_keys configuration section. The TSIG keyring
|
||||
# lives there (eg. all the shared secrets, with some exceptions where there
|
||||
# are some TSIG keys elsewhere, but these should be removed soon). We do
|
||||
# sanity checking of user configuration here, simply by trying to construct
|
||||
# all the keys here.
|
||||
|
||||
from isc.config.module_spec import module_spec_from_file
|
||||
from isc.util.file import path_search
|
||||
from pydnspp import TSIGKey, InvalidParameter
|
||||
from bind10_config import PLUGIN_PATHS
|
||||
spec = module_spec_from_file(path_search('tsig_keys.spec', PLUGIN_PATHS))
|
||||
|
||||
def check(config):
|
||||
# Check the data layout first
|
||||
errors=[]
|
||||
if not spec.validate_config(False, config, errors):
|
||||
return ' '.join(errors)
|
||||
# Get the list of keys, if any
|
||||
keys = config.get('keys', [])
|
||||
# Run through them, check they can be constructed and there are no
|
||||
# duplicates
|
||||
keyNames = set()
|
||||
for key in keys:
|
||||
try:
|
||||
name = str(TSIGKey(key).get_key_name())
|
||||
except InvalidParameter as e:
|
||||
return "TSIG: " + str(e)
|
||||
if name in keyNames:
|
||||
return "Multiple TSIG keys with name '" + name + "'"
|
||||
keyNames.add(name)
|
||||
# No error found, so let's assume it's OK
|
||||
return None
|
||||
|
||||
def load():
|
||||
return (spec, check)
|
21
src/bin/cfgmgr/plugins/tsig_keys.spec
Normal file
21
src/bin/cfgmgr/plugins/tsig_keys.spec
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"module_spec": {
|
||||
"module_name": "tsig_keys",
|
||||
"module_description": "The TSIG keyring is stored here",
|
||||
"config_data": [
|
||||
{
|
||||
"item_name": "keys",
|
||||
"item_type": "list",
|
||||
"item_optional": false,
|
||||
"item_default": [],
|
||||
"list_item_spec": {
|
||||
"item_name": "key",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": []
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import bind10_config
|
||||
from isc.testutils.parse_args import OptsError, TestOptParser
|
||||
|
||||
class MyConfigManager:
|
||||
@@ -110,6 +111,7 @@ class TestConfigManagerStartup(unittest.TestCase):
|
||||
env_var = os.environ["B10_FROM_SOURCE"]
|
||||
|
||||
os.environ["B10_FROM_SOURCE"] = tmp_env_var
|
||||
bind10_config.reload()
|
||||
b = __import__("b10-cfgmgr", globals(), locals())
|
||||
b.PLUGIN_PATH = [] # It's enough to test plugins in one test
|
||||
b.ConfigManager = MyConfigManager
|
||||
@@ -117,6 +119,7 @@ class TestConfigManagerStartup(unittest.TestCase):
|
||||
|
||||
if env_var != None:
|
||||
os.environ["B10_FROM_SOURCE"] = env_var
|
||||
bind10_config.reload()
|
||||
|
||||
sys.modules.pop("b10-cfgmgr")
|
||||
|
||||
|
@@ -17,7 +17,37 @@
|
||||
# variables to python scripts and libraries.
|
||||
import os
|
||||
|
||||
BIND10_MSGQ_SOCKET_FILE = os.path.join("@localstatedir@",
|
||||
"@PACKAGE_NAME@",
|
||||
"msgq_socket").replace("${prefix}",
|
||||
"@prefix@")
|
||||
def reload():
|
||||
# In a function, for testing purposes
|
||||
global BIND10_MSGQ_SOCKET_FILE
|
||||
global DATA_PATH
|
||||
global PLUGIN_PATHS
|
||||
global PREFIX
|
||||
BIND10_MSGQ_SOCKET_FILE = os.path.join("@localstatedir@",
|
||||
"@PACKAGE_NAME@",
|
||||
"msgq_socket").replace("${prefix}",
|
||||
"@prefix@")
|
||||
|
||||
# If B10_FROM_SOURCE is set in the environment, we use data files
|
||||
# from a directory relative to the value of that variable, or, if defined,
|
||||
# relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR. Otherwise
|
||||
# we use the ones installed on the system.
|
||||
# B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for
|
||||
# tests where we want to use variuos types of configuration within the test
|
||||
# environment. (We may want to make it even more generic so that the path is
|
||||
# passed from the boss process)
|
||||
if "B10_FROM_SOURCE" in os.environ:
|
||||
if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
|
||||
DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
|
||||
else:
|
||||
DATA_PATH = os.environ["B10_FROM_SOURCE"]
|
||||
PLUGIN_PATHS = [DATA_PATH + '/src/bin/cfgmgr/plugins']
|
||||
else:
|
||||
PREFIX = "@prefix@"
|
||||
DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
|
||||
PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"]
|
||||
# For testing the plugins so they can find their own spec files
|
||||
if "B10_TEST_PLUGIN_DIR" in os.environ:
|
||||
PLUGIN_PATHS = os.environ["B10_TEST_PLUGIN_DIR"].split(':')
|
||||
|
||||
reload()
|
||||
|
@@ -1,5 +1,5 @@
|
||||
SUBDIRS = . tests
|
||||
|
||||
python_PYTHON = __init__.py process.py socketserver_mixin.py
|
||||
python_PYTHON = __init__.py process.py socketserver_mixin.py file.py
|
||||
|
||||
pythondir = $(pyexecdir)/isc/util
|
||||
|
29
src/lib/python/isc/util/file.py
Normal file
29
src/lib/python/isc/util/file.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2011 Internet Systems Consortium.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Various functions for working with files and directories."""
|
||||
|
||||
from os.path import exists, join
|
||||
|
||||
def path_search(filename, paths):
|
||||
"""
|
||||
Searches list of paths to find filename in one of them. The found one will
|
||||
be returned or IOError will be returned if it isn't found.
|
||||
"""
|
||||
for p in paths:
|
||||
f = join(p, filename)
|
||||
if exists(f):
|
||||
return f
|
||||
raise IOError("'" + filename + "' not found in " + str(paths))
|
@@ -1,5 +1,5 @@
|
||||
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
|
||||
PYTESTS = process_test.py socketserver_mixin_test.py
|
||||
PYTESTS = process_test.py socketserver_mixin_test.py file_test.py
|
||||
EXTRA_DIST = $(PYTESTS)
|
||||
|
||||
# test using command-line arguments, so use check-local target instead of TESTS
|
||||
|
32
src/lib/python/isc/util/tests/file_test.py
Normal file
32
src/lib/python/isc/util/tests/file_test.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2011 Internet Systems Consortium.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import isc.util.file
|
||||
import unittest
|
||||
|
||||
class FileTest(unittest.TestCase):
|
||||
def test_search_path_find(self):
|
||||
"""Test it returns the first occurence of the file"""
|
||||
self.assertEqual('./Makefile',
|
||||
isc.util.file.path_search('Makefile',
|
||||
['/no/such/directory/', '.',
|
||||
'../tests/']))
|
||||
|
||||
def test_search_path_notfound(self):
|
||||
"""Test it throws an exception when the file can't be found"""
|
||||
self.assertRaises(IOError, isc.util.file.path_search, 'no file', ['/no/such/directory'])
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Reference in New Issue
Block a user