From: Jorge Pereira Date: Thu, 27 May 2021 04:48:12 +0000 (-0300) Subject: Add tests for clients loaded from SQL 'nas' table X-Git-Tag: release_3_0_23~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=626dad1ae8b65bfdd1559b1a512aa6ba47cbecf7;p=thirdparty%2Ffreeradius-server.git Add tests for clients loaded from SQL 'nas' table --- diff --git a/Makefile b/Makefile index f4be9e18466..5ec0ab4fde9 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,7 @@ $(BUILD_DIR)/tests/radiusd-c: raddb/test.conf ${BUILD_DIR}/bin/radiusd | build.r @echo "ok" @touch $@ -test: ${BUILD_DIR}/bin/radiusd ${BUILD_DIR}/bin/radclient tests.unit tests.xlat tests.keywords tests.auth $(BUILD_DIR)/tests/radiusd-c | build.raddb +test: ${BUILD_DIR}/bin/radiusd ${BUILD_DIR}/bin/radclient tests.unit tests.xlat tests.keywords tests.auth test.sql_nas_table $(BUILD_DIR)/tests/radiusd-c | build.raddb @$(MAKE) -C src/tests tests # diff --git a/src/tests/all.mk b/src/tests/all.mk index d87a22e0820..142772b35cd 100644 --- a/src/tests/all.mk +++ b/src/tests/all.mk @@ -1,4 +1,7 @@ -SUBMAKEFILES := rbmonkey.mk unit/all.mk map/all.mk xlat/all.mk keywords/all.mk auth/all.mk modules/all.mk +SUBMAKEFILES := rbmonkey.mk unit/all.mk map/all.mk xlat/all.mk keywords/all.mk auth/all.mk modules/all.mk sql_nas_table/all.mk +PORT := 12340 +SECRET := testing123 +DICT_PATH := $(top_srcdir)/share # # Include all of the autoconf definitions into the Make variable space @@ -10,3 +13,66 @@ SUBMAKEFILES := rbmonkey.mk unit/all.mk map/all.mk xlat/all.mk keywords/all.mk a # $(BUILD_DIR)/tests/keywords/autoconf.h.mk: src/include/autoconf.h @grep '^#define' $^ | sed 's/#define /AC_/;s/ / := /' > $@ + +###################################################################### +# +# Generic rules to set up the tests +# +# Use $(eval $(call TEST_BOOTSTRAP)) +# +###################################################################### +define TEST_BOOTSTRAP + +# +# The test files are files without extensions. +# +OUTPUT.$(TEST) := $(patsubst %/,%,$(subst $(top_srcdir)/src,$(BUILD_DIR),$(abspath $(DIR)))) +OUTPUT := $$(OUTPUT.$(TEST)) + +# +# Create the output directory +# +$$(OUTPUT.$(TEST)): + $${Q}mkdir -p $$@ + +# +# All of the output files depend on the input files +# +FILES.$(TEST) := $(addprefix $$(OUTPUT.$(TEST))/,$(sort $(FILES))) + +# +# The output files also depend on the directory +# and on the previous test. +# +$$(FILES.$(TEST)): | $$(OUTPUT.$(TEST)) + +# +# Make sure that the output files depend on the input. +# This way if the input file doesn't exist, we get a +# build error. Without this rule, the test target +# would just get re-built every time, no matter what. +# +$(foreach x, $(FILES), $(eval $$(OUTPUT.$(TEST))/$x: $(DIR)/$x)) + +# +# We have a real file that's created if all of the tests pass. +# +$(BUILD_DIR)/tests/$(TEST): $$(FILES.$(TEST)) + $${Q}touch $$@ + +# +# For simplicity, we create a phony target so that the poor developer +# doesn't need to remember path names +# +$(TEST): $(BUILD_DIR)/tests/$(TEST) + +# +# Clean the output directory and files. +# +.PHONY: clean.$(TEST) +clean.$(TEST): + $${Q}rm -rf $$(OUTPUT.$(TEST)) + $${Q}rm -f $$(BUILD_DIR)/tests/$(TEST) + +clean.test: clean.$(TEST) +endef diff --git a/src/tests/radiusd.mk b/src/tests/radiusd.mk new file mode 100644 index 00000000000..85eb4eb4972 --- /dev/null +++ b/src/tests/radiusd.mk @@ -0,0 +1,115 @@ +# +# The "RADIUSD_SERVICE" macro is charged to start/stop the radiusd instances +# from the mostly test targets. It expects the below variables. +# +# - Already defined by scripts/boiler.mk +# +# DIR = src/tests/$target +# BUILD_DIR = build/ +# +# - Defined by the target +# +# PORT := Run the service +# TEST := test.$target +# +# - Parameter +# +# ${1} config-name found in $(DIR)/config, e.g: src/tests/$target/config/${config-name}.conf +# ${2} output directory +# +# - How to use +# +# 1. $(eval $(call RADIUSD_SERVICE,myconfig,directory/path/)) +# +# 2. It will defined the targets. +# +# $(TEST).radiusd_kill and $(TEST).radiusd_start +# +# 3. The target 'radiusd_start' define the variable $(RADIUSD_RUN) with the +# exactly command used to start the service. +# +# 4. You could use the 'RADIUSD_BIN' to set such path to the "radiusd" binary +# that you want to against the tests. +# +# e.g: +# +# make RADIUSD_BIN=/path/to/my/radiusd test +# +include Make.inc + +define RADIUSD_SERVICE +$$(eval RADIUSD_BIN := $(JLIBTOOL) --silent --mode=execute $$(TESTBIN)/radiusd) + +# +# Kill it. We don't care if it failed or not. However, we do care +# if we can't kill it. +# +.PHONY: $(TEST).radiusd_kill +$(TEST).radiusd_kill: | ${2} + ${Q}if [ -f ${2}/radiusd.pid ]; then \ + if ! ps `cat ${2}/radiusd.pid` >/dev/null 2>&1; then \ + rm -f ${2}/radiusd.pid; \ + echo "FreeRADIUS terminated during test called by $(TEST).radiusd_kill"; \ + echo "GDB output was:"; \ + cat "${2}/gdb.log" 2> /dev/null; \ + echo "--------------------------------------------------"; \ + echo "Last entries in server log (${2}/radiusd.log):"; \ + tail -n 100 "${2}/radiusd.log" 2> /dev/null; \ + exit 0; \ + fi; \ + if ! kill -9 `cat ${2}/radiusd.pid` >/dev/null 2>&1; then \ + exit 1; \ + fi; \ + rm -f ${2}/radiusd.pid; \ + exit 0; \ + fi + +# +# Stop it politely. +# +.PHONY: $(TEST).radiusd_stop +$(TEST).radiusd_stop: | ${2} + ${Q}if [ -f ${2}/radiusd.pid ]; then \ + if ! ps `cat ${2}/radiusd.pid` >/dev/null 2>&1; then \ + rm -f ${2}/radiusd.pid; \ + echo "FreeRADIUS terminated during test called by $(TEST).radiusd_kill"; \ + echo "GDB output was:"; \ + cat "${2}/gdb.log" 2> /dev/null; \ + echo "--------------------------------------------------"; \ + echo "Last entries in server log (${2}/radiusd.log):"; \ + tail -n 100 "${2}/radiusd.log" 2> /dev/null; \ + exit 1; \ + fi; \ + if ! kill -TERM `cat ${2}/radiusd.pid` >/dev/null 2>&1; then \ + exit 1; \ + fi; \ + rm -f ${2}/radiusd.pid; \ + exit 0; \ + fi + +# +# Start radiusd instance +# +${2}/radiusd.pid: ${2} + $$(eval RADIUSD_RUN := TOP_SRCDIR=$(top_srcdir) TESTDIR=$(DIR) OUTPUT=$(OUTPUT) TEST_PORT=$(PORT) $$(RADIUSD_BIN) -Pxxx -d $(DIR)/config -n ${1} -D $(DICT_PATH) -l ${2}/radiusd.log) + ${Q}rm -f ${2}/radiusd.log + ${Q}if ! $$(RADIUSD_RUN); then \ + echo "FAILED STARTING RADIUSD"; \ + grep 'Error :' "${2}/radiusd.log"; \ + echo "Last entries in server log (${2}/radiusd.log):"; \ + tail -n 100 "${2}/radiusd.log" 2> /dev/null; \ + echo "RADIUSD_RUN: $$(RADIUSD_RUN)"; \ + fi + +.PHONY: $(TEST).radiusd_start +$(TEST).radiusd_start: ${2}/radiusd.pid + +# +# If this test framework needs radiusd to be started / stopped, then ensure that +# the output files depend on the radiusd binary. +# +ifneq "$(FILES.$(TEST))" "" +$(foreach x, $(FILES.$(TEST)), $(eval $x: $(TESTBINDIR)/radiusd $(TESTBINDIR)/$(CLIENT) $(top_srcdir)/src/tests/$(subst test.,,$(TEST))/config/${1}.conf)) +endif + +endef diff --git a/src/tests/sql_nas_table/all.mk b/src/tests/sql_nas_table/all.mk new file mode 100644 index 00000000000..98b290ceffd --- /dev/null +++ b/src/tests/sql_nas_table/all.mk @@ -0,0 +1,57 @@ +# +# Unit tests validating the SQL 'nas' table clients +# + +# +# Test name +# +TEST := test.sql_nas_table +FILES := $(subst $(DIR)/,,$(wildcard $(DIR)/*.txt)) + +$(eval $(call TEST_BOOTSTRAP)) + +# +# Config settings +# +SQL_NASTABLE_BUILD_DIR := $(BUILD_DIR)/tests/sql_nas_table +SQL_NASTABLE_RADIUS_LOG := $(SQL_NASTABLE_BUILD_DIR)/radiusd.log +SQL_NASTABLE_GDB_LOG := $(SQL_NASTABLE_BUILD_DIR)/gdb.log +SQL_NASTABLE_DB := $(SQL_NASTABLE_BUILD_DIR)/sql_nas_table.db + +# Used by src/tests/sql_nas_table/config/radiusd.conf +export SQL_NASTABLE_DB + +# +# Generic rules to start / stop the radius service. +# +include src/tests/radiusd.mk +$(eval $(call RADIUSD_SERVICE,radiusd,$(OUTPUT))) + +.PHONY: sql_nas_table_bootstrap +sql_nas_table_bootstrap: + $(Q)rm -f $(SQL_NASTABLE_DB) + $(Q)mkdir -p $(SQL_NASTABLE_BUILD_DIR) + $(Q)sqlite3 $(SQL_NASTABLE_DB) < ./raddb/mods-config/sql/main/sqlite/schema.sql + $(Q)sqlite3 $(SQL_NASTABLE_DB) < ./src/tests/sql_nas_table/clients.sql + +# +# Run the radclient commands against the radiusd. +# +$(OUTPUT)/%: $(DIR)/% | $(TEST).radiusd_kill sql_nas_table_bootstrap $(TEST).radiusd_start + $(Q)echo "SQL_NASTABLE-TEST" + $(Q)mkdir -p $(dir $@) + $(Q)[ -f $(dir $@)/radiusd.pid ] || exit 1 + $(Q)if ! $(TESTBIN)/radclient $(ARGV) -f src/tests/sql_nas_table/auth.txt -D share/ 127.0.0.1:$(PORT) auth $(SECRET) 1> /dev/null 2>&1; then \ + echo "FAILED"; \ + rm -f $(BUILD_DIR)/tests/test.sql_nas_table; \ + $(MAKE) --no-print-directory test.sql_nas_table.radiusd_kill; \ + echo "RADIUSD: $(RADIUSD_RUN)"; \ + echo "SQL_NASTABLE: $(TESTBIN)/radclient $(ARGV) -f $< -xF -d src/tests/sql_nas_table/config -D share/ 127.0.0.1:$(PORT) auth $(SECRET)"; \ + exit 1; \ + fi + + $(Q)touch $@ + +$(TEST): + $(Q)$(MAKE) --no-print-directory $@.radiusd_stop + @touch $(BUILD_DIR)/tests/$@ diff --git a/src/tests/sql_nas_table/auth.txt b/src/tests/sql_nas_table/auth.txt new file mode 100644 index 00000000000..c1b0a1da8e9 --- /dev/null +++ b/src/tests/sql_nas_table/auth.txt @@ -0,0 +1,2 @@ +User-Name = bob +Cleartext-Password = hello diff --git a/src/tests/sql_nas_table/clients.sql b/src/tests/sql_nas_table/clients.sql new file mode 100644 index 00000000000..d631b7f12ed --- /dev/null +++ b/src/tests/sql_nas_table/clients.sql @@ -0,0 +1 @@ +INSERT INTO nas (nasname,shortname,type,ports,secret,server,community,description) VALUES ('127.0.0.1', 'test', 'test', '123', 'testing123', 'extra', '', 'RADIUS Client'); diff --git a/src/tests/sql_nas_table/config/radiusd.conf b/src/tests/sql_nas_table/config/radiusd.conf new file mode 100644 index 00000000000..16513bbc9a7 --- /dev/null +++ b/src/tests/sql_nas_table/config/radiusd.conf @@ -0,0 +1,143 @@ +# -*- text -*- +# +# test configuration file. Do not install. +# +# $Id$ +# + +# +# Minimal radiusd.conf for testing +# +top_srcdir = $ENV{TOP_SRCDIR} +testdir = $ENV{TESTDIR} +output = ${top_srcdir}/$ENV{OUTPUT} +run_dir = ${output} +raddb = raddb +pidfile = ${run_dir}/radiusd.pid +panic_action = "gdb -batch -x src/tests/panic.gdb %e %p > ${run_dir}/gdb.log 2>&1; cat ${run_dir}/gdb.log" + +maindir = ${raddb} +radacctdir = ${run_dir}/radacct +modconfdir = ${maindir}/mods-config +certdir = ${maindir}/certs +cadir = ${maindir}/certs +test_port = $ENV{TEST_PORT} + +client docnet { + ipaddr = 192.0.2.1 + secret = testing123123 +} + +# Only for testing! +# Setting this on a production system is a BAD IDEA. +security { + allow_vulnerable_openssl = yes +} + +policy { + files.authorize { + if (&User-Name == "bob") { + update control { + &Password.Cleartext := "hello" + } + } + } + $INCLUDE ${maindir}/policy.d/ +} + +modules { + expr { + + } + + sql { + driver = "rlm_sql_sqlite" + dialect = "sqlite" + sqlite { + # Path to the sqlite database + filename = "$ENV{SQL_NASTABLE_DB}" + + # How long to wait for write locks on the database to be + # released (in ms) before giving up. + busy_timeout = 200 + + # The bootstrap is handled by src/tests/sql_nas_table/all.mk + } + + radius_db = "radius" + + acct_table1 = "radacct" + acct_table2 = "radacct" + postauth_table = "radpostauth" + authcheck_table = "radcheck" + groupcheck_table = "radgroupcheck" + authreply_table = "radreply" + groupreply_table = "radgroupreply" + usergroup_table = "radusergroup" + read_groups = yes + read_profiles = yes + + # Set to 'yes' to read radius clients from the database ('nas' table) + # Clients will ONLY be read on server startup. + read_clients = yes + + # Table to keep radius client info + client_table = "nas" + + # The group attribute specific to this instance of rlm_sql + group_attribute = "SQL-Group" + + # Read database-specific queries + $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf + } + + always reject { + rcode = reject + } + always fail { + rcode = fail + } + always ok { + rcode = ok + } + always handled { + rcode = handled + } + always invalid { + rcode = invalid + } + always notfound { + rcode = notfound + } + always noop { + rcode = noop + } + always updated { + rcode = updated + } +} + +# +# This virtual server is chosen for processing requests when using: +# +# radiusd -Xd src/tests/ -i 127.0.0.1 -p 12340 -n test +# +server extra { + listen { + ipaddr = 127.0.0.1 + port = ${test_port} + type = auth + } + + authorize { + if (&User-Name == "bob") { + accept + } else { + reject + } + } + + authenticate { + + } +}