Repository: privacyidea/privacyidea

Issue:
RADIUS Server Config: "Send test RADIUS request" does not work
On current privacyIDEA master (c8cb96c74b97815339914a92fbc94b43a97d788a), the "Send test RADIUS request" does not work even though the configuration is correct, because it always uses the secret "FAILED TO DECRYPT PASSWORD". In addition, the WebUI does not display an error message if the test RADIUS request fails.


Discussion:

Resolving patch:
diff --git a/privacyidea/api/radiusserver.py b/privacyidea/api/radiusserver.py
--- a/privacyidea/api/radiusserver.py
+++ b/privacyidea/api/radiusserver.py
@@ -36,7 +36,7 @@
 from flask import g
 import logging
 from privacyidea.lib.radiusserver import (add_radius, RADIUSServer,
-                                          delete_radius, get_radiusservers)
+                                          delete_radius, get_radiusservers, test_radius)
 from privacyidea.models import RADIUSServer as RADIUSServerDB
 from privacyidea.api.auth import admin_required
 
@@ -149,11 +149,8 @@ def test():
     dictionary = getParam(param, "dictionary",
                           default="/etc/privacyidea/dictionary")
 
-    s = RADIUSServerDB(identifier=identifier, server=server, port=port,
-                       secret=secret, dictionary=dictionary,
-                       retries=retries, timeout=timeout)
-    r = RADIUSServer.request(s, user, password)
-
+    r = test_radius(identifier, server, secret, user, password, port=port,
+                    dictionary=dictionary, retries=retries, timeout=timeout)
     g.audit_object.log({'success': r > 0,
                         'info':  r})
     return send_result(r > 0)
diff --git a/privacyidea/lib/radiusserver.py b/privacyidea/lib/radiusserver.py
--- a/privacyidea/lib/radiusserver.py
+++ b/privacyidea/lib/radiusserver.py
@@ -206,6 +206,37 @@ def add_radius(identifier, server, secret, port=1812, description="",
     return r
 
 
+@log_with(log)
+def test_radius(identifier, server, secret, user, password, port=1812, description="",
+               dictionary='/etc/privacyidea/dictionary', retries=3, timeout=5):
+    """
+    This tests a RADIUS server configuration by sending an access request.
+
+    :param identifier: The identifier or the name of the RADIUSServer definition
+    :type identifier: basestring
+    :param server: The FQDN or IP address of the RADIUS server
+    :type server: basestring
+    :param secret: The RADIUS secret
+    :param user: the username to send
+    :param password: the password to send
+    :param port: the radius port
+    :type port: int
+    :param description: Human readable description of the RADIUS server
+        definition
+    :param dictionary: The RADIUS dictionary
+    :return: The result of the access request
+    """
+    cryptedSecret = encryptPassword(secret)
+    if len(cryptedSecret) > 255:
+        raise privacyIDEAError(description=_("The RADIUS secret is too long"),
+                               id=2234)
+    s = RADIUSServerDB(identifier=identifier, server=server, port=port,
+                       secret=cryptedSecret, dictionary=dictionary,
+                       retries=retries, timeout=timeout,
+                       description=description)
+    return RADIUSServer.request(s, user, password)
+
+
 @log_with(log)
 def delete_radius(identifier):
     """


Test patch:
diff --git a/tests/test_lib_radiusserver.py b/tests/test_lib_radiusserver.py
--- a/tests/test_lib_radiusserver.py
+++ b/tests/test_lib_radiusserver.py
@@ -2,15 +2,18 @@
 This test file tests the lib/radiusserver.py
 """
 from .base import MyTestCase
-from privacyidea.lib.error import ConfigAdminError
+from privacyidea.lib.error import ConfigAdminError, privacyIDEAError
 from privacyidea.lib.radiusserver import (add_radius, delete_radius,
                                           get_radiusservers, get_radius,
-                                          RADIUSServer)
+                                          RADIUSServer, test_radius)
 from privacyidea.lib.config import set_privacyidea_config
 import radiusmock
 DICT_FILE = "tests/testdata/dictionary"
 
 
+# prevent nosetests to run test_radius as a unittest
+test_radius.__test__ = False
+
 class RADIUSServerTestCase(MyTestCase):
 
     def test_01_create_radius(self):
@@ -75,3 +78,24 @@ def test_05_RADIUS_request(self):
         # A timeout will return false
         r = RADIUSServer.request(radius.config, "user", "password")
         self.assertEqual(r, False)
+
+    @radiusmock.activate
+    def test_06_test_radius(self):
+        radiusmock.setdata(success=False)
+        r = test_radius(identifier="myserver", server="1.2.3.4",
+                        user="user", password="password",
+                        secret="testing123", dictionary=DICT_FILE)
+        self.assertFalse(r)
+
+        radiusmock.setdata(success=True)
+        r = test_radius(identifier="myserver", server="1.2.3.4",
+                        user="user", password="password",
+                        secret="testing123", dictionary=DICT_FILE)
+        self.assertTrue(r)
+
+        # raises error on long secrets
+        self.assertRaises(privacyIDEAError,
+                          test_radius,
+                          identifier="myserver", server="1.2.3.4",
+                          user="user", password="password",
+                          secret="x" * 96, dictionary=DICT_FILE)