Repository: privacyidea/privacyidea

Issue:
Audit log can not be viewed for specific user realms anymore.
### Top-level intent

Have a policy for helpdesk users, who are only allowed to see audit logs from certain realms.

### Steps to reproduce

1. Create admin policy with auditlog and a specific user realm
2. View audit log
3. Admin get the policy error, that he is not allowed to view the audit log

### Expected outcome

Admin should see audit log with only events with the specified realms.

### Actual outcome

Sees nothing.

### Configuration

* **privacyIDEA version**: 3.3 and 3.3.1
* **Installation method**: probably all




Discussion:
I can reproduce this, when changing the tests/test_api_audit.py like this, adding all the paramters, which would also be sent by the webui:

~~~~python
        # check, that we only see allowed audit realms
        with self.app.test_request_context('/audit/',
                                           method='GET',
                                           data={"action": "**",
                                                 "action_detail": "**",
                                                 "administrator": "**",
                                                 "client": "**",
                                                 "date": "**",
                                                 "info": "**",
                                                 "page": "1",
                                                 "page_size": "10",
                                                 "policies":"**",
                                                 "privacyidea_server":"**",
                                                 "realm": "**",
                                                 "resolver": "**",
                                                 "serial": "**",
                                                 "sortorder": "desc",
                                                 "success": "**",
                                                 "tokentype": "**",
                                                 "user": "**"},
                                           headers={'Authorization': helpdesk_authorization}):
~~~~

Will work with 3.2.x but will fail with 3.3.x
In 3.2 the "realm" and "resolver" (and username) are None in the call ``check_base_action`` for the audit log:

~~~~python
    action = policy_object.match_policies(action=action,
                                          user=username,
                                          realm=realm,
                                          scope=scope,
                                          resolver=resolver,
                                          client=g.client_ip,
                                          adminrealm=admin_realm,
                                          active=True,
                                          audit_data=g.audit_object.audit_data)
~~~~

This is, since for the audit log it only check, if the admin is allowed to view the audit log at all.

Thus, we need to empty these values in 3.3 for the audit log.

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
@@ -969,7 +969,13 @@ def check_base_action(request=None, action=None, anonymous=False):
     (role, username, realm, adminuser, adminrealm) = determine_logged_in_userparams(g.logged_in_user, params)
 
     # In certain cases we can not resolve the user by the serial!
-    if action not in [ACTION.AUDIT]:
+    if action is ACTION.AUDIT:
+        # In case of audit requests, the parameters "realm" and "user" are used for
+        # filtering the audit log. So these values must not be taken from the request parameters,
+        # but rather be NONE. The restriction for the allowed realms in the audit log is determined
+        # in the decorator "allowed_audit_realm".
+        realm = username = resolver = None
+    else:
         realm = params.get("realm")
         if type(realm) == list and len(realm) == 1:
             realm = realm[0]
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
 import sys
 
 #VERSION = "2.1dev4"
-VERSION = "3.3.1"
+VERSION = "3.3.2dev1"
 
 # Taken from kennethreitz/requests/setup.py
 package_directory = os.path.realpath(os.path.dirname(__file__))


Test patch:
diff --git a/tests/test_api_audit.py b/tests/test_api_audit.py
--- a/tests/test_api_audit.py
+++ b/tests/test_api_audit.py
@@ -15,6 +15,26 @@
 
 class APIAuditTestCase(MyApiTestCase):
 
+    realm1a = "realm1a"
+    realm2b = "realm2b"
+
+    def setUp_user_realms(self):
+        # create user realm
+        rid = save_resolver({"resolver": self.resolvername1,
+                             "type": "passwdresolver",
+                             "fileName": PWFILE})
+        self.assertTrue(rid > 0, rid)
+
+        (added, failed) = set_realm(self.realm1a,
+                                    [self.resolvername1])
+        self.assertTrue(len(failed) == 0)
+        self.assertTrue(len(added) == 1)
+
+        (added, failed) = set_realm(self.realm2b,
+                                    [self.resolvername1])
+        self.assertTrue(len(failed) == 0)
+        self.assertTrue(len(added) == 1)
+
     def test_00_get_audit(self):
         with self.app.test_request_context('/audit/',
                                            method='GET',
@@ -70,16 +90,16 @@ def test_02_get_allowed_audit_realm(self):
         # Check that an administrator is only allowed to see log entries of
         # the defined realms.
         # fill some audit entries
-        Audit(action="enroll", success=1, realm="realm1a").save()
-        Audit(action="enroll", success=1, realm="realm1a").save()
-        Audit(action="enroll", success=1, realm="realm2b").save()
-        Audit(action="enroll", success=1, realm="realm2b").save()
-        Audit(action="enroll", success=1, realm="realm2b").save()
+        Audit(action="enroll", success=1, realm=self.realm1a).save()
+        Audit(action="enroll", success=1, realm=self.realm1a).save()
+        Audit(action="enroll", success=1, realm=self.realm2b).save()
+        Audit(action="enroll", success=1, realm=self.realm2b).save()
+        Audit(action="enroll", success=1, realm=self.realm2b).save()
 
         # check, that we see all audit entries
         with self.app.test_request_context('/audit/',
                                            method='GET',
-                                           data={"realm": "realm1a"},
+                                           data={"realm": self.realm1a},
                                            headers={'Authorization': self.at}):
             res = self.app.full_dispatch_request()
             self.assertTrue(res.status_code == 200, res)
@@ -103,7 +123,7 @@ def test_02_get_allowed_audit_realm(self):
 
         with self.app.test_request_context('/audit/',
                                            method='GET',
-                                           data={"realm": "realm2b"},
+                                           data={"realm": self.realm2b},
                                            headers={
                                                'Authorization': self.at}):
             res = self.app.full_dispatch_request()
@@ -115,7 +135,7 @@ def test_02_get_allowed_audit_realm(self):
 
         # set policy for audit realms
         set_policy("audit01", scope=SCOPE.ADMIN, action=ACTION.AUDIT,
-                   realm="realm1a")
+                   realm=self.realm1a)
 
         # check, that we only see allowed audit realms
         with self.app.test_request_context('/audit/',
@@ -127,7 +147,7 @@ def test_02_get_allowed_audit_realm(self):
             self.assertTrue(json_response.get("result").get("status"), res)
             # We now have 3 entries, as we added one by the search in line #43
             audit_list = json_response.get("result").get("value").get("auditdata")
-            audit_realms = [a for a in audit_list if a.get("realm") == "realm1a"]
+            audit_realms = [a for a in audit_list if a.get("realm") == self.realm1a]
             self.assertEqual(len(audit_realms), 3)
             self.assertEqual(json_response.get("result").get("value").get("count"), 3)
 
@@ -137,16 +157,16 @@ def test_02_get_allowed_audit_realm(self):
     def test_03_get_allowed_audit_realm(self):
         # Check than an internal admin is allowed to see all realms
         # A helpdesk user in "adminrealm" is only allowerd to see realm1A
-        Audit(action="enroll", success=1, realm="realm1a").save()
-        Audit(action="enroll", success=1, realm="realm1a").save()
-        Audit(action="enroll", success=1, realm="realm2b").save()
-        Audit(action="enroll", success=1, realm="realm2b").save()
-        Audit(action="enroll", success=1, realm="realm2b").save()
+        Audit(action="enroll", success=1, realm=self.realm1a).save()
+        Audit(action="enroll", success=1, realm=self.realm1a).save()
+        Audit(action="enroll", success=1, realm=self.realm2b).save()
+        Audit(action="enroll", success=1, realm=self.realm2b).save()
+        Audit(action="enroll", success=1, realm=self.realm2b).save()
 
         # check, that we see all audit entries
         with self.app.test_request_context('/audit/',
                                            method='GET',
-                                           data={"realm": "realm1a"},
+                                           data={"realm": self.realm1a},
                                            headers={'Authorization': self.at}):
             res = self.app.full_dispatch_request()
             self.assertTrue(res.status_code == 200, res)
@@ -157,7 +177,7 @@ def test_03_get_allowed_audit_realm(self):
 
         with self.app.test_request_context('/audit/',
                                            method='GET',
-                                           data={"realm": "realm2b"},
+                                           data={"realm": self.realm2b},
                                            headers={
                                                'Authorization': self.at}):
             res = self.app.full_dispatch_request()
@@ -170,7 +190,7 @@ def test_03_get_allowed_audit_realm(self):
         # set policy: helpdesk users in adminrealm are only allowed to
         # view "realm1A".
         set_policy("audit01", scope=SCOPE.ADMIN, action=ACTION.AUDIT,
-                   adminrealm="adminrealm", realm="realm1a")
+                   adminrealm="adminrealm", realm=self.realm1a)
         # Test admin is allowed to view unrestricted logs!
         set_policy("audit02", scope=SCOPE.ADMIN, action=ACTION.AUDIT,
                    user="testadmin")
@@ -200,6 +220,23 @@ def test_03_get_allowed_audit_realm(self):
         # check, that we only see allowed audit realms
         with self.app.test_request_context('/audit/',
                                            method='GET',
+                                           data={"action": "**",
+                                                 "action_detail": "**",
+                                                 "administrator": "**",
+                                                 "client": "**",
+                                                 "date": "**",
+                                                 "info": "**",
+                                                 "page": "1",
+                                                 "page_size": "10",
+                                                 "policies": "**",
+                                                 "privacyidea_server": "**",
+                                                 "realm": "**",
+                                                 "resolver": "**",
+                                                 "serial": "**",
+                                                 "sortorder": "desc",
+                                                 "success": "**",
+                                                 "tokentype": "**",
+                                                 "user": "**"},
                                            headers={'Authorization': helpdesk_authorization}):
             res = self.app.full_dispatch_request()
             self.assertTrue(res.status_code == 200, res)
@@ -210,8 +247,8 @@ def test_03_get_allowed_audit_realm(self):
             auditdata = json_response.get("result").get("value").get("auditdata")
             self.assertGreaterEqual(count, 3)
             # All entries are in realm1A!
-            for ad  in auditdata:
-                self.assertEqual(ad.get("realm"), "realm1a")
+            for ad in auditdata:
+                self.assertEqual(ad.get("realm"), self.realm1a)
 
         # Now check, that the testadmin (self.at) sees all entries!
         with self.app.test_request_context('/audit/',
@@ -229,3 +266,97 @@ def test_03_get_allowed_audit_realm(self):
         # delete policy
         delete_policy("audit01")
         delete_policy("audit02")
+
+    def test_04_get_user_audit_log(self):
+        # Test that the user only sees audit log entries with his own data
+        self.setUp_user_realms()
+        # prepare some log entries for the normal user to try to fetch
+        Audit(action="enroll", success=1, user="selfservice", administrator=None,
+              resolver="resolver1", realm=self.realm1a).save()
+        Audit(action="enroll", success=1, user="selfservice", administrator=None,
+              resolver="resolver1", realm=self.realm1a).save()
+        Audit(action="enroll", success=1, user="selfservice", administrator=None,
+              resolver="resolver1", realm=self.realm2b).save()
+        Audit(action="enroll", success=1, user="selfservice", administrator=None,
+              resolver="resolver1", realm=self.realm2b).save()
+        Audit(action="enroll", success=1, user="selfservice", administrator=None,
+              resolver="resolver1", realm=self.realm2b).save()
+
+        # set policy: normal users in realm1a are allowed to view audit log
+        set_policy("audit01", scope=SCOPE.USER, action=ACTION.AUDIT, realm=self.realm1a)
+
+        user_authorization = None
+        with self.app.test_request_context('/auth',
+                                           method='POST', data={'username': 'selfservice@{0!s}'.format(self.realm1a),
+                                                                'password': 'test'}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            json_response = res.json
+            value = json_response.get("result").get("value")
+            self.assertTrue("auditlog" in value.get("rights"))
+            user_authorization = value.get("token")
+
+        # check, that the normal user only sees his own entries
+        with self.app.test_request_context('/audit/',
+                                           method='GET',
+                                           data={"action": "**",
+                                                 "action_detail": "**",
+                                                 "administrator": "**",
+                                                 "client": "**",
+                                                 "date": "**",
+                                                 "info": "**",
+                                                 "page": "1",
+                                                 "page_size": "10",
+                                                 "policies": "**",
+                                                 "privacyidea_server": "**",
+                                                 "realm": "**",
+                                                 "resolver": "**",
+                                                 "serial": "**",
+                                                 "sortorder": "desc",
+                                                 "success": "**",
+                                                 "tokentype": "**",
+                                                 "user": "**"},
+                                           headers={'Authorization': user_authorization}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            json_response = res.json
+            self.assertTrue(json_response.get("result").get("status"), res)
+            # We now have 3 entries, as we added one by the search in line #43
+            count = json_response.get("result").get("value").get("count")
+            auditdata = json_response.get("result").get("value").get("auditdata")
+            self.assertGreaterEqual(count, 3)
+            # All entries are in realm1A!
+            for ad in auditdata:
+                self.assertEqual(ad.get("realm"), self.realm1a)
+
+        # try to explicitly query another realm
+        with self.app.test_request_context('/audit/',
+                                           method='GET',
+                                           data={"action": "**",
+                                                 "action_detail": "**",
+                                                 "administrator": "**",
+                                                 "client": "**",
+                                                 "date": "**",
+                                                 "info": "**",
+                                                 "page": "1",
+                                                 "page_size": "10",
+                                                 "policies": "**",
+                                                 "privacyidea_server": "**",
+                                                 "realm": self.realm2b,
+                                                 "resolver": "**",
+                                                 "serial": "**",
+                                                 "sortorder": "desc",
+                                                 "success": "**",
+                                                 "tokentype": "**",
+                                                 "user": "**"},
+                                           headers={'Authorization': user_authorization}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            json_response = res.json
+            self.assertTrue(json_response.get("result").get("status"), res)
+            # The normal user can not fetch audit entries, that do not belong to him!
+            count = json_response.get("result").get("value").get("count")
+            self.assertGreaterEqual(count, 0)
+
+        # delete policy
+        delete_policy("audit01")