write audit log call variant which accepts json objects; old/new value logging for...
authorVictor Kirhenshtein <victor@netxms.org>
Wed, 17 May 2017 20:28:55 +0000 (23:28 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Wed, 17 May 2017 20:28:55 +0000 (23:28 +0300)
ChangeLog
include/uuid.h
src/libnetxms/uuid.cpp
src/server/core/Makefile.am
src/server/core/audit.cpp
src/server/core/events.cpp
src/server/core/session.cpp
src/server/core/tools.cpp
src/server/include/nms_core.h
src/server/include/nms_events.h

index 308a26d..eef2441 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,11 @@
 *
+* 2.1
+*
+
+- Improved audit logging
+
+
+*
 * 2.1-RC1
 *
 
index 10b78e0..98bcb6d 100644 (file)
@@ -31,6 +31,11 @@ void LIBNETXMS_EXPORTABLE _uuid_generate(uuid_t out);
 bool LIBNETXMS_EXPORTABLE _uuid_is_null(const uuid_t uu);
 int LIBNETXMS_EXPORTABLE _uuid_parse(const TCHAR *in, uuid_t uu);
 TCHAR LIBNETXMS_EXPORTABLE *_uuid_to_string(const uuid_t uu, TCHAR *out);
+#ifdef UNICODE
+char LIBNETXMS_EXPORTABLE *_uuid_to_stringA(const uuid_t uu, char *out);
+#else
+#define _uuid_to_stringA _uuid_to_string
+#endif
 
 #ifdef __cplusplus
 
@@ -53,6 +58,7 @@ public:
    bool isNull() const { return _uuid_is_null(m_value); }
    TCHAR *toString(TCHAR *buffer) const { return _uuid_to_string(m_value, buffer); }
    String toString() const { TCHAR buffer[64]; return String(_uuid_to_string(m_value, buffer)); }
+   char *toStringA(char *buffer) const { return _uuid_to_stringA(m_value, buffer); }
 
    /**
     * Generate new UUID
index df2fd0a..c2283d9 100644 (file)
@@ -241,6 +241,27 @@ TCHAR LIBNETXMS_EXPORTABLE *_uuid_to_string(const uuid_t uu, TCHAR *out)
    return out;
 }
 
+#ifdef UNICODE
+
+/**
+ * Convert packed UUID to string (non-UNICODE version)
+ */
+char LIBNETXMS_EXPORTABLE *_uuid_to_stringA(const uuid_t uu, char *out)
+{
+   struct __uuid uuid;
+
+   uuid_unpack(uu, &uuid);
+   snprintf(out, 64,
+      "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+      uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+      uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+      uuid.node[0], uuid.node[1], uuid.node[2],
+      uuid.node[3], uuid.node[4], uuid.node[5]);
+   return out;
+}
+
+#endif
+
 #ifndef _WIN32
 
 static int get_random_fd()
index 6c60bdb..ab40183 100644 (file)
@@ -56,14 +56,11 @@ endif
 if USE_INTERNAL_LIBTRE
 libnxcore_la_LIBADD += @top_srcdir@/src/libtre/libnxtre.la
 endif
-
-if ZEROMQ_SUPPORT
 if USE_INTERNAL_JANSSON
 libnxcore_la_LIBADD += @top_srcdir@/src/jansson/libnxjansson.la
 else
 libnxcore_la_LIBADD += -ljansson
 endif
-endif
 
 EXTRA_DIST = \
        nxcore.vcproj \
index 2385157..e447fb8 100644 (file)
@@ -166,7 +166,21 @@ void NXCORE_EXPORTABLE WriteAuditLogWithValues(const TCHAR *subsys, bool isSucce
 }
 
 /**
- * Write audit record
+ * Write audit record with old and new values in JSON format
+ */
+void NXCORE_EXPORTABLE WriteAuditLogWithJsonValues(const TCHAR *subsys, bool isSuccess, UINT32 userId,
+                                               const TCHAR *workstation, int sessionId, UINT32 objectId,
+                                               json_t *oldValue, json_t *newValue,
+                                               const TCHAR *format, ...)
+{
+   va_list args;
+   va_start(args, format);
+   WriteAuditLogWithJsonValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, oldValue, newValue, format, args);
+   va_end(args);
+}
+
+/**
+ * Write audit record with old and new values
  */
 void NXCORE_EXPORTABLE WriteAuditLogWithValues2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
                                                 const TCHAR *workstation, int sessionId, UINT32 objectId,
@@ -223,3 +237,26 @@ void NXCORE_EXPORTABLE WriteAuditLogWithValues2(const TCHAR *subsys, bool isSucc
                SendSyslogRecord((const TCHAR *)extText);
        }
 }
+
+/**
+ * Write audit record with old and new values
+ */
+void NXCORE_EXPORTABLE WriteAuditLogWithJsonValues2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
+                                                    const TCHAR *workstation, int sessionId, UINT32 objectId,
+                                                    json_t *oldValue, json_t *newValue,
+                                                    const TCHAR *format, va_list args)
+{
+   char *js1 = (oldValue != NULL) ? json_dumps(oldValue, 0) : strdup("");
+   char *js2 = (newValue != NULL) ? json_dumps(newValue, 0) : strdup("");
+#ifdef UNICODE
+   WCHAR *js1w = WideStringFromUTF8String(js1);
+   WCHAR *js2w = WideStringFromUTF8String(js2);
+   WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, js1w, js2w, format, args);
+   free(js1w);
+   free(js2w);
+#else
+   WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, js1, js2, format, args);
+#endif
+   free(js1);
+   free(js2);
+}
index 55fa2b4..5064cfc 100644 (file)
@@ -58,6 +58,22 @@ EventTemplate::~EventTemplate()
 }
 
 /**
+ * Convert event template to JSON
+ */
+json_t *EventTemplate::toJson() const
+{
+   json_t *root = json_object();
+   json_object_set_new(root, "code", json_integer(m_code));
+   char guidText[64];
+   json_object_set_new(root, "guid", json_string(m_guid.toStringA(guidText)));
+   json_object_set_new(root, "severity", json_integer(m_severity));
+   json_object_set_new(root, "flags", json_integer(m_flags));
+   json_object_set_new(root, "message", json_string_t(m_messageTemplate));
+   json_object_set_new(root, "description", json_string_t(m_description));
+   return root;
+}
+
+/**
  * Default constructor for event
  */
 Event::Event()
@@ -189,7 +205,7 @@ Event::Event(const EventTemplate *eventTemplate, UINT32 sourceId, UINT32 dciId,
                break;
             case 'A':   // InetAddress object
                buffer = (TCHAR *)malloc(64 * sizeof(TCHAR));
-               va_arg(args, InetAddress *)->toString(buffer);
+               (va_arg(args, InetAddress *))->toString(buffer);
                                        m_parameters.add(buffer);
                break;
             case 'h':
index d17d8d6..6909f49 100644 (file)
@@ -395,6 +395,17 @@ void ClientSession::writeAuditLogWithValues(const TCHAR *subsys, bool success, U
 }
 
 /**
+ * Write audit log with old and new values for changed entity
+ */
+void ClientSession::writeAuditLogWithValues(const TCHAR *subsys, bool success, UINT32 objectId, json_t *oldValue, json_t *newValue, const TCHAR *format, ...)
+{
+   va_list args;
+   va_start(args, format);
+   WriteAuditLogWithJsonValues2(subsys, success, m_dwUserId, m_workstation, m_id, objectId, oldValue, newValue, format, args);
+   va_end(args);
+}
+
+/**
  * Check channel subscription
  */
 bool ClientSession::isSubscribedTo(const TCHAR *channel) const
@@ -1989,8 +2000,7 @@ void ClientSession::login(NXCPMessage *pRequest)
          msg.setField(VID_MESSAGE_OF_THE_DAY, buffer);
 
          debugPrintf(3, _T("User %s authenticated (language=%s clientInfo=\"%s\")"), m_sessionName, m_language, m_clientInfo);
-                       WriteAuditLog(AUDIT_SECURITY, TRUE, m_dwUserId, m_workstation, m_id, 0,
-            _T("User \"%s\" logged in (language: %s; client info: %s)"), szLogin, m_language, m_clientInfo);
+                       writeAuditLog(AUDIT_SECURITY, true, 0, _T("User \"%s\" logged in (language: %s; client info: %s)"), szLogin, m_language, m_clientInfo);
 
                        if (closeOtherSessions)
                        {
@@ -2001,12 +2011,12 @@ void ClientSession::login(NXCPMessage *pRequest)
       else
       {
          msg.setField(VID_RCC, dwResult);
-                       WriteAuditLog(AUDIT_SECURITY, FALSE, m_dwUserId, m_workstation, m_id, 0,
+                       writeAuditLog(AUDIT_SECURITY, false, 0,
                                      _T("User \"%s\" login failed with error code %d (client info: %s)"),
                                                          szLogin, dwResult, m_clientInfo);
                        if (intruderLockout)
                        {
-                               WriteAuditLog(AUDIT_SECURITY, FALSE, m_dwUserId, m_workstation, m_id, 0,
+                               writeAuditLog(AUDIT_SECURITY, false, 0,
                                                                  _T("User account \"%s\" temporary disabled due to excess count of failed authentication attempts"), szLogin);
                        }
                        m_dwUserId = INVALID_INDEX;   // reset user ID to avoid incorrect count of logged in sessions for that user
@@ -2188,23 +2198,26 @@ void ClientSession::modifyEventTemplate(NXCPMessage *pRequest)
    msg.setCode(CMD_REQUEST_COMPLETED);
    msg.setId(pRequest->getId());
 
+   UINT32 eventCode = pRequest->getFieldAsUInt32(VID_EVENT_CODE);
+
    // Check access rights
    if (checkSysAccessRights(SYSTEM_ACCESS_EDIT_EVENT_DB))
    {
       DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
 
-      // Check if event with specific code exists
-      UINT32 dwEventCode = pRequest->getFieldAsUInt32(VID_EVENT_CODE);
-               bool bEventExist = IsDatabaseRecordExist(hdb, _T("event_cfg"), _T("event_code"), dwEventCode);
+               bool bEventExist = IsDatabaseRecordExist(hdb, _T("event_cfg"), _T("event_code"), eventCode);
 
       // Check that we are not trying to create event below 100000
-      if (bEventExist || (dwEventCode >= FIRST_USER_EVENT_ID))
+      if (bEventExist || (eventCode >= FIRST_USER_EVENT_ID))
       {
          // Prepare and execute SQL query
          TCHAR name[MAX_EVENT_NAME];
          pRequest->getFieldAsString(VID_NAME, name, MAX_EVENT_NAME);
          if (IsValidObjectName(name, TRUE))
          {
+            EventTemplate *evt = FindEventTemplateByCode(eventCode);
+            json_t *oldValue = (evt != NULL) ? evt->toJson() : NULL;
+
             DB_STATEMENT hStmt;
             if (bEventExist)
             {
@@ -2222,7 +2235,7 @@ void ClientSession::modifyEventTemplate(NXCPMessage *pRequest)
                DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, pRequest->getFieldAsInt32(VID_FLAGS));
                DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, pRequest->getFieldAsString(VID_MESSAGE), DB_BIND_DYNAMIC, MAX_EVENT_MSG_LENGTH - 1);
                DBBind(hStmt, 5, DB_SQLTYPE_TEXT, pRequest->getFieldAsString(VID_DESCRIPTION), DB_BIND_DYNAMIC);
-               DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, dwEventCode);
+               DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, eventCode);
                if (!bEventExist)
                {
                   DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, uuid::generate());
@@ -2237,6 +2250,11 @@ void ClientSession::modifyEventTemplate(NXCPMessage *pRequest)
                                           nmsg.setCode(CMD_EVENT_DB_UPDATE);
                                           nmsg.setField(VID_NOTIFICATION_CODE, (WORD)NX_NOTIFY_ETMPL_CHANGED);
                                           EnumerateClientSessions(SendEventDBChangeNotification, &nmsg);
+
+                                          evt = FindEventTemplateByCode(eventCode);
+                                          json_t *newValue = (evt != NULL) ? evt->toJson() : NULL;
+                                          writeAuditLogWithValues(AUDIT_SYSCFG, true, 0, oldValue, newValue, _T("Event template %s [%d] modified"), name, eventCode);
+                                          json_decref(newValue);
                }
                else
                {
@@ -2248,6 +2266,7 @@ void ClientSession::modifyEventTemplate(NXCPMessage *pRequest)
             {
                msg.setField(VID_RCC, RCC_DB_FAILURE);
             }
+            json_decref(oldValue);
          }
          else
          {
@@ -2263,6 +2282,7 @@ void ClientSession::modifyEventTemplate(NXCPMessage *pRequest)
    else
    {
       msg.setField(VID_RCC, RCC_ACCESS_DENIED);
+      writeAuditLog(AUDIT_SYSCFG, false, 0, _T("Access denied on modify event template [%d]"), eventCode);
    }
 
    // Send response
@@ -2301,7 +2321,7 @@ void ClientSession::deleteEventTemplate(NXCPMessage *pRequest)
 
          msg.setField(VID_RCC, RCC_SUCCESS);
 
-                       WriteAuditLog(AUDIT_SYSCFG, TRUE, m_dwUserId, m_workstation, m_id, 0, _T("Event template %d deleted"), dwEventCode);
+                       writeAuditLog(AUDIT_SYSCFG, true, 0, _T("Event template [%d] deleted"), dwEventCode);
       }
       else
       {
@@ -2312,6 +2332,7 @@ void ClientSession::deleteEventTemplate(NXCPMessage *pRequest)
    else
    {
       msg.setField(VID_RCC, RCC_ACCESS_DENIED);
+      writeAuditLog(AUDIT_SYSCFG, false, 0, _T("Access denied on delete event template [%d]"), dwEventCode);
    }
 
    // Send response
index 95de838..57cbe83 100644 (file)
@@ -471,3 +471,14 @@ ObjectArray<ObjectsDistance> *FindNearestObjects(UINT32 currObjectId, int maxDis
    currObj->decRefCount();
    return result;
 }
+
+/**
+ * Create JSON string from wide character string
+ */
+json_t *json_string_w(const WCHAR *s)
+{
+   char *us = UTF8StringFromWideString(s);
+   json_t *js = json_string(us);
+   free(us);
+   return js;
+}
index eafcab4..31cfd02 100644 (file)
@@ -785,6 +785,7 @@ public:
 
    void writeAuditLog(const TCHAR *subsys, bool success, UINT32 objectId, const TCHAR *format, ...);
    void writeAuditLogWithValues(const TCHAR *subsys, bool success, UINT32 objectId, const TCHAR *oldValue, const TCHAR *newValue, const TCHAR *format, ...);
+   void writeAuditLogWithValues(const TCHAR *subsys, bool success, UINT32 objectId, json_t *oldValue, json_t *newValue, const TCHAR *format, ...);
 
    int getId() const { return m_id; }
    void setId(int id) { if (m_id == -1) m_id = id; }
@@ -1150,6 +1151,13 @@ void OnSyslogConfigurationChange(const TCHAR *name, const TCHAR *value);
 
 void EscapeString(String &str);
 
+json_t *json_string_w(const WCHAR *s);
+#ifdef UNICODE
+#define json_string_t json_string_w
+#else
+#define json_string_t json_string
+#endif
+
 void InitAuditLog();
 void NXCORE_EXPORTABLE WriteAuditLog(const TCHAR *subsys, bool isSuccess, UINT32 userId,
                                      const TCHAR *workstation, int sessionId, UINT32 objectId,
@@ -1161,10 +1169,18 @@ void NXCORE_EXPORTABLE WriteAuditLogWithValues(const TCHAR *subsys, bool isSucce
                                                const TCHAR *workstation, int sessionId, UINT32 objectId,
                                                const TCHAR *oldValue, const TCHAR *newValue,
                                                const TCHAR *format, ...);
+void NXCORE_EXPORTABLE WriteAuditLogWithJsonValues(const TCHAR *subsys, bool isSuccess, UINT32 userId,
+                                                   const TCHAR *workstation, int sessionId, UINT32 objectId,
+                                                   json_t *oldValue, json_t *newValue,
+                                                   const TCHAR *format, ...);
 void NXCORE_EXPORTABLE WriteAuditLogWithValues2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
                                                 const TCHAR *workstation, int sessionId, UINT32 objectId,
                                                 const TCHAR *oldValue, const TCHAR *newValue,
                                                 const TCHAR *format, va_list args);
+void NXCORE_EXPORTABLE WriteAuditLogWithJsonValues2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
+                                                    const TCHAR *workstation, int sessionId, UINT32 objectId,
+                                                    json_t *oldValue, json_t *newValue,
+                                                    const TCHAR *format, va_list args);
 
 bool ValidateConfig(Config *config, UINT32 flags, TCHAR *errorText, int errorTextLen);
 UINT32 ImportConfig(Config *config, UINT32 flags);
index ec84a0a..decb28e 100644 (file)
@@ -24,6 +24,7 @@
 #define _nms_events_h_
 
 #include <nxevent.h>
+#include <jansson.h>
 
 
 //
@@ -60,6 +61,8 @@ public:
    UINT32 getFlags() const { return m_flags; }
    const TCHAR *getMessageTemplate() const { return m_messageTemplate; }
    const TCHAR *getDescription() const { return m_description; }
+
+   json_t *toJson() const;
 };
 
 /**