Repository: privacyidea/privacyidea

Issue:
API-Requests for event enpoint don't get logged to Audit Log
Due to the fact that [`after_request()`](https://github.com/privacyidea/privacyidea/blob/12cbb8dfbde6f72849cc81daf04fc52004b2f929/privacyidea/api/before_after.py#L221) is not decorated with `eventhandling_blueprint.after_request`, the requests to the event-Endpoint are never logged in the Audit-Log.


Discussion:
Just for the record: also, the responses are not signed because ``sign_response`` is never called. 
@plettich I can do this?


Resolving patch:
diff --git a/privacyidea/api/before_after.py b/privacyidea/api/before_after.py
--- a/privacyidea/api/before_after.py
+++ b/privacyidea/api/before_after.py
@@ -211,6 +211,7 @@ def before_request():
 @machineresolver_blueprint.after_request
 @caconnector_blueprint.after_request
 @smtpserver_blueprint.after_request
+@eventhandling_blueprint.after_request
 @radiusserver_blueprint.after_request
 @periodictask_blueprint.after_request
 @privacyideaserver_blueprint.after_request
@@ -243,6 +244,7 @@ def after_request(response):
 @audit_blueprint.app_errorhandler(AuthError)
 @application_blueprint.app_errorhandler(AuthError)
 @smtpserver_blueprint.app_errorhandler(AuthError)
+@eventhandling_blueprint.app_errorhandler(AuthError)
 @subscriptions_blueprint.app_errorhandler(AuthError)
 @monitoring_blueprint.app_errorhandler(AuthError)
 @postrequest(sign_response, request=request)
@@ -275,6 +277,7 @@ def auth_error(error):
 @audit_blueprint.app_errorhandler(PolicyError)
 @application_blueprint.app_errorhandler(PolicyError)
 @smtpserver_blueprint.app_errorhandler(PolicyError)
+@eventhandling_blueprint.app_errorhandler(PolicyError)
 @register_blueprint.app_errorhandler(PolicyError)
 @recover_blueprint.app_errorhandler(PolicyError)
 @subscriptions_blueprint.app_errorhandler(PolicyError)
@@ -297,6 +300,7 @@ def policy_error(error):
 @audit_blueprint.app_errorhandler(ResourceNotFoundError)
 @application_blueprint.app_errorhandler(ResourceNotFoundError)
 @smtpserver_blueprint.app_errorhandler(ResourceNotFoundError)
+@eventhandling_blueprint.app_errorhandler(ResourceNotFoundError)
 @register_blueprint.app_errorhandler(ResourceNotFoundError)
 @recover_blueprint.app_errorhandler(ResourceNotFoundError)
 @subscriptions_blueprint.app_errorhandler(ResourceNotFoundError)
@@ -322,6 +326,7 @@ def resource_not_found_error(error):
 @audit_blueprint.app_errorhandler(privacyIDEAError)
 @application_blueprint.app_errorhandler(privacyIDEAError)
 @smtpserver_blueprint.app_errorhandler(privacyIDEAError)
+@eventhandling_blueprint.app_errorhandler(privacyIDEAError)
 @register_blueprint.app_errorhandler(privacyIDEAError)
 @recover_blueprint.app_errorhandler(privacyIDEAError)
 @subscriptions_blueprint.app_errorhandler(privacyIDEAError)
@@ -349,6 +354,7 @@ def privacyidea_error(error):
 @audit_blueprint.app_errorhandler(500)
 @application_blueprint.app_errorhandler(500)
 @smtpserver_blueprint.app_errorhandler(500)
+@eventhandling_blueprint.app_errorhandler(500)
 @register_blueprint.app_errorhandler(500)
 @recover_blueprint.app_errorhandler(500)
 @subscriptions_blueprint.app_errorhandler(500)
diff --git a/privacyidea/api/event.py b/privacyidea/api/event.py
--- a/privacyidea/api/event.py
+++ b/privacyidea/api/event.py
@@ -86,6 +86,7 @@ def get_module_positions(handlermodule=None):
     h_obj = get_handler_object(handlermodule)
     if h_obj:
         ret = h_obj.allowed_positions
+    g.audit_object.log({"success": True})
     return send_result(ret)
 
 
@@ -103,6 +104,7 @@ def get_module_actions(handlermodule=None):
     h_obj = get_handler_object(handlermodule)
     if h_obj:
         ret = h_obj.actions
+    g.audit_object.log({"success": True})
     return send_result(ret)
 
 
@@ -120,6 +122,7 @@ def get_module_conditions(handlermodule=None):
     h_obj = get_handler_object(handlermodule)
     if h_obj:
         ret = h_obj.conditions
+    g.audit_object.log({"success": True})
     return send_result(ret)
 
 


Test patch:
diff --git a/tests/test_api_events.py b/tests/test_api_events.py
--- a/tests/test_api_events.py
+++ b/tests/test_api_events.py
@@ -1,4 +1,6 @@
 import json
+
+from privacyidea.lib.policy import SCOPE, set_policy, delete_policy
 from .base import MyApiTestCase
 from . import smtpmock
 from privacyidea.lib.config import set_privacyidea_config
@@ -6,6 +8,51 @@
 
 class APIEventsTestCase(MyApiTestCase):
 
+    def test_00_api_errors(self):
+        # check for auth error
+        with self.app.test_request_context('/event',
+                                           method='GET'):
+            res = self.app.full_dispatch_request()
+            self.assertEqual(res.status_code, 401, res)
+            self.assertEqual(res.json['result']['error']['code'], 4033, res.json)
+
+        param = {
+            "name": "Send an email",
+            "event": "token_init",
+            "action": "sendmail",
+            "handlermodule": "UserNotification",
+            "conditions": '{"blabla": "yes"}'
+        }
+        with self.app.test_request_context('/event',
+                                           method='POST',
+                                           data=param,
+                                           headers={'Authorization': self.at}):
+            # check for policy error with a restrictive admin policy
+            set_policy(name="adm_disable_event", scope=SCOPE.ADMIN,
+                       action="configwrite")
+            res = self.app.full_dispatch_request()
+            self.assertEqual(res.status_code, 403, res)
+            self.assertEqual(res.json['result']['error']['code'], 303, res.json)
+            delete_policy('adm_disable_event')
+
+        # check fo resourceNotFound error
+        with self.app.test_request_context('/event/enable/1234',
+                                           method='POST',
+                                           headers={'Authorization': self.at}):
+            res = self.app.full_dispatch_request()
+            self.assertEqual(res.status_code, 404, res)
+            self.assertEqual(res.json['result']['error']['code'], 601, res.json)
+        # And check if the /event request writes a valid (failed) audit entry
+        with self.app.test_request_context('/audit/',
+                                           method='GET',
+                                           data={'action': 'POST /event/enable/<eventid>'},
+                                           headers={'Authorization': self.at}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            auditentry = res.json.get("result").get("value").get("auditdata")[0]
+            self.assertEqual(auditentry['action'], 'POST /event/enable/<eventid>', auditentry)
+            self.assertEqual(auditentry['success'], 0, auditentry)
+
     def test_01_crud_events(self):
 
         # list empty events
@@ -15,9 +62,19 @@ def test_01_crud_events(self):
 
             res = self.app.full_dispatch_request()
             self.assertTrue(res.status_code == 200, res)
-            result = json.loads(res.data.decode('utf8')).get("result")
-            detail = json.loads(res.data.decode('utf8')).get("detail")
+            result = res.json.get("result")
             self.assertEqual(result.get("value"), [])
+            self.assertIn('signature', res.json, res.json)
+
+        # Check if the /event request writes a valid audit entry
+        with self.app.test_request_context('/audit/',
+                                           method='GET',
+                                           headers={'Authorization': self.at}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            auditentry = res.json.get("result").get("value").get("auditdata")[0]
+            self.assertEqual(auditentry['action'], 'GET /event', auditentry)
+            self.assertEqual(auditentry['success'], 1, auditentry)
 
         # create an event configuration
         param = {