@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
#
-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
#
$(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
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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/$@
--- /dev/null
+User-Name = bob
+Cleartext-Password = hello
--- /dev/null
+INSERT INTO nas (nasname,shortname,type,ports,secret,server,community,description) VALUES ('127.0.0.1', 'test', 'test', '123', 'testing123', 'extra', '', 'RADIUS Client');
--- /dev/null
+# -*- 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 {
+
+ }
+}