]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add support for ldap, mysql and postgres tests through the use of an external VM.
authorPhilippe Wooding <philippe.wooding@networkradius.com>
Wed, 6 May 2015 16:18:23 +0000 (18:18 +0200)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 2 Jun 2015 18:35:58 +0000 (14:35 -0400)
Required either TEST_SERVER of <MODULE>_TEST_SERVER env variable(s) to be defined.
Add support for 'PRE' conditions in module tests' unlang definition (just like it's used in the keywords tests)

62 files changed:
src/tests/modules/all.mk
src/tests/modules/ldap/acct.attrs [new file with mode: 0644]
src/tests/modules/ldap/acct.unlang [new file with mode: 0644]
src/tests/modules/ldap/all.mk [new file with mode: 0644]
src/tests/modules/ldap/auth.attrs [new file with mode: 0644]
src/tests/modules/ldap/auth.unlang [new file with mode: 0644]
src/tests/modules/ldap/example.com.ldif [new symlink]
src/tests/modules/ldap/module.conf [new file with mode: 0644]
src/tests/modules/sql_mysql/.gitignore [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_0_start.attrs [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_0_start.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_1_update.attrs [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_1_update.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_2_stop.attrs [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_2_stop.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_start_conflict.attrs [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_start_conflict.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_update_no_start.attrs [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_update_no_start.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/all.mk [new file with mode: 0644]
src/tests/modules/sql_mysql/auth.attrs [new file with mode: 0644]
src/tests/modules/sql_mysql/auth.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/module.conf [new file with mode: 0644]
src/tests/modules/sql_postgresql/.gitignore [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_0_start.attrs [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_0_start.unlang [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_1_update.attrs [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_1_update.unlang [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_2_stop.attrs [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_2_stop.unlang [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_start_conflict.attrs [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_start_conflict.unlang [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_update_no_start.attrs [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_update_no_start.unlang [new file with mode: 0644]
src/tests/modules/sql_postgresql/all.mk [new file with mode: 0644]
src/tests/modules/sql_postgresql/auth.attrs [new file with mode: 0644]
src/tests/modules/sql_postgresql/auth.unlang [new file with mode: 0644]
src/tests/modules/sql_postgresql/module.conf [new file with mode: 0644]
src/tests/modules/sql_sqlite/acct_1_update.unlang
src/tests/modules/sql_sqlite/acct_2_stop.unlang
src/tests/modules/sql_sqlite/acct_start_conflict.unlang
src/tests/modules/sql_sqlite/acct_update_no_start.unlang
src/tests/modules/sql_sqlite/all.mk
src/tests/modules/test.mk [new file with mode: 0644]
src/tests/salt-test-server/.gitignore [new file with mode: 0644]
src/tests/salt-test-server/README [new file with mode: 0644]
src/tests/salt-test-server/build.sh [new file with mode: 0755]
src/tests/salt-test-server/salt/iptable.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/iptables [new file with mode: 0644]
src/tests/salt-test-server/salt/ldap.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/ldap/base.ldif [new file with mode: 0644]
src/tests/salt-test-server/salt/ldap/schema_freeradius.ldif [new file with mode: 0644]
src/tests/salt-test-server/salt/mysql.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/mysql/schema.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/mysql/setup.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/ntp.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/postgres.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/postgres/schema.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/postgres/setup.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/top.sls [new file with mode: 0644]
src/tests/salt-test-server/salt_config/master [new file with mode: 0644]
src/tests/salt-test-server/salt_config/roster [new file with mode: 0644]

index c2c15472931195cc715c5e0a8e6207b11778cdbb..ee5fd2c5356146c3e25ec29e5203698dec588628 100644 (file)
@@ -32,132 +32,9 @@ $(foreach x,$(TEST_SUBBUILT),$(eval $x.test: rlm_$(subst /,_,$x).la))
 ######################################################################
 #
 #  For the remaining subdirs, add on the directory to include.
+#  test.mk will run the tests for all modules
+#  It is included last so that the module specific makefiles can be processed first
+#  (modules that require a test server can
 #
-SUBMAKEFILES := $(addsuffix /all.mk,$(TEST_BUILT) $(subst _,/,$(TEST_SUBBUILT)))
+SUBMAKEFILES := $(addsuffix /all.mk,$(TEST_BUILT) $(subst _,/,$(TEST_SUBBUILT))) test.mk
 
-#
-#  Add the module tests to the overall dependencies
-#
-tests.modules: tests.unit tests.keywords tests.auth $(patsubst %,%.test,$(TEST_BUILT) $(TEST_SUBBUILT))
-
-######################################################################
-#
-#  And now more makefile magic to automatically run the tests
-#  for each module.
-#
-
-define DEFAULT_ATTRS
-ifeq "$(wildcard ${1}.attrs)"
-${1}.attrs
-else
-src/tests/modules/default-input.attrs
-endif
-endef
-
-#
-#  Files in the output dir depend on the unit tests
-#
-#      src/tests/$(MODULE_DIR)/FOO.unlang      unlang for the test
-#      src/tests/$(MODULE_DIR)/FOO.attrs       input RADIUS and output filter
-#      build/tests/$(MODULE_DIR)/FOO.out       updated if the test succeeds
-#      build/tests/$(MODULE_DIR)/FOO.log       debug output for the test
-#
-#  If the test fails, then look for ERROR in the input.  No error
-#  means it's unexpected, so we die.
-#
-#  Otherwise, check the log file for a parse error which matches the
-#  ERROR line in the input.
-#
-$(BUILD_DIR)/tests/modules/%: src/tests/modules/%.unlang $(BUILD_DIR)/tests/modules/%.attrs $(TESTBINDIR)/unittest | build.raddb
-       @mkdir -p $(dir $@)
-       @echo MODULE-TEST $(lastword $(subst /, ,$(dir $@))) $(basename $(notdir $@))
-       @if ! MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx > $@.log 2>&1; then \
-               if ! grep ERROR $< 2>&1 > /dev/null; then \
-                       cat $@.log; \
-                       echo "# $@.log"; \
-                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
-                       exit 1; \
-               fi; \
-               FOUND=$$(grep ^$< $@.log | head -1 | sed 's/:.*//;s/.*\[//;s/\].*//'); \
-               EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \
-               if [ "$$EXPECTED" != "$$FOUND" ]; then \
-                       cat $@.log; \
-                       echo "# $@.log"; \
-                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
-                       exit 1; \
-               fi \
-       fi
-       @touch $@
-
-#
-#  Sometimes we have a default input.  So use that.  Otherwise, use
-#  the input specific to the test.
-#
-MODULE_UNLANG          := $(wildcard src/tests/modules/*/*.unlang src/tests/modules/*/*/*.unlang)
-MODULE_ATTRS_REQUIRES  := $(patsubst %.unlang,%.attrs,$(MODULE_UNLANG))
-MODULE_ATTRS_EXISTS    := $(wildcard src/tests/modules/*/*.attrs src/tests/modules/*/*/*.attrs)
-MODULE_ATTRS_NEEDS     := $(filter-out $(MODULE_ATTRS_EXISTS),$(MODULE_ATTRS_REQUIRES))
-
-MODULE_CONF_REQUIRES   := $(patsubst %.unlang,%.conf,$(MODULE_UNLANG))
-MODULE_CONF_EXISTS     := $(wildcard src/tests/modules/*/*.conf src/tests/modules/*/*/*.attrs)
-MODULE_CONF_NEEDS      := $(filter-out $(MODULE_CONF_EXISTS),$(MODULE_CONF_REQUIRES))
-
-#
-#  The complete list of tests which are to be run
-#
-MODULE_TESTS           := $(patsubst src/tests/modules/%/all.mk,%,$(wildcard src/tests/modules/*/all.mk))
-
-
-#
-#  Target-specific rules
-#
-define MODULE_COPY_FILE
-$(BUILD_DIR)/${1}: src/${1}
-       @mkdir -p $$(@D)
-       @cp $$< $$@
-
-endef
-
-#
-#  Default rules
-#
-define MODULE_COPY_ATTR
-$(BUILD_DIR)/${1}: src/tests/modules/default-input.attrs
-       mkdir -p $$(@D)
-       @cp $$< $$@
-endef
-
-#
-#  FIXME: get this working
-#
-define MODULE_COPY_CONF
-$(BUILD_DIR)/${1}: src/tests/modules/${2}/module.conf
-       @mkdir -p $$(@D)
-       @cp $$< $$@
-endef
-
-define MODULE_FILE_TARGET
-$(BUILD_DIR)/${1}: src/${1}.unlang $(BUILD_DIR)/${1}.attrs
-
-endef
-
-define MODULE_TEST_TARGET
-${1}.test: $(patsubst %.unlang,%,$(subst src,$(BUILD_DIR),$(filter src/tests/modules/${1}/%,$(MODULE_UNLANG))))
-
-endef
-
-#
-#  Create the rules from the list of input files
-#
-$(foreach x,$(MODULE_ATTRS_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
-$(foreach x,$(MODULE_CONF_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
-
-$(foreach x,$(MODULE_ATTRS_NEEDS),$(eval $(call MODULE_COPY_ATTR,$(subst src/,,$x))))
-# FIXME: copy src/tests/modules/*/module.conf to the right place, too
-
-$(foreach x,$(MODULE_UNLANG),$(eval $(call MODULE_FILE_TARGET,$(patsubst %.unlang,%,$(subst src/,,$x)))))
-$(foreach x,$(MODULE_TESTS),$(eval $(call MODULE_TEST_TARGET,$x)))
-
-.PHONY: clean.modules.test
-clean.modules.test:
-       @rm -rf $(BUILD_DIR)/tests/modules/
diff --git a/src/tests/modules/ldap/acct.attrs b/src/tests/modules/ldap/acct.attrs
new file mode 100644 (file)
index 0000000..1d57034
--- /dev/null
@@ -0,0 +1,35 @@
+#
+#  Input packet
+#
+User-Name = 'john'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/ldap/acct.unlang b/src/tests/modules/ldap/acct.unlang
new file mode 100644 (file)
index 0000000..2297ea7
--- /dev/null
@@ -0,0 +1,23 @@
+#
+#  Run the "ldap" module
+#  PRE: auth
+#
+ldap.accounting {
+}
+if (ok) {
+        test_pass
+}
+else {
+        test_fail
+}
+
+update {
+        Tmp-String-0 := "%{ldap:ldap://$ENV{TEST_SERVER}/uid=john,ou=people,dc=example,dc=com?description}"
+}
+
+if (&Tmp-String-0 != "User john is online") {
+        test_fail
+}
+else {
+        test_pass
+}
diff --git a/src/tests/modules/ldap/all.mk b/src/tests/modules/ldap/all.mk
new file mode 100644 (file)
index 0000000..3464f68
--- /dev/null
@@ -0,0 +1,11 @@
+#
+#  Test the "ldap" module
+#
+
+#  MODULE.test is the main target for this module.
+
+# Don't test ldap if TEST_SERVER ENV is not set
+ldap_require_test_server := 1
+
+ldap.test:
+       @echo OK: ldap.test
diff --git a/src/tests/modules/ldap/auth.attrs b/src/tests/modules/ldap/auth.attrs
new file mode 100644 (file)
index 0000000..be988ee
--- /dev/null
@@ -0,0 +1,15 @@
+#
+#  Input packet
+#
+User-Name = "john"
+User-Password = "password"
+NAS-IP-Address = 1.2.3.5
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Idle-Timeout == 3600
+Session-Timeout == 7200
+Acct-Interim-Interval == 1800
+Framed-IP-Netmask == "255.255.0.0"
diff --git a/src/tests/modules/ldap/auth.unlang b/src/tests/modules/ldap/auth.unlang
new file mode 100644 (file)
index 0000000..5a84500
--- /dev/null
@@ -0,0 +1,80 @@
+#
+#  Run the "ldap" module
+#
+ldap
+
+if (&control:NAS-IP-Address != 1.2.3.4) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&control:Reply-Message != "Hello world") {
+        test_fail
+}
+else {
+        test_pass
+}
+
+# Cmp operator means Framed-IP-Address is ignored
+if (&control:Framed-IP-Address) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+# IP netmask defined in profile1 should overwrite radprofile value.
+if (&reply:Framed-IP-Netmask != 255.255.0.0) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&reply:Acct-Interim-Interval != 1800) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&reply:Idle-Timeout != 3600) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&reply:Session-Timeout != 7200) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if ("%{pairs:reply:}" == "") {
+        test_fail
+}
+
+
+if (LDAP-Group == "foo") {
+        test_pass
+}
+else {
+        test_fail
+}
+
+ldap.post-auth
+
+update {
+        Tmp-String-0 := "%{ldap:ldap://$ENV{TEST_SERVER}/uid=john,ou=people,dc=example,dc=com?description}"
+}
+
+if (&Tmp-String-0 != "User %{User-Name} authenticated") {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/ldap/example.com.ldif b/src/tests/modules/ldap/example.com.ldif
new file mode 120000 (symlink)
index 0000000..055d379
--- /dev/null
@@ -0,0 +1 @@
+../../salt-test-server/salt/ldap/base.ldif
\ No newline at end of file
diff --git a/src/tests/modules/ldap/module.conf b/src/tests/modules/ldap/module.conf
new file mode 100644 (file)
index 0000000..bf978e6
--- /dev/null
@@ -0,0 +1,538 @@
+# -*- text -*-
+#
+#  $Id$
+
+#
+#  Lightweight Directory Access Protocol (LDAP)
+#
+ldap {
+       #  Note that this needs to match the name(s) in the LDAP server
+       #  certificate, if you're using ldaps.  See OpenLDAP documentation
+       #  for the behavioral semantics of specifying more than one host.
+       #
+       #  Depending on the libldap in use, server may be an LDAP URI.
+       #  In the case of OpenLDAP this allows additional the following
+       #  additional schemes:
+       #  - ldaps:// (LDAP over SSL)
+       #  - ldapi:// (LDAP over Unix socket)
+       #  - ldapc:// (Connectionless LDAP)
+       server = $ENV{LDAP_TEST_SERVER}
+#      server = 'ldap.rrdns.example.org'
+
+       #  Port to connect on, defaults to 389, will be ignored for LDAP URIs.
+#      port = 389
+
+       #  Administrator account for searching and possibly modifying.
+       identity = 'cn=admin,dc=example,dc=com'
+       password = secret
+
+       #  Unless overridden in another section, the dn from which all
+       #  searches will start from.
+       base_dn = 'dc=example,dc=com'
+
+       #  SASL parameters to use for admin binds
+       #
+       #  When we're prompted by the SASL library, these control
+       #  the responses given.
+       #
+       sasl {
+               # SASL mechanism
+#              mech = 'PLAIN'
+
+               # SASL authorisation identity to proxy.
+#              proxy = 'autz_id'
+
+               # SASL realm. Used for kerberos.
+#              realm = 'example.org'
+       }
+
+       #
+       #  Generic valuepair attribute
+       #
+
+       #  If set, this will attribute will be retrieved in addition to any
+       #  mapped attributes.
+       #
+       #  Values should be in the format:
+       #       <radius attr> <op> <value>
+       #
+       #  Where:
+       #       <radius attr>:  Is the attribute you wish to create
+       #                       with any valid list and request qualifiers.
+       #       <op>:           Is any assignment operator (=, :=, +=, -=).
+       #       <value>:        Is the value to parse into the new valuepair.
+       #                       If the value is wrapped in double quotes it
+       #                       will be xlat expanded.
+       valuepair_attribute = 'radiusAttribute'
+
+       #
+       #  Mapping of LDAP directory attributes to RADIUS dictionary attributes.
+       #
+
+       #  WARNING: Although this format is almost identical to the unlang
+       #  update section format, it does *NOT* mean that you can use other
+       #  unlang constructs in module configuration files.
+       #
+       #  Configuration items are in the format:
+       #       <radius attr> <op> <ldap attr>
+       #
+       #  Where:
+       #       <radius attr>:  Is the destination RADIUS attribute
+       #                       with any valid list and request qualifiers.
+       #       <op>:           Is any assignment attribute (=, :=, +=, -=).
+       #       <ldap attr>:    Is the attribute associated with user or
+       #                       profile objects in the LDAP directory.
+       #                       If the attribute name is wrapped in double
+       #                       quotes it will be xlat expanded.
+       #
+       #  Request and list qualifiers may also be placed after the 'update'
+       #  section name to set defaults destination requests/lists
+       #  for unqualified RADIUS attributes.
+       #
+       #  Note: LDAP attribute names should be single quoted unless you want
+       #  the name value to be derived from an xlat expansion, or an
+       #  attribute ref.
+       update {
+               control:Password-With-Header    += 'userPassword'
+               reply:Idle-Timeout              := 'radiusIdleTimeout'
+               reply:Framed-IP-Netmask         := 'radiusFramedIPNetmask'
+#              control:NT-Password             := 'ntPassword'
+#              reply:Reply-Message             := 'radiusReplyMessage'
+#              reply:Tunnel-Type               := 'radiusTunnelType'
+#              reply:Tunnel-Medium-Type        := 'radiusTunnelMediumType'
+#              reply:Tunnel-Private-Group-ID   := 'radiusTunnelPrivategroupId'
+
+               #  Where only a list is specified as the RADIUS attribute,
+               #  the value of the LDAP attribute is parsed as a valuepair
+               #  in the same format as the 'valuepair_attribute' (above).
+               control:                        += 'radiusControlAttribute'
+               request:                        += 'radiusRequestAttribute'
+               reply:                          += 'radiusReplyAttribute'
+       }
+
+       #  Set to yes if you have eDirectory and want to use the universal
+       #  password mechanism.
+#      edir = no
+
+       #  Set to yes if you want to bind as the user after retrieving the
+       #  Cleartext-Password. This will consume the login grace, and
+       #  verify user authorization.
+#      edir_autz = no
+
+       #  Note: set_auth_type was removed in v3.x.x
+       #  Equivalent functionality can be achieved by adding the following
+       #  stanza to the authorize {} section of your virtual server.
+       #
+       #    ldap
+       #    if ((ok || updated) && User-Password) {
+       #        update {
+       #            control:Auth-Type := ldap
+       #        }
+       #    }
+
+       #
+       #  User object identification.
+       #
+       user {
+               #  Where to start searching in the tree for users
+               base_dn = 'ou=people,dc=example,dc=com'
+               #base_dn = 'ou=people,${..base_dn}'
+
+               #  Filter for user objects, should be specific enough
+               #  to identify a single user object.
+               filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+
+               #  SASL parameters to use for user binds
+               #
+               #  When we're prompted by the SASL library, these control
+               #  the responses given.
+               #
+               #  Any of the config items below may be an attribute ref
+               #  or and expansion, so different SASL mechs, proxy IDs
+               #  and realms may be used for different users.
+               sasl {
+                       # SASL mechanism
+#                      mech = 'PLAIN'
+
+                       # SASL authorisation identity to proxy.
+#                      proxy = &User-Name
+
+                       # SASL realm. Used for kerberos.
+#                      realm = 'example.org'
+               }
+
+               #  Search scope, may be 'base', 'one', sub' or 'children'
+#              scope = 'sub'
+
+               #  If this is undefined, anyone is authorised.
+               #  If it is defined, the contents of this attribute
+               #  determine whether or not the user is authorised
+#              access_attribute = 'dialupAccess'
+
+               #  Control whether the presence of 'access_attribute'
+               #  allows access, or denys access.
+               #
+               #  If 'yes', and the access_attribute is present, or
+               #  'no' and the access_attribute is absent then access
+               #  will be allowed.
+               #
+               #  If 'yes', and the access_attribute is absent, or
+               #  'no' and the access_attribute is present, then
+               #  access will not be allowed.
+               #
+               #  If the value of the access_attribute is 'false', it
+               #  will negate the result.
+               #
+               #  e.g.
+               #    access_positive = yes
+               #    access_attribute = userAccessAllowed
+               #
+               #  With an LDAP object containing:
+               #    userAccessAllowed: false
+               #
+               #  Will result in the user being locked out.
+#              access_positive = yes
+       }
+
+       #
+       #  User membership checking.
+       #
+       group {
+               #  Where to start searching in the tree for groups
+               base_dn = 'ou=groups,dc=example,dc=com'
+
+               #  Filter for group objects, should match all available
+               #  group objects a user might be a member of.
+               filter = '(objectClass=groupOfNames)'
+
+               # Search scope, may be 'base', 'one', sub' or 'children'
+#              scope = 'sub'
+
+               #  Attribute that uniquely identifies a group.
+               #  Is used when converting group DNs to group
+               #  names.
+               name_attribute = cn
+
+               #  Filter to find group objects a user is a member of.
+               #  That is, group objects with attributes that
+               #  identify members (the inverse of membership_attribute).
+               membership_filter = "(|(member=%{control:Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+
+               #  The attribute in user objects which contain the names
+               #  or DNs of groups a user is a member of.
+               #
+               #  Unless a conversion between group name and group DN is
+               #  needed, there's no requirement for the group objects
+               #  referenced to actually exist.
+#              membership_attribute = 'memberOf'
+
+               #  If cacheable_name or cacheable_dn are enabled,
+               #  all group information for the user will be
+               #  retrieved from the directory and written to LDAP-Group
+               #  attributes appropriate for the instance of rlm_ldap.
+               #
+               #  For group comparisons these attributes will be checked
+               #  instead of querying the LDAP directory directly.
+               #
+               #  This feature is intended to be used with rlm_cache.
+               #
+               #  If you wish to use this feature, you should enable
+               #  the type that matches the format of your check items
+               #  i.e. if your groups are specified as DNs then enable
+               #  cacheable_dn else enable cacheable_name.
+#              cacheable_name = 'no'
+#              cacheable_dn = 'no'
+
+               #  Override the normal cache attribute (<inst>-LDAP-Group)
+               #  and create a custom attribute.  This can help if multiple
+               #  module instances are used in fail-over.
+#              cache_attribute = 'LDAP-Cached-Membership'
+       }
+
+       #
+       #  User profiles. RADIUS profile objects contain sets of attributes
+       #  to insert into the request. These attributes are mapped using
+       #  the same mapping scheme applied to user objects.
+       #
+       profile {
+               #  Filter for RADIUS profile objects
+               filter = '(objectclass=radiusprofile)'
+
+               #  The default profile applied to all users.
+               default = 'cn=radprofile,ou=profiles,dc=example,dc=com'
+
+               #  The list of profiles which are applied (after the default)
+               #  to all users.
+               #  The 'User-Profile' attribute in the control list
+               #  will override this setting at run-time.
+               attribute = 'radiusProfileDn'
+       }
+
+       #
+       #  Bulk load clients from the directory
+       #
+       client {
+               #   Where to start searching in the tree for clients
+               base_dn = 'ou=people,dc=example,dc=com'
+
+               #
+               #  Filter to match client objects
+               #
+               filter = '(objectClass=radiusClient)'
+
+               # Search scope, may be 'base', 'one', 'sub' or 'children'
+#              scope = 'sub'
+
+               #
+               #  Sets default values (not obtained from LDAP) for new client entries
+               #
+               template {
+#                      login                           = 'test'
+#                      password                        = 'test'
+#                      proto                           = tcp
+#                      require_message_authenticator   = yes
+
+                       # Uncomment to add a home_server with the same
+                       # attributes as the client.
+#                      coa_server {
+#                              response_window = 2.0
+#                      }
+               }
+
+               #
+               #  Client attribute mappings are in the format:
+               #      <client attribute> = <ldap attribute>
+               #
+               #  The following attributes are required:
+               #    * ipaddr | ipv4addr | ipv6addr - Client IP Address.
+               #    * secret - RADIUS shared secret.
+               #
+               #  All other attributes usually supported in a client
+               #  definition are also supported here.
+               #
+               #  Schemas are available in doc/schemas/ldap for openldap and eDirectory
+               #
+               attribute {
+                       ipaddr                          = 'radiusClientIdentifier'
+                       secret                          = 'radiusClientSecret'
+#                      shortname                       = 'radiusClientShortname'
+#                      nas_type                        = 'radiusClientType'
+#                      virtual_server                  = 'radiusClientVirtualServer'
+#                      require_message_authenticator   = 'radiusClientRequireMa'
+               }
+       }
+
+       #  Load clients on startup
+#      read_clients = no
+
+       #
+       #  Modify user object on receiving Accounting-Request
+       #
+
+       #  Useful for recording things like the last time the user logged
+       #  in, or the Acct-Session-ID for CoA/DM.
+       #
+       #  LDAP modification items are in the format:
+       #       <ldap attr> <op> <value>
+       #
+       #  Where:
+       #       <ldap attr>:    The LDAP attribute to add modify or delete.
+       #       <op>:           One of the assignment operators:
+       #                       (:=, +=, -=, ++).
+       #                       Note: '=' is *not* supported.
+       #       <value>:        The value to add modify or delete.
+       #
+       #  WARNING: If using the ':=' operator with a multi-valued LDAP
+       #  attribute, all instances of the attribute will be removed and
+       #  replaced with a single attribute.
+       accounting {
+               reference = "%{tolower:type.%{Acct-Status-Type}}"
+
+               type {
+                       start {
+                               update {
+                                       description := "User %{User-Name} is online"
+                               }
+                       }
+
+                       interim-update {
+                               update {
+                                       description := "Last seen at %S"
+                               }
+                       }
+
+                       stop {
+                               update {
+                                       description := "Offline at %S"
+                               }
+                       }
+               }
+       }
+
+       #
+       #  Post-Auth can modify LDAP objects too
+       #
+       post-auth {
+               update {
+                       description := "User %{User-Name} authenticated"
+               }
+       }
+
+       #
+       #  LDAP connection-specific options.
+       #
+       #  These options set timeouts, keep-alives, etc. for the connections.
+       #
+       options {
+               #  Control under which situations aliases are followed.
+               #  May be one of 'never', 'searching', 'finding' or 'always'
+               #  default: libldap's default which is usually 'never'.
+               #
+               #  LDAP_OPT_DEREF is set to this value.
+#              dereference = 'always'
+
+               #
+               #  The following two configuration items control whether the
+               #  server follows references returned by LDAP directory.
+               #  They are  mostly for Active Directory compatibility.
+               #  If you set these to 'no', then searches will likely return
+               #  'operations error', instead of a useful result.
+               #
+               chase_referrals = yes
+               rebind = yes
+
+               #  Seconds to wait for LDAP query to finish. default: 20
+               timeout = 10
+
+               #  Seconds LDAP server has to process the query (server-side
+               #  time limit). default: 20
+               #
+               #  LDAP_OPT_TIMELIMIT is set to this value.
+               timelimit = 3
+
+               #  Seconds to wait for response of the server. (network
+               #  failures) default: 10
+               #
+               #  LDAP_OPT_NETWORK_TIMEOUT is set to this value.
+               net_timeout = 1
+
+               #  LDAP_OPT_X_KEEPALIVE_IDLE
+               idle = 60
+
+               #  LDAP_OPT_X_KEEPALIVE_PROBES
+               probes = 3
+
+               #  LDAP_OPT_X_KEEPALIVE_INTERVAL
+               interval = 3
+
+               #  ldap_debug: debug flag for LDAP SDK
+               #  (see OpenLDAP documentation).  Set this to enable
+               #  huge amounts of LDAP debugging on the screen.
+               #  You should only use this if you are an LDAP expert.
+               #
+               #       default: 0x0000 (no debugging messages)
+               #       Example:(LDAP_DEBUG_FILTER+LDAP_DEBUG_CONNS)
+               ldap_debug = 0x0028
+       }
+
+       #
+       #  This subsection configures the tls related items
+       #  that control how FreeRADIUS connects to an LDAP
+       #  server.  It contains all of the 'tls_*' configuration
+       #  entries used in older versions of FreeRADIUS.  Those
+       #  configuration entries can still be used, but we recommend
+       #  using these.
+       #
+       tls {
+               # Set this to 'yes' to use TLS encrypted connections
+               # to the LDAP database by using the StartTLS extended
+               # operation.
+               #
+               # The StartTLS operation is supposed to be
+               # used with normal ldap connections instead of
+               # using ldaps (port 636) connections
+#              start_tls = yes
+
+#              ca_file = ${certdir}/cacert.pem
+
+#              ca_path = ${certdir}
+#              certificate_file = /path/to/radius.crt
+#              private_key_file = /path/to/radius.key
+#              random_file = ${certdir}/random
+
+               #  Certificate Verification requirements.  Can be:
+               #    'never' (don't even bother trying)
+               #    'allow' (try, but don't fail if the certificate
+               #               can't be verified)
+               #    'demand' (fail if the certificate doesn't verify.)
+               #
+               #  The default is 'allow'
+#              require_cert    = 'demand'
+       }
+
+
+       #  As of version 3.0, the 'pool' section has replaced the
+       #  following configuration items:
+       #
+       #  ldap_connections_number
+
+       #  The connection pool is new for 3.0, and will be used in many
+       #  modules, for all kinds of connection-related activity.
+       #
+       #  When the server is not threaded, the connection pool
+       #  limits are ignored, and only one connection is used.
+       pool {
+               #  Number of connections to start
+               start = 5
+
+               #  Minimum number of connections to keep open
+               min = 4
+
+               #  Maximum number of connections
+               #
+               #  If these connections are all in use and a new one
+               #  is requested, the request will NOT get a connection.
+               #
+               #  Setting 'max' to LESS than the number of threads means
+               #  that some threads may starve, and you will see errors
+               #  like 'No connections available and at max connection limit'
+               #
+               #  Setting 'max' to MORE than the number of threads means
+               #  that there are more connections than necessary.
+               max = 4
+
+               #  Spare connections to be left idle
+               #
+               #  NOTE: Idle connections WILL be closed if 'idle_timeout'
+               #  is set.
+               spare = 3
+
+               #  Number of uses before the connection is closed
+               #
+               #  0 means 'infinite'
+               uses = 0
+
+               #  The lifetime (in seconds) of the connection
+               lifetime = 0
+
+               #  Idle timeout (in seconds).  A connection which is
+               #  unused for this length of time will be closed.
+               idle_timeout = 60
+
+               # The number of seconds to wait after the server tries
+               # to open a connection, and fails.  During this time,
+               # no new connections will be opened.
+               #
+               retry_delay = 1
+
+               #  NOTE: All configuration settings are enforced.  If a
+               #  connection is closed because of 'idle_timeout',
+               #  'uses', or 'lifetime', then the total number of
+               #  connections MAY fall below 'min'.  When that
+               #  happens, it will open a new connection.  It will
+               #  also log a WARNING message.
+               #
+               #  The solution is to either lower the 'min' connections,
+               #  or increase lifetime/idle_timeout.
+       }
+}
diff --git a/src/tests/modules/sql_mysql/.gitignore b/src/tests/modules/sql_mysql/.gitignore
new file mode 100644 (file)
index 0000000..405551a
--- /dev/null
@@ -0,0 +1 @@
+rlm_sql_sqlite.db
diff --git a/src/tests/modules/sql_mysql/acct_0_start.attrs b/src/tests/modules/sql_mysql/acct_0_start.attrs
new file mode 100644 (file)
index 0000000..ba5e194
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_mysql/acct_0_start.unlang b/src/tests/modules/sql_mysql/acct_0_start.unlang
new file mode 100644 (file)
index 0000000..5b4cc81
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_mysql:DELETE FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql_mysql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_mysql/acct_1_update.attrs b/src/tests/modules/sql_mysql/acct_1_update.attrs
new file mode 100644 (file)
index 0000000..33f6ec0
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 10
+Acct-Output-Octets = 10
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 30
+Acct-Input-Packets = 10
+Acct-Output-Packets = 10
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_mysql/acct_1_update.unlang b/src/tests/modules/sql_mysql/acct_1_update.unlang
new file mode 100644 (file)
index 0000000..2eb461a
--- /dev/null
@@ -0,0 +1,30 @@
+#
+#  PRE: acct_0_start
+#
+sql_mysql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_mysql/acct_2_stop.attrs b/src/tests/modules/sql_mysql/acct_2_stop.attrs
new file mode 100644 (file)
index 0000000..b6f1883
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 15
+Acct-Output-Octets = 15
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 60
+Acct-Input-Packets = 15
+Acct-Output-Packets = 15
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_mysql/acct_2_stop.unlang b/src/tests/modules/sql_mysql/acct_2_stop.unlang
new file mode 100644 (file)
index 0000000..03f9570
--- /dev/null
@@ -0,0 +1,30 @@
+#
+#  PRE: acct_1_update
+#
+sql_mysql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 60)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_mysql/acct_start_conflict.attrs b/src/tests/modules/sql_mysql/acct_start_conflict.attrs
new file mode 100644 (file)
index 0000000..82eeee4
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000001'
+Acct-Unique-Session-Id = '00000001'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_mysql/acct_start_conflict.unlang b/src/tests/modules/sql_mysql/acct_start_conflict.unlang
new file mode 100644 (file)
index 0000000..f3fabc1
--- /dev/null
@@ -0,0 +1,80 @@
+#
+#  PRE: acct_2_stop
+#
+
+#
+#  Check that conflicting unique IDs triggers failover to alternative query
+#
+
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_mysql:DELETE FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Insert the Accounting-Request start
+#
+sql_mysql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+#
+#  Check the database has at least one row
+#
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Check acctsessiontime matches the value in the request
+#
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Change acctsessiontime and verify it's updated
+#
+update request {
+       Connect-Info = 'updated'
+}
+sql_mysql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_pass
+}
+update {
+       Tmp-String-0 := "%{sql_mysql:SELECT connectinfo_start FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-String-0 != 'updated')) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_mysql/acct_update_no_start.attrs b/src/tests/modules/sql_mysql/acct_update_no_start.attrs
new file mode 100644 (file)
index 0000000..23f46cd
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 10
+Acct-Output-Octets = 10
+Acct-Session-Id = '00000002'
+Acct-Unique-Session-Id = '00000002'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 30
+Acct-Input-Packets = 10
+Acct-Output-Packets = 10
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_mysql/acct_update_no_start.unlang b/src/tests/modules/sql_mysql/acct_update_no_start.unlang
new file mode 100644 (file)
index 0000000..76dd4f4
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  PRE: acct_start_conflict
+#
+
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_mysql:DELETE FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql_mysql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_mysql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_mysql/all.mk b/src/tests/modules/sql_mysql/all.mk
new file mode 100644 (file)
index 0000000..48a961b
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Test the mysql module
+#
+
+# Don't test sql_mysql if TEST_SERVER ENV is not set
+sql_mysql_require_test_server := 1
+
+#  MODULE.test is the main target for this module.
+sql_mysql.test:
+       @echo OK: sql_mysql.test
diff --git a/src/tests/modules/sql_mysql/auth.attrs b/src/tests/modules/sql_mysql/auth.attrs
new file mode 100644 (file)
index 0000000..73fb40e
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  Input packet
+#
+User-Name = "foo"
+User-Password = "password"
+NAS-IP-Address = "1.2.3.4"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Idle-Timeout == 3600
diff --git a/src/tests/modules/sql_mysql/auth.unlang b/src/tests/modules/sql_mysql/auth.unlang
new file mode 100644 (file)
index 0000000..1d1b93d
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_mysql:DELETE FROM radcheck WHERE username = 'foo'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_mysql:INSERT INTO radcheck (username, attribute, op, value) VALUES ('foo', 'NAS-IP-Address', '==', '1.2.3.4'), ('foo', 'Cleartext-Password', ':=', 'password')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_mysql:DELETE FROM radreply WHERE username = 'foo'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_mysql:INSERT INTO radreply (username, attribute, op, value) VALUES ('foo', 'Idle-Timeout', ':=', '3600')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql_mysql
diff --git a/src/tests/modules/sql_mysql/module.conf b/src/tests/modules/sql_mysql/module.conf
new file mode 100644 (file)
index 0000000..d8d1b10
--- /dev/null
@@ -0,0 +1,50 @@
+sql sql_mysql {
+       driver = "rlm_sql_mysql"
+       dialect = "mysql"
+
+        # Connection info:
+        #
+        server = $ENV{SQL_MYSQL_TEST_SERVER}
+        port = 3306
+        login = "radius"
+        password = "radpass"
+
+        # Database table configuration for everything except Oracle
+        radius_db = "radius"
+       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
+
+       # Remove stale session if checkrad does not see a double login
+       delete_stale_sessions = yes
+
+       pool {
+               start = 1
+               min = 0
+               max = 1
+               spare = 3
+               uses = 2
+               lifetime = 1
+               idle_timeout = 60
+               retry_delay = 1
+       }
+
+       # 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"
+
+       # Read database-specific queries
+       $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
+}
diff --git a/src/tests/modules/sql_postgresql/.gitignore b/src/tests/modules/sql_postgresql/.gitignore
new file mode 100644 (file)
index 0000000..405551a
--- /dev/null
@@ -0,0 +1 @@
+rlm_sql_sqlite.db
diff --git a/src/tests/modules/sql_postgresql/acct_0_start.attrs b/src/tests/modules/sql_postgresql/acct_0_start.attrs
new file mode 100644 (file)
index 0000000..ba5e194
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_postgresql/acct_0_start.unlang b/src/tests/modules/sql_postgresql/acct_0_start.unlang
new file mode 100644 (file)
index 0000000..51d0081
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_postgresql:DELETE FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql_postgresql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_postgresql/acct_1_update.attrs b/src/tests/modules/sql_postgresql/acct_1_update.attrs
new file mode 100644 (file)
index 0000000..33f6ec0
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 10
+Acct-Output-Octets = 10
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 30
+Acct-Input-Packets = 10
+Acct-Output-Packets = 10
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_postgresql/acct_1_update.unlang b/src/tests/modules/sql_postgresql/acct_1_update.unlang
new file mode 100644 (file)
index 0000000..42b211b
--- /dev/null
@@ -0,0 +1,30 @@
+#
+#  PRE: acct_0_start
+#
+sql_postgresql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_postgresql/acct_2_stop.attrs b/src/tests/modules/sql_postgresql/acct_2_stop.attrs
new file mode 100644 (file)
index 0000000..d8f9dbc
--- /dev/null
@@ -0,0 +1,38 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Stop
+Acct-Terminate-Cause = User-Request
+Acct-Delay-Time = 1
+Acct-Input-Octets = 15
+Acct-Output-Octets = 15
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 120
+Acct-Input-Packets = 15
+Acct-Output-Packets = 15
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_postgresql/acct_2_stop.unlang b/src/tests/modules/sql_postgresql/acct_2_stop.unlang
new file mode 100644 (file)
index 0000000..7d24635
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  PRE: acct_1_update
+#
+sql_postgresql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 120)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_postgresql:SELECT AcctTerminateCause FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-String-0 || (&Tmp-String-0 != 'User-Request')) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_postgresql/acct_start_conflict.attrs b/src/tests/modules/sql_postgresql/acct_start_conflict.attrs
new file mode 100644 (file)
index 0000000..82eeee4
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000001'
+Acct-Unique-Session-Id = '00000001'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_postgresql/acct_start_conflict.unlang b/src/tests/modules/sql_postgresql/acct_start_conflict.unlang
new file mode 100644 (file)
index 0000000..c69c88b
--- /dev/null
@@ -0,0 +1,76 @@
+#
+#  Check that conflicting unique IDs triggers failover to alternative query
+#
+
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_postgresql:DELETE FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Insert the Accounting-Request start
+#
+sql_postgresql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+#
+#  Check the database has at least one row
+#
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Check acctsessiontime matches the value in the request
+#
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Change acctsessiontime and verify it's updated
+#
+update request {
+       Connect-Info = 'updated'
+}
+sql_postgresql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+update {
+       Tmp-String-0 := "%{sql_postgresql:SELECT connectinfo_start FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-String-0 != 'updated')) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_postgresql/acct_update_no_start.attrs b/src/tests/modules/sql_postgresql/acct_update_no_start.attrs
new file mode 100644 (file)
index 0000000..23f46cd
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 10
+Acct-Output-Octets = 10
+Acct-Session-Id = '00000002'
+Acct-Unique-Session-Id = '00000002'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 30
+Acct-Input-Packets = 10
+Acct-Output-Packets = 10
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql_postgresql/acct_update_no_start.unlang b/src/tests/modules/sql_postgresql/acct_update_no_start.unlang
new file mode 100644 (file)
index 0000000..acffcba
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_postgresql:DELETE FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql_postgresql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql_postgresql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql_postgresql/all.mk b/src/tests/modules/sql_postgresql/all.mk
new file mode 100644 (file)
index 0000000..017d798
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Test the postgresql module
+#
+
+# Don't test sql_postgresql if TEST_SERVER ENV is not set
+sql_postgresql_require_test_server := 1
+
+#  MODULE.test is the main target for this module.
+sql_postgresql.test:
+       @echo OK: sql_postgresql.test
diff --git a/src/tests/modules/sql_postgresql/auth.attrs b/src/tests/modules/sql_postgresql/auth.attrs
new file mode 100644 (file)
index 0000000..73fb40e
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  Input packet
+#
+User-Name = "foo"
+User-Password = "password"
+NAS-IP-Address = "1.2.3.4"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Idle-Timeout == 3600
diff --git a/src/tests/modules/sql_postgresql/auth.unlang b/src/tests/modules/sql_postgresql/auth.unlang
new file mode 100644 (file)
index 0000000..da5ddae
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql_postgresql:DELETE FROM radcheck WHERE username = 'foo'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_postgresql:INSERT INTO radcheck (username, attribute, op, value) VALUES ('foo', 'NAS-IP-Address', '==', '1.2.3.4'), ('foo', 'Cleartext-Password', ':=', 'password')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_postgresql:DELETE FROM radreply WHERE username = 'foo'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql_postgresql:INSERT INTO radreply (username, attribute, op, value) VALUES ('foo', 'Idle-Timeout', ':=', '3600')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql_postgresql
diff --git a/src/tests/modules/sql_postgresql/module.conf b/src/tests/modules/sql_postgresql/module.conf
new file mode 100644 (file)
index 0000000..7fbda39
--- /dev/null
@@ -0,0 +1,50 @@
+sql sql_postgresql {
+       driver = "rlm_sql_postgresql"
+       dialect = "postgresql"
+
+        # Connection info:
+        #
+        server = $ENV{SQL_POSTGRESQL_TEST_SERVER}
+        port = 5432
+        login = "radius"
+        password = "radpass"
+
+        # Database table configuration for everything except Oracle
+        radius_db = "radius"
+       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
+
+       # Remove stale session if checkrad does not see a double login
+       delete_stale_sessions = yes
+
+       pool {
+               start = 1
+               min = 0
+               max = 1
+               spare = 3
+               uses = 2
+               lifetime = 1
+               idle_timeout = 60
+               retry_delay = 1
+       }
+
+       # 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"
+
+       # Read database-specific queries
+       $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
+}
index f63f40c8c3cf92f7336772625e6393d3262f3047..4b2113332bdd4fffeae0fab396f1fc4f6bdaaebf 100644 (file)
@@ -1,5 +1,5 @@
 #
-#  PRE: acct_start
+#  PRE: acct_0_start
 #
 sql.accounting
 if (ok) {
index 7060e2d787a35cd16f7f6d50039cdcbeac1d326b..0e319c7ea3a3692466941bd2014baf14d59d6692 100644 (file)
@@ -1,5 +1,5 @@
 #
-#  PRE: acct_update
+#  PRE: acct_1_update
 #
 sql.accounting
 if (ok) {
index 1ad56dc06177ac765e6042a088855c2013338941..81b3f84e6eda49b94a3ec09758f182d9d05fb743 100644 (file)
@@ -1,4 +1,6 @@
 #
+#  PRE: acct_2_stop
+#
 #  Check that conflicting unique IDs triggers failover to alternative query
 #
 
index 5805f24bd2cc4206c7c3a55e898a1decf55d2673..c47208439e33e7a32c062b7fc1df83109f73b86a 100644 (file)
@@ -1,4 +1,6 @@
 #
+# PRE: acct_start_conflict
+#
 #  Clear out old data
 #
 update {
index 95e9b8043d489a8641f553985119616d4d58838b..fa15faee2364e31adda8ad0e08b703cfcd6e501f 100644 (file)
@@ -6,13 +6,3 @@
 sql_sqlite.test:
        @echo OK: sql_sqlite.test
 
-SQLITE_TESTDIR := $(BUILD_DIR)/tests/modules/sql_sqlite
-
-$(SQLITE_TESTDIR)/acct_update_no_start: $(SQLITE_TESTDIR)/acct_start_conflict
-
-$(SQLITE_TESTDIR)/acct_start_conflict: $(SQLITE_TESTDIR)/acct_2_stop
-
-$(SQLITE_TESTDIR)/acct_2_stop: $(SQLITE_TESTDIR)/acct_1_update
-
-$(SQLITE_TESTDIR)/acct_1_update: $(SQLITE_TESTDIR)/acct_0_start
-
diff --git a/src/tests/modules/test.mk b/src/tests/modules/test.mk
new file mode 100644 (file)
index 0000000..41e4d11
--- /dev/null
@@ -0,0 +1,161 @@
+#
+#  Add the module tests to the overall dependencies
+#
+tests.modules: tests.unit tests.keywords tests.auth
+
+# If module requires test server, make sure TEST_SERVER of <MODULE>_TEST_SERVER variables are defined
+# If TEST_SERVER is defined, define <MODULE>_TEST_SERVER for all modules that have CHECK_MODULE_TEST_CAN_BE_RUN
+define CHECK_MODULE_TEST_CAN_BE_RUN
+  ifndef ${1}_require_test_server
+    tests.modules: ${1}.test
+  else
+    ifdef TEST_SERVER
+      tests.modules: ${1}.test
+      export $(shell echo ${1} | tr a-z A-Z)_TEST_SERVER := $(TEST_SERVER)
+    endif
+    ifdef $(shell echo ${1} | tr a-z A-Z)_TEST_SERVER
+      tests.modules: ${1}.test
+    endif
+  endif
+endef
+$(foreach x,$(TEST_BUILT) $(TEST_SUBBUILT),$(eval $(call CHECK_MODULE_TEST_CAN_BE_RUN,$x)))
+
+######################################################################
+#
+#  And now more makefile magic to automatically run the tests
+#  for each module.
+#
+
+define DEFAULT_ATTRS
+ifeq "$(wildcard ${1}.attrs)"
+${1}.attrs
+else
+src/tests/modules/default-input.attrs
+endif
+endef
+
+#
+#  Files in the output dir depend on the unit tests
+#
+#      src/tests/$(MODULE_DIR)/FOO.unlang      unlang for the test
+#      src/tests/$(MODULE_DIR)/FOO.attrs       input RADIUS and output filter
+#      build/tests/$(MODULE_DIR)/FOO.out       updated if the test succeeds
+#      build/tests/$(MODULE_DIR)/FOO.log       debug output for the test
+#
+#  If the test fails, then look for ERROR in the input.  No error
+#  means it's unexpected, so we die.
+#
+#  Otherwise, check the log file for a parse error which matches the
+#  ERROR line in the input.
+#
+$(BUILD_DIR)/tests/modules/%: src/tests/modules/%.unlang $(BUILD_DIR)/tests/modules/%.attrs $(TESTBINDIR)/unittest | build.raddb
+       @mkdir -p $(dir $@)
+       @echo MODULE-TEST $(lastword $(subst /, ,$(dir $@))) $(basename $(notdir $@))
+       @if ! MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xxx > $@.log 2>&1; then \
+               if ! grep ERROR $< 2>&1 > /dev/null; then \
+                       cat $@.log; \
+                       echo "# $@.log"; \
+                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
+                       exit 1; \
+               fi; \
+               FOUND=$$(grep ^$< $@.log | head -1 | sed 's/:.*//;s/.*\[//;s/\].*//'); \
+               EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \
+               if [ "$$EXPECTED" != "$$FOUND" ]; then \
+                       cat $@.log; \
+                       echo "# $@.log"; \
+                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
+                       exit 1; \
+               fi \
+       fi
+       @touch $@
+
+#
+#  Sometimes we have a default input.  So use that.  Otherwise, use
+#  the input specific to the test.
+#
+MODULE_UNLANG          := $(wildcard src/tests/modules/*/*.unlang src/tests/modules/*/*/*.unlang)
+MODULE_ATTRS_REQUIRES  := $(patsubst %.unlang,%.attrs,$(MODULE_UNLANG))
+MODULE_ATTRS_EXISTS    := $(wildcard src/tests/modules/*/*.attrs src/tests/modules/*/*/*.attrs)
+MODULE_ATTRS_NEEDS     := $(filter-out $(MODULE_ATTRS_EXISTS),$(MODULE_ATTRS_REQUIRES))
+
+MODULE_CONF_REQUIRES   := $(patsubst %.unlang,%.conf,$(MODULE_UNLANG))
+MODULE_CONF_EXISTS     := $(wildcard src/tests/modules/*/*.conf src/tests/modules/*/*/*.attrs)
+MODULE_CONF_NEEDS      := $(filter-out $(MODULE_CONF_EXISTS),$(MODULE_CONF_REQUIRES))
+
+#
+#  The complete list of tests which are to be run
+#
+MODULE_TESTS           := $(patsubst src/tests/modules/%/all.mk,%,$(wildcard src/tests/modules/*/all.mk))
+
+
+#
+#  Target-specific rules
+#
+define MODULE_COPY_FILE
+$(BUILD_DIR)/${1}: src/${1}
+       @mkdir -p $$(@D)
+       @cp $$< $$@
+
+endef
+
+#
+#  Default rules
+#
+define MODULE_COPY_ATTR
+$(BUILD_DIR)/${1}: src/tests/modules/default-input.attrs
+       mkdir -p $$(@D)
+       @cp $$< $$@
+endef
+
+#
+#  FIXME: get this working
+#
+define MODULE_COPY_CONF
+$(BUILD_DIR)/${1}: src/tests/modules/${2}/module.conf
+       @mkdir -p $$(@D)
+       @cp $$< $$@
+endef
+
+define MODULE_FILE_TARGET
+$(BUILD_DIR)/${1}: src/${1}.unlang $(BUILD_DIR)/${1}.attrs
+
+endef
+
+define MODULE_TEST_TARGET
+${1}.test: $(patsubst %.unlang,%,$(subst src,$(BUILD_DIR),$(filter src/tests/modules/${1}/%,$(MODULE_UNLANG))))
+
+endef
+
+#
+#  Create the rules from the list of input files
+#
+$(foreach x,$(MODULE_ATTRS_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
+$(foreach x,$(MODULE_CONF_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
+
+$(foreach x,$(MODULE_ATTRS_NEEDS),$(eval $(call MODULE_COPY_ATTR,$(subst src/,,$x))))
+# FIXME: copy src/tests/modules/*/module.conf to the right place, too
+
+$(foreach x,$(MODULE_UNLANG),$(eval $(call MODULE_FILE_TARGET,$(patsubst %.unlang,%,$(subst src/,,$x)))))
+$(foreach x,$(MODULE_TESTS),$(eval $(call MODULE_TEST_TARGET,$x)))
+
+.PHONY: clean.modules.test
+clean.modules.test:
+       @rm -rf $(BUILD_DIR)/tests/modules/
+
+#
+#  For each file, look for precursor test.
+#  Ensure that each test depends on its precursors.
+#
+-include $(BUILD_DIR)/tests/modules/depends.mk
+
+$(BUILD_DIR)/tests/modules/depends.mk: $(MODULE_UNLANG) | $(BUILD_DIR)/tests/modules
+       @rm -f $@
+       @for x in $^; do \
+               y=`grep PRE $$x | awk '{ print $$3 }'`; \
+               if [ "$$y" != "" ]; then \
+                       z=`echo $$x | sed 's,src/,$(BUILD_DIR)/', | sed 's/.unlang//'`; \
+                       d=$$(basename $$(dirname $$x)); \
+                       echo "$$z: $(BUILD_DIR)/tests/modules/$$d/$$y" >> $@; \
+                       echo "" >> $@; \
+               fi \
+       done
diff --git a/src/tests/salt-test-server/.gitignore b/src/tests/salt-test-server/.gitignore
new file mode 100644 (file)
index 0000000..e3aa327
--- /dev/null
@@ -0,0 +1,8 @@
+# Local files
+*.swp
+.DS_Store
+*.md5
+
+# Salt runtime directories
+tmp
+cache
diff --git a/src/tests/salt-test-server/README b/src/tests/salt-test-server/README
new file mode 100644 (file)
index 0000000..8265f46
--- /dev/null
@@ -0,0 +1,3 @@
+Salt script to build the test VM required for running the ldap, mysql & postgres tests.
+
+See http://docs.saltstack.com/en/latest/index.html
diff --git a/src/tests/salt-test-server/build.sh b/src/tests/salt-test-server/build.sh
new file mode 100755 (executable)
index 0000000..ad1873b
--- /dev/null
@@ -0,0 +1 @@
+salt-ssh --config-dir=salt_config -l quiet "test-server" state.highstate
diff --git a/src/tests/salt-test-server/salt/iptable.sls b/src/tests/salt-test-server/salt/iptable.sls
new file mode 100644 (file)
index 0000000..7aefdd1
--- /dev/null
@@ -0,0 +1,13 @@
+{% if grains['os'] == 'CentOS' %}
+update_firewall:
+    file.managed:
+        - name: /etc/sysconfig/iptables
+        - source: salt://iptables
+
+reload_iptables:
+    cmd.wait:
+        - cwd: /
+        - name: service iptables reload
+        - watch:
+            - file: /etc/sysconfig/iptables
+{% endif %}
diff --git a/src/tests/salt-test-server/salt/iptables b/src/tests/salt-test-server/salt/iptables
new file mode 100644 (file)
index 0000000..2e2d4a2
--- /dev/null
@@ -0,0 +1,15 @@
+# Generated by iptables-save v1.4.7 on Thu Feb 19 13:41:09 2015
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 5432 -j ACCEPT
+-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
+-A INPUT -p icmp -j ACCEPT
+-A INPUT -i lo -j ACCEPT
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+-A INPUT -j REJECT --reject-with icmp-host-prohibited
+-A FORWARD -j REJECT --reject-with icmp-host-prohibited
+COMMIT
+# Completed on Thu Feb 19 13:41:09 2015
diff --git a/src/tests/salt-test-server/salt/ldap.sls b/src/tests/salt-test-server/salt/ldap.sls
new file mode 100644 (file)
index 0000000..006abf8
--- /dev/null
@@ -0,0 +1,41 @@
+{% if grains['os'] == 'Ubuntu' %}
+
+# In Ubuntu 14.10, openldap comes with a broken AppArmor profile (can't connect through socket)
+# Disable AppArmor alltogether
+/etc/init.d/apparmor teardown:
+   cmd.run
+
+update-rc.d -f apparmor remove:
+   cmd.run
+
+{% endif %}
+
+slapd:
+    pkg.installed
+
+ldap-utils:
+    pkg.installed
+
+# Copy ldif file for base structure
+/root/base.ldif:
+    file.managed:
+        - source: salt://ldap/base.ldif
+
+# Copy ldif file for FreeRADIUS schema
+/root/schema_freeradius.ldif:
+    file.managed:
+        - source: salt://ldap/schema_freeradius.ldif
+
+# Add FreeRADIUS schema
+add_fr_schema:
+    cmd.run:
+        - name: "ldapadd -Y EXTERNAL -H ldapi:/// -f /root/schema_freeradius.ldif"
+        - cwd: /root/
+        - unless: "/usr/bin/ldapsearch -Y EXTERNAL -H ldapi:/// -b cn={4}radius,cn=schema,cn=config -s base"
+
+# Create base structure in LDAP
+build_base_structure:
+    cmd.run:
+        - name: "/usr/bin/ldapadd -Y EXTERNAL -H ldapi:/// -f /root/base.ldif"
+        - cwd: /root/
+        - unless: "/usr/bin/ldapsearch -Y EXTERNAL -H ldapi:/// -b dc=example,dc=com -s base"
diff --git a/src/tests/salt-test-server/salt/ldap/base.ldif b/src/tests/salt-test-server/salt/ldap/base.ldif
new file mode 100644 (file)
index 0000000..1cf5d97
--- /dev/null
@@ -0,0 +1,84 @@
+# Database settings
+dn: olcDatabase=hdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcHdbConfig
+olcDatabase: {1}hdb
+olcSuffix: dc=example,dc=com
+olcDbDirectory: /var/lib/ldap
+olcRootDN: cn=admin,dc=example,dc=com
+olcRootPW: {SSHA}SgCZuAcGQA5HlgKi+g5xwVyI2NhXRFYh
+olcDbConfig: set_cachesize 0 2097152 0
+olcDbConfig: set_lk_max_objects 1500
+olcDbConfig: set_lk_max_locks 1500
+olcDbConfig: set_lk_max_lockers 1500
+olcDbIndex: objectClass eq
+olcLastMod: TRUE
+olcDbCheckpoint: 512 30
+olcAccess: to * by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
+olcAccess: to attrs=userPassword by dn="cn=admin,dc=example,dc=com" write by anonymous auth by self write by * none
+olcAccess: to attrs=shadowLastChange by self write by * read
+olcAccess: to dn.base="" by * read
+olcAccess: to * by dn="cn=admin,dc=example,dc=com" write by * read
+
+# Create top-level object in domain
+dn: dc=example,dc=com
+objectClass: top
+objectClass: dcObject
+objectclass: organization
+o: Example Organization
+dc: Example
+description: LDAP Example
+
+dn: ou=people,dc=example,dc=com
+objectClass: organizationalUnit
+ou: people
+
+dn: ou=groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: groups
+
+# foo, groups, example.com
+dn: cn=foo,ou=groups,dc=example,dc=com
+cn: foo
+objectClass: groupOfNames
+objectClass: top
+member: uid=john,ou=people,dc=example,dc=com
+
+dn: ou=profiles,dc=example,dc=com
+objectClass: organizationalUnit
+ou: profiles
+
+dn: cn=radprofile,ou=profiles,dc=example,dc=com
+objectClass: radiusObjectProfile
+objectClass: radiusprofile
+cn: radprofile
+radiusFramedIPNetmask: 255.255.255.0
+
+dn: cn=profile1,ou=profiles,dc=example,dc=com
+objectClass: radiusObjectProfile
+objectClass: radiusprofile
+cn: profile1
+radiusReplyAttribute: Framed-IP-Netmask := 255.255.0.0
+radiusReplyAttribute: Acct-Interim-Interval := 1800
+radiusRequestAttribute: Service-Type := Framed-User
+radiusControlAttribute: Framed-IP-Address == 1.2.3.4
+radiusControlAttribute: Reply-Message == "Hello world"
+
+dn: uid=john,ou=people,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: radiusprofile
+uid: john
+sn: Doe
+givenName: John
+cn: John Doe
+displayName: John Doe
+userPassword: {cleartext}password
+uidNumber: 100
+gidNumber: 100
+homeDirectory: /home/john
+radiusIdleTimeout: 3600
+radiusAttribute: reply:Session-Timeout := 7200
+radiusAttribute: control:NAS-IP-Address := 1.2.3.4
+radiusProfileDN: cn=profile1,ou=profiles,dc=example,dc=com
diff --git a/src/tests/salt-test-server/salt/ldap/schema_freeradius.ldif b/src/tests/salt-test-server/salt/ldap/schema_freeradius.ldif
new file mode 100644 (file)
index 0000000..44d2cb9
--- /dev/null
@@ -0,0 +1,76 @@
+dn: cn=radius,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: radius
+olcAttributeTypes: {0}( 1.3.6.1.4.1.11344.4.3.1.1 NAME 'radiusArapFeatures' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {1}( 1.3.6.1.4.1.11344.4.3.1.2 NAME 'radiusArapSecurity' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {2}( 1.3.6.1.4.1.11344.4.3.1.3 NAME 'radiusArapZoneAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {3}( 1.3.6.1.4.1.11344.4.3.1.44 NAME 'radiusAuthType' DESC 'controlItem: Auth-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {4}( 1.3.6.1.4.1.11344.4.3.1.4 NAME 'radiusCallbackId' DESC 'replyItem: Callback-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {5}( 1.3.6.1.4.1.11344.4.3.1.5 NAME 'radiusCallbackNumber' DESC 'replyItem: Callback-Number' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {6}( 1.3.6.1.4.1.11344.4.3.1.6 NAME 'radiusCalledStationId' DESC 'controlItem: Called-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {7}( 1.3.6.1.4.1.11344.4.3.1.7 NAME 'radiusCallingStationId' DESC 'controlItem: Calling-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {8}( 1.3.6.1.4.1.11344.4.3.1.8 NAME 'radiusClass' DESC 'replyItem: Class' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {9}( 1.3.6.1.4.1.11344.4.3.1.45 NAME 'radiusClientIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {10}( 1.3.6.1.4.1.11344.4.3.1.9 NAME 'radiusFilterId' DESC 'replyItem: Filter-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {11}( 1.3.6.1.4.1.11344.4.3.1.10 NAME 'radiusFramedAppleTalkLink' DESC 'replyItem: Framed-AppleTalk-Link' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {12}( 1.3.6.1.4.1.11344.4.3.1.11 NAME 'radiusFramedAppleTalkNetwork' DESC 'replyItem: Framed-AppleTalk-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {13}( 1.3.6.1.4.1.11344.4.3.1.12 NAME 'radiusFramedAppleTalkZone' DESC 'replyItem: Framed-AppleTalk-Zone' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {14}( 1.3.6.1.4.1.11344.4.3.1.13 NAME 'radiusFramedCompression' DESC 'replyItem: Framed-Compression' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {15}( 1.3.6.1.4.1.11344.4.3.1.14 NAME 'radiusFramedIPAddress' DESC 'replyItem: Framed-IP-Address' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {16}( 1.3.6.1.4.1.11344.4.3.1.15 NAME 'radiusFramedIPNetmask' DESC 'replyItem: Framed-IP-Netmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {17}( 1.3.6.1.4.1.11344.4.3.1.16 NAME 'radiusFramedIPXNetwork' DESC 'replyItem: Framed-IPX-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {18}( 1.3.6.1.4.1.11344.4.3.1.17 NAME 'radiusFramedMTU' DESC' replyItem: Framed-MTU' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {19}( 1.3.6.1.4.1.11344.4.3.1.18 NAME 'radiusFramedProtocol'DESC 'replyItem: Framed-Protocol' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {20}( 1.3.6.1.4.1.11344.4.3.1.19 NAME 'radiusFramedRoute' DESC 'replyItem: Framed-Route' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {21}( 1.3.6.1.4.1.11344.4.3.1.20 NAME 'radiusFramedRouting' DESC 'replyItem: Framed-Routing' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {22}( 1.3.6.1.4.1.11344.4.3.1.46 NAME 'radiusGroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {23}( 1.3.6.1.4.1.11344.4.3.1.47 NAME 'radiusHint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {24}( 1.3.6.1.4.1.11344.4.3.1.48 NAME 'radiusHuntgroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {25}( 1.3.6.1.4.1.11344.4.3.1.21 NAME 'radiusIdleTimeout' DESC 'replyItem: Idle-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {26}( 1.3.6.1.4.1.11344.4.3.1.22 NAME 'radiusLoginIPHost' DESC 'replyItem: Login-IP-Host' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {27}( 1.3.6.1.4.1.11344.4.3.1.23 NAME 'radiusLoginLATGroup' DESC 'replyItem: Login-LAT-Group' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {28}( 1.3.6.1.4.1.11344.4.3.1.24 NAME 'radiusLoginLATNode' DESC 'replyItem: Login-LAT-Node' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {29}( 1.3.6.1.4.1.11344.4.3.1.25 NAME 'radiusLoginLATPort' DESC 'replyItem: Login-LAT-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {30}( 1.3.6.1.4.1.11344.4.3.1.26 NAME 'radiusLoginLATService' DESC 'replyItem: Login-LAT-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {31}( 1.3.6.1.4.1.11344.4.3.1.27 NAME 'radiusLoginService' DESC 'replyItem: Login-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {32}( 1.3.6.1.4.1.11344.4.3.1.28 NAME 'radiusLoginTCPPort' DESC 'replyItem: Login-TCP-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {33}( 1.3.6.1.4.1.11344.4.3.1.29 NAME 'radiusPasswordRetry' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {34}( 1.3.6.1.4.1.11344.4.3.1.30 NAME 'radiusPortLimit' DESC 'replyItem: Port-Limit' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {35}( 1.3.6.1.4.1.11344.4.3.1.49 NAME 'radiusProfileDN' DESC '' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: {36}( 1.3.6.1.4.1.11344.4.3.1.31 NAME 'radiusPrompt' DESC ''EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {37}( 1.3.6.1.4.1.11344.4.3.1.50 NAME 'radiusProxyToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {38}( 1.3.6.1.4.1.11344.4.3.1.51 NAME 'radiusReplicateToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {39}( 1.3.6.1.4.1.11344.4.3.1.52 NAME 'radiusRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE)
+olcAttributeTypes: {40}( 1.3.6.1.4.1.11344.4.3.1.32 NAME 'radiusServiceType' DESC 'replyItem: Service-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {41}( 1.3.6.1.4.1.11344.4.3.1.33 NAME 'radiusSessionTimeout'DESC 'replyItem: Session-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {42}( 1.3.6.1.4.1.11344.4.3.1.34 NAME 'radiusTerminationAction' DESC 'replyItem: Termination-Action' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {43}( 1.3.6.1.4.1.11344.4.3.1.35 NAME 'radiusTunnelAssignmentId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
+olcAttributeTypes: {44}( 1.3.6.1.4.1.11344.4.3.1.36 NAME 'radiusTunnelMediumType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {45}( 1.3.6.1.4.1.11344.4.3.1.37 NAME 'radiusTunnelPassword' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {46}( 1.3.6.1.4.1.11344.4.3.1.38 NAME 'radiusTunnelPreference' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {47}( 1.3.6.1.4.1.11344.4.3.1.39 NAME 'radiusTunnelPrivateGroupId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {48}( 1.3.6.1.4.1.11344.4.3.1.40 NAME 'radiusTunnelServerEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {49}( 1.3.6.1.4.1.11344.4.3.1.41 NAME 'radiusTunnelType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {50}( 1.3.6.1.4.1.11344.4.3.1.42 NAME 'radiusVSA' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {51}( 1.3.6.1.4.1.11344.4.3.1.43 NAME 'radiusTunnelClientEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {52}( 1.3.6.1.4.1.11344.4.3.1.53 NAME 'radiusSimultaneousUse' DESC 'controlItem: Simultaneous-Use' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: {53}( 1.3.6.1.4.1.11344.4.3.1.54 NAME 'radiusLoginTime' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {54}( 1.3.6.1.4.1.11344.4.3.1.55 NAME 'radiusUserCategory' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {55}( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+olcAttributeTypes: {56}( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {57}( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC 'controlItem: Expiration' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {58}( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {59}( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusNASIpAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {60}( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusReplyMessage' DESC 'replyItem: Reply-Message' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {61}( 1.3.6.1.4.1.11344.4.3.1.63 NAME 'radiusControlAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {62}( 1.3.6.1.4.1.11344.4.3.1.64 NAME 'radiusReplyAttribute' DESC 'replyItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {63}( 1.3.6.1.4.1.11344.4.3.1.65 NAME 'radiusRequestAttribute' DESC 'requestItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcObjectClasses: {0}( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $
+ radiusCallbackId $ radiusCallbackNumber $radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $
+ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $radiusFramedProtocol $ radiusAttribute $
+ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $
+ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $
+ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $
+ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $
+ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute ) )
+olcObjectClasses: {1}( 1.3.6.1.4.1.11344.4.3.2.2 NAME 'radiusObjectProfile' DESC 'A Container Objectclass to be used for creating radius profile object' SUP top STRUCTURAL MUST cn MAY ( uid $ userPassword $ description ) )
diff --git a/src/tests/salt-test-server/salt/mysql.sls b/src/tests/salt-test-server/salt/mysql.sls
new file mode 100644 (file)
index 0000000..df1ea00
--- /dev/null
@@ -0,0 +1,74 @@
+mysql-server:
+    pkg.installed
+
+# On Ubuntu, the default MySQL install only listens on localhost
+/etc/mysql/my.cnf:
+{% if grains['os'] == 'Ubuntu' %}
+    file.sed:
+        - before: 127.0.0.1
+        - after: 0.0.0.0
+        - limit: ^bind-address\s+=
+        - require:
+            - pkg: mysql-server
+{% else %}
+    file.exists
+{% endif %}
+
+mysql_daemon:
+    service:
+{% if grains['os'] == 'CentOS' %}
+        - name: mysqld
+{% elif grains['os'] == 'Ubuntu' or grains['os'] == 'Debian' %}
+        - name: mysql
+{% endif %}
+        - running
+        - enable: True
+        - watch:
+            - file: /etc/mysql/my.cnf
+        - require:
+            - pkg: mysql-server
+
+## FW rules don't work well with CentOS < 7
+# Insert is run each time
+#
+#    iptables.insert:
+#        - position: 1
+#        - table: filter
+#        - chain: INPUT
+#        - j: ACCEPT        # Use 'j' instead of 'jump' because iptables-save outputs 'j' flag.
+#        - match: state
+#        - connstate: NEW
+#        - dport: 3306
+#        - proto: tcp
+#        - save: True
+
+# Copy DB schema file
+/salt/mysql/schema.sql:
+    file.managed:
+        - source: salt://mysql/schema.sql
+        - makedirs: true
+
+# Copy DB setup script
+/salt/mysql/setup.sql:
+    file.managed:
+        - source: salt://mysql/setup.sql
+        - makedirs: true
+
+# Create DB
+echo "CREATE DATABASE radius" | mysql:
+    cmd.run:
+        - creates: /var/lib/mysql/radius/db.opt
+
+# Create FreeRADIUS schema
+mysql radius < /salt/mysql/schema.sql:
+    cmd.run:
+        - unless: "echo 'desc radacct' | mysql radius"
+        - require:
+            - file: /salt/mysql/schema.sql
+
+# Setup DB access
+mysql radius < /salt/mysql/setup.sql:
+    cmd.run:
+        - unless: "echo \"show grants for 'radius';\" | mysql"
+        - require:
+            - file: /salt/mysql/setup.sql
diff --git a/src/tests/salt-test-server/salt/mysql/schema.sql b/src/tests/salt-test-server/salt/mysql/schema.sql
new file mode 100644 (file)
index 0000000..7761a62
--- /dev/null
@@ -0,0 +1,150 @@
+###########################################################################
+# $Id$                 #
+#                                                                         #
+#  schema.sql                       rlm_sql - FreeRADIUS SQL Module       #
+#                                                                         #
+#     Database schema for MySQL rlm_sql module                            #
+#                                                                         #
+#     To load:                                                            #
+#         mysql -uroot -prootpass radius < schema.sql                     #
+#                                                                         #
+#                                   Mike Machado <mike@innercite.com>     #
+###########################################################################
+#
+# Table structure for table 'radacct'
+#
+
+CREATE TABLE radacct (
+  radacctid bigint(21) NOT NULL auto_increment,
+  acctsessionid varchar(64) NOT NULL default '',
+  acctuniqueid varchar(32) NOT NULL default '',
+  username varchar(64) NOT NULL default '',
+  groupname varchar(64) NOT NULL default '',
+  realm varchar(64) default '',
+  nasipaddress varchar(15) NOT NULL default '',
+  nasportid varchar(50) default NULL,
+  nasporttype varchar(32) default NULL,
+  acctstarttime datetime NULL default NULL,
+  acctupdatetime datetime NULL default NULL,
+  acctstoptime datetime NULL default NULL,
+  acctinterval int(12) default NULL,
+  acctsessiontime int(12) unsigned default NULL,
+  acctauthentic varchar(32) default NULL,
+  connectinfo_start varchar(50) default NULL,
+  connectinfo_stop varchar(50) default NULL,
+  acctinputoctets bigint(20) default NULL,
+  acctoutputoctets bigint(20) default NULL,
+  calledstationid varchar(50) NOT NULL default '',
+  callingstationid varchar(50) NOT NULL default '',
+  acctterminatecause varchar(32) NOT NULL default '',
+  servicetype varchar(32) default NULL,
+  framedprotocol varchar(32) default NULL,
+  framedipaddress varchar(15) NOT NULL default '',
+  PRIMARY KEY (radacctid),
+  UNIQUE KEY acctuniqueid (acctuniqueid),
+  KEY username (username),
+  KEY framedipaddress (framedipaddress),
+  KEY acctsessionid (acctsessionid),
+  KEY acctsessiontime (acctsessiontime),
+  KEY acctstarttime (acctstarttime),
+  KEY acctinterval (acctinterval),
+  KEY acctstoptime (acctstoptime),
+  KEY nasipaddress (nasipaddress)
+) ENGINE = INNODB;
+
+#
+# Table structure for table 'radcheck'
+#
+
+CREATE TABLE radcheck (
+  id int(11) unsigned NOT NULL auto_increment,
+  username varchar(64) NOT NULL default '',
+  attribute varchar(64)  NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '==',
+  value varchar(253) NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY username (username(32))
+);
+
+#
+# Table structure for table 'radgroupcheck'
+#
+
+CREATE TABLE radgroupcheck (
+  id int(11) unsigned NOT NULL auto_increment,
+  groupname varchar(64) NOT NULL default '',
+  attribute varchar(64)  NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '==',
+  value varchar(253)  NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY groupname (groupname(32))
+);
+
+#
+# Table structure for table 'radgroupreply'
+#
+
+CREATE TABLE radgroupreply (
+  id int(11) unsigned NOT NULL auto_increment,
+  groupname varchar(64) NOT NULL default '',
+  attribute varchar(64)  NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '=',
+  value varchar(253)  NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY groupname (groupname(32))
+);
+
+#
+# Table structure for table 'radreply'
+#
+
+CREATE TABLE radreply (
+  id int(11) unsigned NOT NULL auto_increment,
+  username varchar(64) NOT NULL default '',
+  attribute varchar(64) NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '=',
+  value varchar(253) NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY username (username(32))
+);
+
+
+#
+# Table structure for table 'radusergroup'
+#
+
+CREATE TABLE radusergroup (
+  username varchar(64) NOT NULL default '',
+  groupname varchar(64) NOT NULL default '',
+  priority int(11) NOT NULL default '1',
+  KEY username (username(32))
+);
+
+#
+# Table structure for table 'radpostauth'
+#
+CREATE TABLE radpostauth (
+  id int(11) NOT NULL auto_increment,
+  username varchar(64) NOT NULL default '',
+  pass varchar(64) NOT NULL default '',
+  reply varchar(32) NOT NULL default '',
+  authdate timestamp NOT NULL,
+  PRIMARY KEY  (id)
+) ENGINE = INNODB;
+
+#
+# Table structure for table 'nas'
+#
+CREATE TABLE nas (
+  id int(10) NOT NULL auto_increment,
+  nasname varchar(128) NOT NULL,
+  shortname varchar(32),
+  type varchar(30) DEFAULT 'other',
+  ports int(5),
+  secret varchar(60) DEFAULT 'secret' NOT NULL,
+  server varchar(64),
+  community varchar(50),
+  description varchar(200) DEFAULT 'RADIUS Client',
+  PRIMARY KEY (id),
+  KEY nasname (nasname)
+);
diff --git a/src/tests/salt-test-server/salt/mysql/setup.sql b/src/tests/salt-test-server/salt/mysql/setup.sql
new file mode 100644 (file)
index 0000000..3b9ec54
--- /dev/null
@@ -0,0 +1,18 @@
+# -*- text -*-
+##
+## admin.sql -- MySQL commands for creating the RADIUS user.
+##
+##     WARNING: You should change 'localhost' and 'radpass'
+##              to something else.  Also update raddb/sql.conf
+##              with the new RADIUS password.
+##
+##     $Id$
+
+#
+#  Create default administrator for RADIUS
+#
+CREATE USER 'radius';
+SET PASSWORD FOR 'radius' = PASSWORD('radpass');
+
+# Need to read when running RADIUS and delete for cleanup
+GRANT ALL ON radius.* TO 'radius';
diff --git a/src/tests/salt-test-server/salt/ntp.sls b/src/tests/salt-test-server/salt/ntp.sls
new file mode 100644 (file)
index 0000000..66434ff
--- /dev/null
@@ -0,0 +1,22 @@
+UTC:
+  timezone.system
+
+ntp_daemon:
+    # Make sure ntp is installed and running
+    pkg:
+{% if grains['os'] == 'CentOS' or grains['os'] == 'Ubuntu' or grains['os'] == 'Debian' %}
+        - name: ntp
+{% elif grains['os'] == 'FreeBSD' %}
+        - name: openntpd
+{% endif %}
+        - installed
+
+# Make sure ntpd is running and enabled (start on boot)
+{% if grains['os'] == 'CentOS' or grains['os'] == 'FreeBSD' %}
+ntpd:
+{% elif grains['os'] == 'Ubuntu' or grains['os'] == 'Debian' %}
+ntp:
+{% endif %}
+    service:
+        - running
+        - enable: True
diff --git a/src/tests/salt-test-server/salt/postgres.sls b/src/tests/salt-test-server/salt/postgres.sls
new file mode 100644 (file)
index 0000000..26fc4e6
--- /dev/null
@@ -0,0 +1,71 @@
+postgresql:
+    # Install postgres and make sure it is running and starts on boot
+    pkg:
+        - installed
+    # Only try to start service after DB has been initialized (will fail otherwise)
+    service:
+        - name: postgresql
+        - running
+        - enable: True
+
+postgres_set_interface:
+    file.sed:
+        - name: /etc/postgresql/9.4/main/postgresql.conf
+        - before: ^\#listen_addresses = 'localhost'
+        - after: listen_addresses = '*'
+
+postgres_password_auth:
+    # Add authentication from anywhere
+    file.append:
+        - name: /etc/postgresql/9.4/main/pg_hba.conf
+        - text:
+            - host    radius      radius        0.0.0.0/0            md5
+
+postgres_restart:
+    # Restart postgres after changes to the config file (reload isn't enough)
+    cmd.wait:
+        - cwd: /
+        - name: service postgresql restart
+        - require:
+            - pkg: postgresql
+            - file: postgres_password_auth
+            - file: postgres_set_interface
+        - watch:
+            - file: /etc/postgresql/9.4/main/postgresql.conf
+            - file: /etc/postgresql/9.4/main/pg_hba.conf
+
+# Copy DB schema file
+/salt/postgres/schema.sql:
+    file.managed:
+        - source: salt://postgres/schema.sql
+        - makedirs: true
+
+# Copy DB setup script
+/salt/postgres/setup.sql:
+    file.managed:
+        - source: salt://postgres/setup.sql
+        - makedirs: true
+
+# Create DB
+create_db:
+    cmd.run:
+        - cwd: /
+        - name: createdb radius
+        - user: postgres
+        - unless: psql --list | grep radius
+
+# Create FreeRADIUS schema
+psql radius < /salt/postgres/schema.sql:
+    cmd.run:
+        - user: postgres
+        - unless: "echo '\\dt public.*' | psql radius | grep radacct;"
+        - require:
+            - file: /salt/postgres/schema.sql
+
+# Setup DB access
+psql radius < /salt/postgres/setup.sql:
+    cmd.run:
+        - user: postgres
+        - unless: "echo '\\du' | psql radius | grep radius"
+        - require:
+            - file: /salt/postgres/setup.sql
diff --git a/src/tests/salt-test-server/salt/postgres/schema.sql b/src/tests/salt-test-server/salt/postgres/schema.sql
new file mode 100644 (file)
index 0000000..c94ee9e
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * $Id$
+ *
+ * Postgresql schema for FreeRADIUS
+ *
+ * All field lengths need checking as some are still suboptimal. -pnixon 2003-07-13
+ *
+ */
+
+/*
+ * Table structure for table 'radacct'
+ *
+ * Note: Column type bigserial does not exist prior to Postgres 7.2
+ *       If you run an older version you need to change this to serial
+ */
+CREATE TABLE radacct (
+       RadAcctId               bigserial PRIMARY KEY,
+       AcctSessionId           text NOT NULL,
+       AcctUniqueId            text NOT NULL UNIQUE,
+       UserName                text,
+       GroupName               text,
+       Realm                   text,
+       NASIPAddress            inet NOT NULL,
+       NASPortId               text,
+       NASPortType             text,
+       AcctStartTime           timestamp with time zone,
+       AcctUpdateTime          timestamp with time zone,
+       AcctStopTime            timestamp with time zone,
+       AcctInterval            bigint,
+       AcctSessionTime         bigint,
+       AcctAuthentic           text,
+       ConnectInfo_start       text,
+       ConnectInfo_stop        text,
+       AcctInputOctets         bigint,
+       AcctOutputOctets        bigint,
+       CalledStationId         text,
+       CallingStationId        text,
+       AcctTerminateCause      text,
+       ServiceType             text,
+       FramedProtocol          text,
+       FramedIPAddress         inet
+);
+-- This index may be useful..
+-- CREATE UNIQUE INDEX radacct_whoson on radacct (AcctStartTime, nasipaddress);
+
+-- For use by update-, stop- and simul_* queries
+CREATE INDEX radacct_active_session_idx ON radacct (AcctUniqueId) WHERE AcctStopTime IS NULL;
+
+-- Add if you you regularly have to replay packets
+-- CREATE INDEX radacct_session_idx ON radacct (AcctUniqueId);
+
+-- For backwards compatibility
+-- CREATE INDEX radacct_active_user_idx ON radacct (AcctSessionId, UserName, NASIPAddress) WHERE AcctStopTime IS NULL;
+
+-- For use by onoff-
+CREATE INDEX radacct_bulk_close ON radacct (NASIPAddress, AcctStartTime) WHERE AcctStopTime IS NULL;
+
+-- and for common statistic queries:
+CREATE INDEX radacct_start_user_idx ON radacct (AcctStartTime, UserName);
+-- and, optionally
+-- CREATE INDEX radacct_stop_user_idx ON radacct (acctStopTime, UserName);
+
+/*
+ * There was WAAAY too many indexes previously. This combo index
+ * should take care of the most common searches.
+ * I have commented out all the old indexes, but left them in case
+ * someone wants them. I don't recomend anywone use them all at once
+ * as they will slow down your DB too much.
+ *  - pnixon 2003-07-13
+ */
+
+/*
+ * create index radacct_UserName on radacct (UserName);
+ * create index radacct_AcctSessionId on radacct (AcctSessionId);
+ * create index radacct_AcctUniqueId on radacct (AcctUniqueId);
+ * create index radacct_FramedIPAddress on radacct (FramedIPAddress);
+ * create index radacct_NASIPAddress on radacct (NASIPAddress);
+ * create index radacct_AcctStartTime on radacct (AcctStartTime);
+ * create index radacct_AcctStopTime on radacct (AcctStopTime);
+*/
+
+
+
+/*
+ * Table structure for table 'radcheck'
+ */
+CREATE TABLE radcheck (
+       id                      serial PRIMARY KEY,
+       UserName                text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '==',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radcheck_UserName on radcheck (UserName,Attribute);
+/*
+ * Use this index if you use case insensitive queries
+ */
+-- create index radcheck_UserName_lower on radcheck (lower(UserName),Attribute);
+
+/*
+ * Table structure for table 'radgroupcheck'
+ */
+CREATE TABLE radgroupcheck (
+       id                      serial PRIMARY KEY,
+       GroupName               text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '==',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radgroupcheck_GroupName on radgroupcheck (GroupName,Attribute);
+
+/*
+ * Table structure for table 'radgroupreply'
+ */
+CREATE TABLE radgroupreply (
+       id                      serial PRIMARY KEY,
+       GroupName               text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '=',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radgroupreply_GroupName on radgroupreply (GroupName,Attribute);
+
+/*
+ * Table structure for table 'radreply'
+ */
+CREATE TABLE radreply (
+       id                      serial PRIMARY KEY,
+       UserName                text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '=',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radreply_UserName on radreply (UserName,Attribute);
+/*
+ * Use this index if you use case insensitive queries
+ */
+-- create index radreply_UserName_lower on radreply (lower(UserName),Attribute);
+
+/*
+ * Table structure for table 'radusergroup'
+ */
+CREATE TABLE radusergroup (
+       id                      serial PRIMARY KEY,
+       UserName                text NOT NULL DEFAULT '',
+       GroupName               text NOT NULL DEFAULT '',
+       priority                integer NOT NULL DEFAULT 0
+);
+create index radusergroup_UserName on radusergroup (UserName);
+/*
+ * Use this index if you use case insensitive queries
+ */
+-- create index radusergroup_UserName_lower on radusergroup (lower(UserName));
+
+--
+-- Table structure for table 'radpostauth'
+--
+
+CREATE TABLE radpostauth (
+       id                      bigserial PRIMARY KEY,
+       username                text NOT NULL,
+       pass                    text,
+       reply                   text,
+       CalledStationId         text,
+       CallingStationId        text,
+       authdate                timestamp with time zone NOT NULL default now()
+);
+
+/*
+ * Table structure for table 'nas'
+ */
+CREATE TABLE nas (
+       id                      serial PRIMARY KEY,
+       nasname                 text NOT NULL,
+       shortname               text NOT NULL,
+       type                    text NOT NULL DEFAULT 'other',
+       ports                   integer,
+       secret                  text NOT NULL,
+       server                  text,
+       community               text,
+       description             text
+);
+create index nas_nasname on nas (nasname);
diff --git a/src/tests/salt-test-server/salt/postgres/setup.sql b/src/tests/salt-test-server/salt/postgres/setup.sql
new file mode 100644 (file)
index 0000000..6b41aa1
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * admin.sql -- PostgreSQL commands for creating the RADIUS user.
+ *
+ *     WARNING: You should change 'localhost' and 'radpass'
+ *              to something else.  Also update raddb/sql.conf
+ *              with the new RADIUS password.
+ *
+ *     WARNING: This example file is untested.  Use at your own risk.
+ *              Please send any bug fixes to the mailing list.
+ *
+ *     $Id$
+ */
+
+/*
+ *  Create default administrator for RADIUS
+ */
+CREATE USER radius WITH PASSWORD 'radpass';
+
+/* radius user needs ti clean tables in test env */
+GRANT ALL ON ALL TABLES IN SCHEMA public TO radius;
+GRANT SELECT, USAGE ON ALL SEQUENCES IN schema public TO radius;
diff --git a/src/tests/salt-test-server/salt/top.sls b/src/tests/salt-test-server/salt/top.sls
new file mode 100644 (file)
index 0000000..efba703
--- /dev/null
@@ -0,0 +1,7 @@
+base:
+  'test-server':
+    - ntp
+    - mysql
+    - postgres
+    - ldap
+    - iptable
diff --git a/src/tests/salt-test-server/salt_config/master b/src/tests/salt-test-server/salt_config/master
new file mode 100644 (file)
index 0000000..257396a
--- /dev/null
@@ -0,0 +1,12 @@
+root_dir: .
+# pki_dir and cachedir are prefixed with root_dir
+pki_dir: /tmp/
+cachedir: /cache/
+file_roots:
+  base:
+    # salt directory in current working directory
+    - salt
+pillar_roots:
+  base:
+    # pillar directory in current working directory
+    - pillar
diff --git a/src/tests/salt-test-server/salt_config/roster b/src/tests/salt-test-server/salt_config/roster
new file mode 100644 (file)
index 0000000..8958bd1
--- /dev/null
@@ -0,0 +1,4 @@
+test-server:
+    host: 192.168.2.132
+    user: root
+    passwd: root