Repository: privacyidea/privacyidea

Issue:
Testing token fails with setrealm policy present
### Top-level intent

Test a token in the UI.
Testing a token in the Web UI triggers a request like

    https://privacyidea/validate/check
    serial=PISM1234
    pass=1234

When a ``setrealm`` policy is set, than the request will be modified to

    https://privacyidea/validate/check
    serial=PISM1234
    pass=1234
    realm=somerealm

This will result in an error "user can not be found in the resolver", since privacyidea thinks there is a user object ``User(login="", realm="somerealm")`` and can not find an empty-string-user.

### Steps to reproduce

s.o.

### Expected outcome

Testing the token should work ;-)

### Configuration

This only happens when a setrealm-policy is active.

# Possible ideas

I am currently not sure how to address this, if we should

1. find the user and fill in the user
2. only do the setrealm, if a user is contained
3. ignore the ``realm`` if a serial is given.



Discussion:
The set_realm policy is only used with /validate/check and /validate/samlcheck. If we have no user in the original request, we will end up with a broken user like ``User(login="", realm="newrealm").
This aboslutely does not make any sense.

Therefor we change the decorater ``set_realm`` to be totally ignored, if we have no loginname!

Resolving patch:
diff --git a/privacyidea/api/lib/prepolicy.py b/privacyidea/api/lib/prepolicy.py
--- a/privacyidea/api/lib/prepolicy.py
+++ b/privacyidea/api/lib/prepolicy.py
@@ -736,24 +736,20 @@ def set_realm(request=None, action=None):
     # At the moment a realm parameter with no user parameter returns a user
     # object like "@realm". If this is changed one day, we need to also fetch
     #  the realm
-    if request.User:
+    if request.User and request.User.login:
         user_object = request.User
         username = user_object.login
         policy_match = Match.user(g, scope=SCOPE.AUTHZ, action=ACTION.SETREALM,
                                   user_object=user_object)
-    else:  # pragma: no cover
-        username = None
-        policy_match = Match.realm(g, scope=SCOPE.AUTHZ, action=ACTION.SETREALM,
-                                   realm=request.all_data.get("realm"))
-    new_realm = policy_match.action_values(unique=False)
-    if len(new_realm) > 1:
-        raise PolicyError("I do not know, to which realm I should set the "
-                          "new realm. Conflicting policies exist.")
-    elif len(new_realm) == 1:
-        # There is one specific realm, which we set in the request
-        request.all_data["realm"] = list(new_realm)[0]
-        # We also need to update the user
-        request.User = User(username, request.all_data["realm"])
+        new_realm = policy_match.action_values(unique=False)
+        if len(new_realm) > 1:
+            raise PolicyError("I do not know, to which realm I should set the "
+                              "new realm. Conflicting policies exist.")
+        elif len(new_realm) == 1:
+            # There is one specific realm, which we set in the request
+            request.all_data["realm"] = list(new_realm)[0]
+            # We also need to update the user
+            request.User = User(username, request.all_data["realm"])
 
     return True
 


Test patch:
diff --git a/tests/test_api_lib_policy.py b/tests/test_api_lib_policy.py
--- a/tests/test_api_lib_policy.py
+++ b/tests/test_api_lib_policy.py
@@ -470,11 +470,17 @@ def test_06_set_realm(self):
         g.policy_object = PolicyClass()
 
         # request, that matches the policy
-        req.all_data = {"realm": "somerealm"}
-        req.User = None
+        req.all_data = {}
+        req.User = User()
+        set_realm(req)
+        # Check, that the realm was not set, since there was no user in the request
+        self.assertEqual(req.all_data.get("realm"), None)
+
+        req.all_data = {}
+        req.User = User(login="cornelius", realm="somerealm")
         set_realm(req)
         # Check, if the realm was modified to the realm specified in the policy
-        self.assertTrue(req.all_data.get("realm") == self.realm1)
+        self.assertEqual(req.all_data.get("realm"), self.realm1)
 
         # A request, that does not match the policy:
         req.all_data = {"realm": "otherrealm"}
@@ -491,7 +497,7 @@ def test_06_set_realm(self):
         g.policy_object = PolicyClass()
         # This request will trigger two policies with different realms to set
         req.all_data = {"realm": "somerealm"}
-        req.User = None
+        req.User = User(login="cornelius", realm="somerealm")
         self.assertRaises(PolicyError, set_realm, req)
 
         # finally delete policy
diff --git a/tests/test_api_validate.py b/tests/test_api_validate.py
--- a/tests/test_api_validate.py
+++ b/tests/test_api_validate.py
@@ -249,6 +249,39 @@ def test_03_classic_policies(self):
             self.assertTrue(result.get("status"))
             self.assertTrue(result.get("value"))
 
+    def test_04_testing_token_with_setrealm(self):
+        """
+        When testing a token in the UI, the following request is triggered.
+        https://privacyidea/validate/check
+        serial=PISM1234
+        pass=1234
+
+        If we have a setrealm policy, this request is triggered:
+        https://privacyidea/validate/check
+        serial=PISM1234
+        pass=1234
+        realm=newrealm
+        """
+        init_token({"serial": "SPASS_04", "type": "spass", "pin": "1234"})
+
+        set_policy(name="pol_setrealm_01",
+                   scope=SCOPE.AUTHZ,
+                   action="{0!s}={1!s}".format(ACTION.SETREALM, self.realm1))
+
+        # Successfully test the token
+        with self.app.test_request_context('/validate/check',
+                                           method='POST',
+                                           data={"serial": "SPASS_04",
+                                                 "pass": "1234"}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            result = res.json.get("result")
+            self.assertTrue(result.get("status"))
+            self.assertTrue(result.get("value"))
+
+        delete_policy("pol_setrealm_01")
+        remove_token("SPASS_04")
+
 
 class DisplayTANTestCase(MyApiTestCase):