Repository: privacyidea/privacyidea

Issue:
Audit object not initialized on login route
The Audit object for the current request is not initialized for the login route:
https://github.com/privacyidea/privacyidea/blob/88c36350fb9bded7ea97255e22cea6fac477dee2/privacyidea/webui/login.py#L57

When checking whether remote users are allowed:
https://github.com/privacyidea/privacyidea/blob/4d59eab2941308f742927851e3e4a45eee7ae739/privacyidea/api/lib/prepolicy.py#L1213
the `action_values()` function fails when trying to write to the audit log:
https://github.com/privacyidea/privacyidea/blob/d6fd3f83de5354c762fc1b8110179e1821302041/privacyidea/lib/policy.py#L2366



Discussion:
I think the audit object was probably not initialized on purpose, to avoid cluttering the audit log as soon as the login form is called (since it is actually not part of the REST API).

So we need to check, how we actually want to deal with that. 

Currently to my understanding ``is_remote_user_allowed`` would need to be able to run without a user, since at this point, actually no user has authenticated.
Correct, `is_remote_user_allowed()` just checks if the policy is set. We can add `write_to_audit_log=False` to the parameters of `action_values()` to avoid the error.
I was thinking that the `audit_object` wasn't initialized on purpose...


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
@@ -1197,13 +1197,15 @@ def mock_fail(req, action):
     raise Exception("This is an Exception in an external check function")
 
 
-def is_remote_user_allowed(req):
+def is_remote_user_allowed(req, write_to_audit_log=True):
     """
     Checks if the REMOTE_USER server variable is allowed to be used.
 
     .. note:: This is not used as a decorator!
 
     :param req: The flask request, containing the remote user and the client IP
+    :param write_to_audit_log: whether the policy name should be added to the audit log entry
+    :type write_to_audit_log: bool
     :return: a bool value
     """
     res = False
@@ -1213,7 +1215,8 @@ def is_remote_user_allowed(req):
         ruser_active = Match.generic(g, scope=SCOPE.WEBUI,
                                      action=ACTION.REMOTE_USER,
                                      user=loginname,
-                                     realm=realm).action_values(unique=False)
+                                     realm=realm).action_values(unique=False,
+                                                                write_to_audit_log=write_to_audit_log)
         # there should be only one action value here
         if ruser_active:
             if list(ruser_active)[0] == REMOTE_USER.ACTIVE:
diff --git a/privacyidea/webui/login.py b/privacyidea/webui/login.py
--- a/privacyidea/webui/login.py
+++ b/privacyidea/webui/login.py
@@ -114,7 +114,7 @@ def single_page_application():
             realms = ",".join(get_realms())
 
     try:
-        if is_remote_user_allowed(request):
+        if is_remote_user_allowed(request, write_to_audit_log=False):
             remote_user = request.remote_user
         password_reset = is_password_reset(g)
         hsm_ready = True


Test patch:
diff --git a/tests/test_ui_login.py b/tests/test_ui_login.py
--- a/tests/test_ui_login.py
+++ b/tests/test_ui_login.py
@@ -6,7 +6,7 @@
 implementation is contained webui/login.py
 """
 from .base import MyTestCase
-from privacyidea.lib.policy import set_policy, SCOPE, ACTION
+from privacyidea.lib.policy import set_policy, SCOPE, ACTION, PolicyClass, delete_all_policies
 from privacyidea.lib.utils import to_unicode
 import re
 
@@ -64,3 +64,23 @@ def test_05_custom_login_text(self):
             self.assertTrue(res.status_code == 200, res)
             self.assertTrue(b"Go for it!" in res.data)
 
+    def test_06_remote_user(self):
+        delete_all_policies()
+        # test login when no policies are set
+        self.assertEqual(len(PolicyClass().policies), 0, PolicyClass().policies)
+        with self.app.test_request_context('/',
+                                           method='GET',
+                                           environ_base={'REMOTE_USER': 'foo'}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            self.assertTrue(b"<input type=hidden id=REMOTE_USER value=\"\">" in res.data)
+
+        # test login with remote_user policy set
+        set_policy("remote_user", scope=SCOPE.WEBUI,
+                   action="{0!s}=allowed".format(ACTION.REMOTE_USER))
+        with self.app.test_request_context('/',
+                                           method='GET',
+                                           environ_base={'REMOTE_USER': 'foo'}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            self.assertTrue(b"<input type=hidden id=REMOTE_USER value=\"foo\">" in res.data)