Repository: privacyidea/privacyidea

Issue:
Shorten some audit information so that they fit into the info column
I noticed that some actions do not appear in the audit log by default because their ``info`` field is too long. Enabling SQL Truncate would of course be an action, but IMHO we could also shorten some infos.

An example: Successful authentication via the passthru policy is missing from the audit log.
```
[2018-04-27 15:25:07,007][21276][139772704446208][ERROR][privacyidea.lib.auditmodules.sqlaudit:241] DATA: {'info': u"The user authenticated against the RADIUS server alt according to policy 'passthru'.", 'realm': u'ucsrealm', 'tokentype': None, 'success': True, 'privacyidea_server': 'localhost', 'client_user_agent': None, 'client': '::1', 'user': u'fred', 'resolver': u'ucsresolver', 'action_detail': '', 'action': 'POST /validate/check', 'serial': None}
```

The ``info`` column is 50 characters by default, and I guess we could shorten the message ``u"The user authenticated against the RADIUS server alt according to policy 'passthru'."`` (84 chars) a bit.


Discussion:
We have the setting PI_AUDIT_SQL_TRUNCATE in pi.cfg. 
This should truncate the fields accordingly.

I know -- but I think shortening some messages like the above would still be a good idea because:
* PI_AUDIT_SQL_TRUNCATE is False on default Ubuntu installs. So users will not see successful passthru authentications by default.
* Even if SQL truncation is enabled, the truncated info will miss information: The info, truncated to 50 characters, is just ``"The user authenticated against the RADIUS server a"``, which cuts off both the policy name and the RADIUS server.

We could, e.g., shorten the info to ``by RADIUS server X and policy 'Y'.``. Of course, this may still fail if we have long RADIUS server or policy names.
Right. Sounds good. We also should set TRUNCATE  to true for new installations. 

Resolving patch:
diff --git a/privacyidea/api/lib/postpolicy.py b/privacyidea/api/lib/postpolicy.py
--- a/privacyidea/api/lib/postpolicy.py
+++ b/privacyidea/api/lib/postpolicy.py
@@ -710,7 +710,7 @@ def autoassign(request, response):
 
                                 g.audit_object.log(
                                     {"success": True,
-                                     "action_info":
+                                     "info":
                                          "Token assigned via auto assignment",
                                      "serial": token_obj.token.serial})
                                 break
diff --git a/privacyidea/lib/policydecorators.py b/privacyidea/lib/policydecorators.py
--- a/privacyidea/lib/policydecorators.py
+++ b/privacyidea/lib/policydecorators.py
@@ -227,9 +227,8 @@ def auth_user_has_no_token(wrapped_function, user_object, passw,
             # Now we need to check, if the user really has no token.
             tokencount = get_tokens(user=user_object, count=True)
             if tokencount == 0:
-                return True, {"message": "The user has no token, but is "
-                                         "accepted due to policy '%s'." %
-                                         pass_no_token[0].get("name")}
+                return True, {"message": u"user has no token, accepted due to '{!s}'".format(
+                    pass_no_token[0].get("name"))}
 
     # If nothing else returned, we return the wrapped function
     return wrapped_function(user_object, passw, options)
@@ -265,9 +264,8 @@ def auth_user_does_not_exist(wrapped_function, user_object, passw,
         if pass_no_user:
             # Check if user object exists
             if not user_object.exist():
-                return True, {"message": "The user does not exist, but is "
-                                         "accepted due to policy '%s'." %
-                                         pass_no_user[0].get("name")}
+                return True, {"message": u"user does not exist, accepted due to '{!s}'".format(
+                    pass_no_user[0].get("name"))}
 
     # If nothing else returned, we return the wrapped function
     return wrapped_function(user_object, passw, options)
@@ -310,9 +308,8 @@ def auth_user_passthru(wrapped_function, user_object, passw, options=None):
             if pass_thru_action in ["userstore", True]:
                 # Now we need to check the userstore password
                 if user_object.check_password(passw):
-                    return True, {"message": "The user authenticated against "
-                                             "his userstore according to "
-                                             "policy '%s'." % policy_name}
+                    return True, {"message": u"against userstore due to '{!s}'".format(
+                                      policy_name)}
             else:
                 # We are doing RADIUS passthru
                 log.info("Forwarding the authentication request to the radius "
@@ -320,10 +317,8 @@ def auth_user_passthru(wrapped_function, user_object, passw, options=None):
                 radius = get_radius(pass_thru_action)
                 r = radius.request(radius.config, user_object.login, passw)
                 if r:
-                    return True, {'message': "The user authenticated against "
-                                             "the RADIUS server %s according "
-                                             "to policy '%s'." %
-                                             (pass_thru_action, policy_name)}
+                    return True, {'message': u"against RADIUS server {!s} due to '{!s}'".format(
+                                      pass_thru_action, policy_name)}
 
     # If nothing else returned, we return the wrapped function
     return wrapped_function(user_object, passw, options)


Test patch:
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
@@ -1527,8 +1527,8 @@ def test_23_pass_no_user_and_pass_no_token(self):
             self.assertEqual(result.get("value"), True)
             detail = json.loads(res.data).get("detail")
             self.assertEqual(detail.get("message"),
-                             u"The user does not exist, but is accepted "
-                             u"due to policy 'pass_no'.")
+                             u"user does not exist, accepted "
+                             u"due to 'pass_no'")
 
         r = get_tokens(user=User(user, self.realm2), count=True)
         self.assertEqual(r, 1)
@@ -1544,8 +1544,8 @@ def test_23_pass_no_user_and_pass_no_token(self):
             self.assertEqual(result.get("value"), True)
             detail = json.loads(res.data).get("detail")
             self.assertEqual(detail.get("message"),
-                             u"The user has no token, but is "
-                             u"accepted due to policy 'pass_no'.")
+                             u"user has no token, "
+                             u"accepted due to 'pass_no'")
 
         r = get_tokens(user=User(user, self.realm2), count=True)
         self.assertEqual(r, 1)
@@ -1600,7 +1600,7 @@ def test_23a_pass_no_user_resolver(self):
             self.assertTrue(res.status_code == 200, res)
             detail = json.loads(res.data).get("detail")
             self.assertEqual(detail.get("message"),
-                             u'The user does not exist, but is accepted due to policy \'pol1\'.')
+                             u'user does not exist, accepted due to \'pol1\'')
         delete_policy("pol1")
 
     @responses.activate
diff --git a/tests/test_lib_policydecorator.py b/tests/test_lib_policydecorator.py
--- a/tests/test_lib_policydecorator.py
+++ b/tests/test_lib_policydecorator.py
@@ -185,8 +185,8 @@ def test_04_user_does_not_exist(self):
                                         options=options)
         self.assertTrue(rv[0])
         self.assertEqual(rv[1].get("message"),
-                         u"The user does not exist, but is accepted due "
-                         u"to policy 'pol1'.")
+                         u"user does not exist, accepted due "
+                         u"to 'pol1'")
         delete_policy("pol1")
 
     def test_04a_user_does_not_exist_without_resolver(self):
@@ -210,8 +210,8 @@ def test_04a_user_does_not_exist_without_resolver(self):
                                       options=options)
         self.assertTrue(rv[0])
         self.assertEqual(rv[1].get("message"),
-                         u"The user does not exist, but is accepted due "
-                         u"to policy 'pol1'.")
+                         u"user does not exist, accepted due "
+                         u"to 'pol1'")
         delete_policy("pol1")
 
     def test_05_user_has_no_tokens(self):
@@ -235,8 +235,8 @@ def test_05_user_has_no_tokens(self):
                                       options=options)
         self.assertTrue(rv[0])
         self.assertEqual(rv[1].get("message"),
-                         u"The user has no token, but is accepted due to "
-                         u"policy 'pol1'.")
+                         u"user has no token, accepted due to "
+                         u"'pol1'")
         delete_policy("pol1")
 
     @radiusmock.activate
@@ -263,8 +263,7 @@ def test_06_passthru(self):
                                 options=options)
         self.assertTrue(rv[0])
         self.assertEqual(rv[1].get("message"),
-                         u"The user authenticated against his userstore "
-                         u"according to policy 'pol1'.")
+                         u"against userstore due to 'pol1'")
 
         # Now set a PASSTHRU policy to the userstore (new style)
         set_policy(name="pol1",
@@ -277,8 +276,7 @@ def test_06_passthru(self):
                                 options=options)
         self.assertTrue(rv[0])
         self.assertEqual(rv[1].get("message"),
-                         u"The user authenticated against his userstore "
-                         u"according to policy 'pol1'.")
+                         u"against userstore due to 'pol1'")
 
         # Now set a PASSTHRU policy to a RADIUS config (new style)
         radiusmock.setdata(success=True)
@@ -295,8 +293,7 @@ def test_06_passthru(self):
                                 options=options)
         self.assertTrue(rv[0])
         self.assertEqual(rv[1].get("message"),
-                         u"The user authenticated against the RADIUS server "
-                         u"radiusconfig1 according to policy 'pol1'.")
+                         u"against RADIUS server radiusconfig1 due to 'pol1'")
 
         # Now assign a token to the user. If the user has a token and the
         # passthru policy is set, the user must not be able to authenticate