]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add tests of rlm_rest
authorNick Porter <nick@portercomputing.co.uk>
Fri, 10 Feb 2023 17:01:41 +0000 (17:01 +0000)
committerNick Porter <nick@portercomputing.co.uk>
Mon, 13 Feb 2023 15:00:45 +0000 (15:00 +0000)
.github/workflows/ci.yml
src/tests/modules/rest/all.mk [new file with mode: 0644]
src/tests/modules/rest/module.conf [new file with mode: 0644]
src/tests/modules/rest/rest_module.attrs [new file with mode: 0644]
src/tests/modules/rest/rest_module.unlang [new file with mode: 0644]
src/tests/modules/rest/rest_xlat.attrs [new file with mode: 0644]
src/tests/modules/rest/rest_xlat.unlang [new file with mode: 0644]

index 3cd8fff002b65abf9b36d33fd1835d01990c1206..c39eb7abc76cf3525fe32711b5eeeeac2ce391cc 100644 (file)
@@ -15,6 +15,9 @@ env:
   APT_OPTS: "-y --no-install-recommends"
   LDAP_TEST_SERVER: 127.0.0.1
   LDAP_TEST_SERVER_PORT: 3890
+  REST_TEST_SERVER: 127.0.0.1
+  REST_TEST_SERVER_PORT: 8080
+  REST_TEST_SERVER_SSL_PORT: 8443
 #
 # Update between running in a container (GitHub runner and
 # self-hosted runner), or directly on the VM (currently GitHub
diff --git a/src/tests/modules/rest/all.mk b/src/tests/modules/rest/all.mk
new file mode 100644 (file)
index 0000000..e2a6133
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Test the "rest" module
+#
+
+# Don't test smtp if REST_TEST_SERVER ENV is not set
+rest_require_test_server := 1
diff --git a/src/tests/modules/rest/module.conf b/src/tests/modules/rest/module.conf
new file mode 100644 (file)
index 0000000..ac16109
--- /dev/null
@@ -0,0 +1,45 @@
+#rest unit test config
+json {
+}
+
+rest {
+       tls {
+               ca_info_file = "$ENV{top_srcdir}raddb/restcerts/ca.pem"
+
+               private_key_password = "whatever"
+
+               random_file = /dev/urandom
+
+               check_cert_cn = no
+       }
+
+       connect_uri = "http://$ENV{REST_TEST_SERVER}:$ENV{REST_TEST_SERVER_PORT}/"
+
+       xlat {
+               body_uri_encode = no
+               tls = ${..tls}
+       }
+
+       authorize {
+               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=authorize"
+               method = "GET"
+               tls = ${..tls}
+       }
+
+       authenticate {
+               uri = "https://$ENV{REST_TEST_SERVER}:$ENV{REST_TEST_SERVER_SSL_PORT}/auth?section=authenticate"
+               method = "POST"
+               tls = ${..tls}
+               body = 'post'
+               data = 'user=%{User-Name}'
+               auth = 'basic'
+       }
+
+       accounting {
+               uri = "https://$ENV{REST_TEST_SERVER}:$ENV{REST_TEST_SERVER_SSL_PORT}/user/%{User-Name}/mac/%{Called-Station-ID}?action=post-auth&section=accounting"
+               method = 'POST'
+               body = 'json'
+               data = '{"NAS": "%{NAS-IP-Address}", "Password": "%{User-Password}", "Verify": true}'
+               tls = ${..tls}
+       }
+}
diff --git a/src/tests/modules/rest/rest_module.attrs b/src/tests/modules/rest/rest_module.attrs
new file mode 100644 (file)
index 0000000..3ecf895
--- /dev/null
@@ -0,0 +1,14 @@
+#
+#  Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'Bob'
+User-Password = 'Saget'
+Called-Station-Id = 'aa:bb:cc:dd:ee:ff'
+NAS-IP-Address = '192.168.1.1'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+
diff --git a/src/tests/modules/rest/rest_module.unlang b/src/tests/modules/rest/rest_module.unlang
new file mode 100644 (file)
index 0000000..c78c380
--- /dev/null
@@ -0,0 +1,111 @@
+# Pre-set Tmp-String-2 to check correct operator behaviour
+update {
+       &control:Tmp-String-2 := "foo"
+}
+
+# Test "authorize" rest call.  Uses http to a GET end point
+rest
+
+debug_control
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-0 == "authorize")) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-1 == "GET")) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-1[*] == "/user/<username>/mac/<client>")) {
+       test_fail
+}
+
+if (!(&control:User-Name == "Bob")) {
+       test_fail
+}
+
+# The "op" for setting Tmp-String-2 is ^=
+if (!(&control:Tmp-String-2[0] == "Bob") || !(&control:Tmp-String-2[1] == "foo")) {
+       test_fail
+}
+
+# Reset control attributes
+update control {
+       &Tmp-String-0[*] !* ANY
+       &Tmp-String-1[*] !* ANY
+       &User-Name[*] !* ANY
+}
+
+# Pre-fill NAS-IP-Address to check operator behaviour
+update {
+       &control:NAS-IP-Address := "10.0.0.10"
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# Test "accounting" rest call.  Uses https to a POST end point
+rest.accounting
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-0 == "accounting")) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-1 == "POST")) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-1[*] == "/user/<username>/mac/<client>")) {
+       test_fail
+}
+
+if (!(&control:User-Name == "Bob")) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-2[0] == "Bob") || !(&control:Tmp-String-2[1] == "Bob") || !(&control:Tmp-String-2[2] == "foo")) {
+       test_fail
+}
+
+# NAS IP Address is passed in body data
+if (!(&control:NAS-IP-Address[0] == "10.0.0.10") || !(&control:NAS-IP-Address[1] == "192.168.1.1")) {
+       test_fail
+}
+
+debug_control
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# Test "authenticate" rest call.  Uses http basic authentication
+rest.authenticate
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&reply:REST-HTTP-Body == "Section: authenticate, User: Bob, Authenticated: true\n")) {
+       test_fail
+}
+
+# Clear up reply
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+test_pass
diff --git a/src/tests/modules/rest/rest_xlat.attrs b/src/tests/modules/rest/rest_xlat.attrs
new file mode 100644 (file)
index 0000000..6370197
--- /dev/null
@@ -0,0 +1,18 @@
+#
+#  Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'Bob'
+User-Password = 'Saget'
+Called-Station-Id = 'aa:bb:cc:dd:ee:ff'
+NAS-IP-Address = '192.168.1.1'
+Login-IP-Host = 127.0.0.1
+NAS-Port = 8080
+Calling-Station-Id = 'dummy&unsafe=escaped'
+Tmp-String-9 = ''
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+
diff --git a/src/tests/modules/rest/rest_xlat.unlang b/src/tests/modules/rest/rest_xlat.unlang
new file mode 100644 (file)
index 0000000..861364f
--- /dev/null
@@ -0,0 +1,191 @@
+#
+# PRE rest_module eval
+#
+# Largely a back port of the rlm_rest tests from v4, with v4 specific functionality removed.
+#
+
+update {
+       &Tmp-String-0 := "$ENV{REST_TEST_SERVER}"
+       &Tmp-Integer-0 := "$ENV{REST_TEST_SERVER_PORT}"
+       &Tmp-Integer-1 := "$ENV{REST_TEST_SERVER_SSL_PORT}"
+       &Tmp-String-1 := "notfound"
+}
+
+# Retrieve a plain text file
+update {
+       &control:Tmp-String-1 := "%{rest:GET http://%{Tmp-String-0}:%{Tmp-Integer-0}/test.txt}"
+}
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&control:Tmp-String-1 == "Sample text response\n")) {
+       test_fail
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# Take host from incomming packet
+update {
+       &control:Tmp-String-1 := "%{rest:http://%{Login-IP-Host}:%{Tmp-Integer-0}/test.txt}"
+}
+
+if (!(&reply:REST-HTTP-Status-Code == 200) || !(&control:Tmp-String-1 == "Sample text response\n")) {
+       test_fail
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# Check a "not found" gives a 404 status code
+update {
+       &control:Tmp-String-1 := "%{rest:GET http://%{Tmp-String-0}:%{Tmp-Integer-0}/%{Tmp-String-1}}"
+}
+
+if (!(&reply:REST-HTTP-Status-Code == 404)) {
+       test_fail
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# GET with URL parameters
+update {
+       &Tmp-String-2 := "%{rest:GET http://%{Tmp-String-0}:%{Tmp-Integer-0}/user/%{User-Name}/mac/%{Called-Station-Id}}"
+}
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&Tmp-String-2 =~ /"control:Tmp-String-1":\["GET","\\\/user\\\/<username>\\\/mac\\\/<client>"\]/)) {
+       test_fail
+}
+
+if (&Tmp-String-2 =~ /"control:User-Name":\{([^}]+)\}/) {
+       if (!("%{1}" =~ /"value":"Bob"/)) {
+               test_fail
+       }
+} else {
+       test_fail
+}
+
+update {
+       &control:Tmp-String-3 := 'dummy'
+}
+
+update {
+       &control:Tmp-String-2 = "%{json_encode:&NAS-IP-Address}"
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# POST to https with JSON body data
+update {
+       &Tmp-String-2 := "%{rest:POST https://%{Tmp-String-0}:%{Tmp-Integer-1}/user/%{User-Name}/mac/%{Called-Station-Id}?section=accounting %{control:Tmp-String-2}}"
+}
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&Tmp-String-2 =~ /"control:Tmp-String-1":\["POST","\\\/user\\\/<username>\\\/mac\\\/<client>"\]/)) {
+       test_fail
+}
+
+if (&Tmp-String-2 =~ /"control:User-Name":\{([^}]+)\}/) {
+       if (!("%{1}" =~ /"value":"Bob"/)) {
+               test_fail
+       }
+} else {
+       test_fail
+}
+
+update {
+       &control:Tmp-String-2 := "NAS=%{NAS-IP-Address}&user=%{User-Name}"
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# POST to https with POST body data
+update {
+       &Tmp-String-2 := "%{rest:POST https://%{Tmp-String-0}:%{Tmp-Integer-1}/post/test?section=dummy %{control:Tmp-String-2}}"
+}
+
+if (!(&reply:REST-HTTP-Status-Code == 200)) {
+       test_fail
+}
+
+if (!(&Tmp-String-2 == "Section: dummy, User: Bob\n")) {
+       test_fail
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# URI with tainted values in the arguments - input argument includes URI argument
+# separator - make sure this doesn't end up generating extra arguments, but gets escaped.
+update {
+       &Tmp-String-2 := "%{rest:GET http://%{Tmp-String-0}:%{Tmp-Integer-0}/user/%{User-Name}/reflect/?station=%{Calling-Station-Id}}"
+}
+
+if (!(&Tmp-String-2 == "{\"station\":\"dummy&unsafe=escaped\"}\n" )) {
+       test_fail
+}
+
+# Zero length untainted value - check parsing doesn't break on zero length string
+update {
+       &Tmp-String-8 := ""
+}
+update {
+       &Tmp-String-2 := "%{rest:http://%{Tmp-String-0}:%{Tmp-Integer-0}/user/%{User-Name}/reflect/%{Tmp-String-8}?station=%{User-Name}}"
+}
+
+if (!(&Tmp-String-2 == "{\"station\":\"Bob\"}\n" )) {
+       test_fail
+}
+
+# Clear previous status code and body
+update reply {
+       &REST-HTTP-Status-Code !* ANY
+       &REST-HTTP-Body !* ANY
+}
+
+# Zero length tainted value - check escaping doesn't break on zero length string
+update {
+       &Tmp-String-2 := "%{rest:http://%{Tmp-String-0}:%{Tmp-Integer-0}/user/%{User-Name}/reflect/%{Tmp-String-9}?station=%{Called-Station-Id}}"
+}
+
+if (!(&Tmp-String-2 == "{\"station\":\"aa:bb:cc:dd:ee:ff\"}\n" )) {
+       test_fail
+}
+
+# Clear previous status code and body
+update reply {
+       REST-HTTP-Status-Code !* ANY
+       REST-HTTP-Body !* ANY
+}
+
+test_pass