2016-04-29 23:49:56 -07:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
exports = module.exports = {
|
2021-01-06 21:57:23 -08:00
|
|
|
add,
|
2021-06-01 09:35:20 -07:00
|
|
|
upsertLoginEvent,
|
2021-01-06 21:57:23 -08:00
|
|
|
get,
|
2023-12-04 01:38:08 +01:00
|
|
|
getActivationEvent,
|
2021-08-20 11:27:35 -07:00
|
|
|
listPaged,
|
2021-01-06 21:57:23 -08:00
|
|
|
cleanup,
|
2021-06-01 09:35:20 -07:00
|
|
|
_clear: clear,
|
2016-04-30 11:49:51 -07:00
|
|
|
|
2020-03-19 17:02:42 -07:00
|
|
|
// keep in sync with webadmin index.js filter
|
2016-04-30 13:56:03 -07:00
|
|
|
ACTION_ACTIVATE: 'cloudron.activate',
|
2016-06-17 17:12:55 -05:00
|
|
|
ACTION_APP_CLONE: 'app.clone',
|
2016-04-30 13:34:06 -07:00
|
|
|
ACTION_APP_CONFIGURE: 'app.configure',
|
2019-09-19 17:04:11 -07:00
|
|
|
ACTION_APP_REPAIR: 'app.repair',
|
2016-04-30 13:34:06 -07:00
|
|
|
ACTION_APP_INSTALL: 'app.install',
|
|
|
|
|
ACTION_APP_RESTORE: 'app.restore',
|
2021-05-26 09:27:15 -07:00
|
|
|
ACTION_APP_IMPORT: 'app.import',
|
2016-04-30 13:34:06 -07:00
|
|
|
ACTION_APP_UNINSTALL: 'app.uninstall',
|
|
|
|
|
ACTION_APP_UPDATE: 'app.update',
|
2019-05-07 12:04:43 +02:00
|
|
|
ACTION_APP_UPDATE_FINISH: 'app.update.finish',
|
2021-09-30 10:45:25 -07:00
|
|
|
ACTION_APP_BACKUP: 'app.backup',
|
|
|
|
|
ACTION_APP_BACKUP_FINISH: 'app.backup.finish',
|
2017-03-02 17:13:04 +01:00
|
|
|
ACTION_APP_LOGIN: 'app.login',
|
2019-01-17 15:31:34 +01:00
|
|
|
ACTION_APP_OOM: 'app.oom',
|
2019-02-11 12:32:02 -08:00
|
|
|
ACTION_APP_UP: 'app.up',
|
2019-01-17 15:49:04 +01:00
|
|
|
ACTION_APP_DOWN: 'app.down',
|
2020-03-19 17:02:42 -07:00
|
|
|
ACTION_APP_START: 'app.start',
|
|
|
|
|
ACTION_APP_STOP: 'app.stop',
|
|
|
|
|
ACTION_APP_RESTART: 'app.restart',
|
2018-11-09 18:45:44 -08:00
|
|
|
|
2016-05-01 11:42:12 -07:00
|
|
|
ACTION_BACKUP_FINISH: 'backup.finish',
|
|
|
|
|
ACTION_BACKUP_START: 'backup.start',
|
2019-09-27 09:43:40 -07:00
|
|
|
ACTION_BACKUP_CLEANUP_START: 'backup.cleanup.start', // obsolete
|
2019-01-11 12:48:40 -08:00
|
|
|
ACTION_BACKUP_CLEANUP_FINISH: 'backup.cleanup.finish',
|
2018-11-09 18:45:44 -08:00
|
|
|
|
2018-11-14 20:38:49 -08:00
|
|
|
ACTION_CERTIFICATE_NEW: 'certificate.new',
|
2022-11-28 22:32:34 +01:00
|
|
|
ACTION_CERTIFICATE_RENEWAL: 'certificate.renew', // obsolete
|
2022-02-24 19:52:51 -08:00
|
|
|
ACTION_CERTIFICATE_CLEANUP: 'certificate.cleanup',
|
2018-11-09 18:45:44 -08:00
|
|
|
|
2019-02-04 20:24:28 -08:00
|
|
|
ACTION_DASHBOARD_DOMAIN_UPDATE: 'dashboard.domain.update',
|
|
|
|
|
|
2024-01-13 12:18:14 +01:00
|
|
|
ACTION_DIRECTORY_SERVER_CONFIGURE: 'directoryserver.configure',
|
|
|
|
|
|
2018-11-10 00:43:46 -08:00
|
|
|
ACTION_DOMAIN_ADD: 'domain.add',
|
|
|
|
|
ACTION_DOMAIN_UPDATE: 'domain.update',
|
|
|
|
|
ACTION_DOMAIN_REMOVE: 'domain.remove',
|
|
|
|
|
|
2024-01-13 13:02:43 +01:00
|
|
|
ACTION_EXTERNAL_LDAP_CONFIGURE: 'externalldap.configure',
|
|
|
|
|
|
2022-04-01 13:44:46 -07:00
|
|
|
ACTION_INSTALL_FINISH: 'cloudron.install.finish',
|
|
|
|
|
|
2020-09-09 22:31:50 -07:00
|
|
|
ACTION_MAIL_LOCATION: 'mail.location',
|
2018-11-09 18:51:58 -08:00
|
|
|
ACTION_MAIL_ENABLED: 'mail.enabled',
|
2018-11-10 00:32:27 -08:00
|
|
|
ACTION_MAIL_DISABLED: 'mail.disabled',
|
2018-11-09 18:49:55 -08:00
|
|
|
ACTION_MAIL_MAILBOX_ADD: 'mail.box.add',
|
|
|
|
|
ACTION_MAIL_MAILBOX_REMOVE: 'mail.box.remove',
|
2020-01-24 16:55:41 -08:00
|
|
|
ACTION_MAIL_MAILBOX_UPDATE: 'mail.box.update',
|
2018-11-09 18:49:55 -08:00
|
|
|
ACTION_MAIL_LIST_ADD: 'mail.list.add',
|
|
|
|
|
ACTION_MAIL_LIST_REMOVE: 'mail.list.remove',
|
2020-01-24 16:55:41 -08:00
|
|
|
ACTION_MAIL_LIST_UPDATE: 'mail.list.update',
|
2018-11-09 18:45:44 -08:00
|
|
|
|
2018-11-10 00:43:46 -08:00
|
|
|
ACTION_PROVISION: 'cloudron.provision',
|
|
|
|
|
ACTION_RESTORE: 'cloudron.restore', // unused
|
2016-05-02 09:39:38 -07:00
|
|
|
ACTION_START: 'cloudron.start',
|
2021-09-24 10:22:45 -07:00
|
|
|
|
|
|
|
|
ACTION_SERVICE_CONFIGURE: 'service.configure',
|
|
|
|
|
ACTION_SERVICE_REBUILD: 'service.rebuild',
|
|
|
|
|
ACTION_SERVICE_RESTART: 'service.restart',
|
|
|
|
|
|
2016-04-30 13:56:03 -07:00
|
|
|
ACTION_UPDATE: 'cloudron.update',
|
2019-08-03 13:59:11 -07:00
|
|
|
ACTION_UPDATE_FINISH: 'cloudron.update.finish',
|
2018-11-09 18:45:44 -08:00
|
|
|
|
2016-04-30 13:56:03 -07:00
|
|
|
ACTION_USER_ADD: 'user.add',
|
2016-04-30 23:16:37 -07:00
|
|
|
ACTION_USER_LOGIN: 'user.login',
|
2022-07-29 20:39:18 +02:00
|
|
|
ACTION_USER_LOGIN_GHOST: 'user.login.ghost',
|
2021-01-06 21:57:23 -08:00
|
|
|
ACTION_USER_LOGOUT: 'user.logout',
|
2016-04-30 13:56:03 -07:00
|
|
|
ACTION_USER_REMOVE: 'user.remove',
|
2018-07-05 13:51:22 -07:00
|
|
|
ACTION_USER_UPDATE: 'user.update',
|
|
|
|
|
ACTION_USER_TRANSFER: 'user.transfer',
|
2019-01-12 09:58:11 -08:00
|
|
|
|
2024-06-12 10:46:23 +02:00
|
|
|
ACTION_USER_DIRECTORY_PROFILE_CONFIG_UPDATE: 'userdirectory.profileconfig.update',
|
|
|
|
|
|
2020-10-27 22:39:05 -07:00
|
|
|
ACTION_VOLUME_ADD: 'volume.add',
|
|
|
|
|
ACTION_VOLUME_UPDATE: 'volume.update',
|
2021-10-11 15:51:16 +02:00
|
|
|
ACTION_VOLUME_REMOUNT: 'volume.remount',
|
2020-10-27 22:39:05 -07:00
|
|
|
ACTION_VOLUME_REMOVE: 'volume.remove',
|
|
|
|
|
|
2019-01-19 13:22:29 +01:00
|
|
|
ACTION_DYNDNS_UPDATE: 'dyndns.update',
|
|
|
|
|
|
2019-12-16 14:06:55 -08:00
|
|
|
ACTION_SUPPORT_TICKET: 'support.ticket',
|
|
|
|
|
ACTION_SUPPORT_SSH: 'support.ssh',
|
|
|
|
|
|
2023-05-15 11:08:00 +02:00
|
|
|
ACTION_PROCESS_CRASH: 'system.crash' // obsolete
|
2016-04-29 23:49:56 -07:00
|
|
|
};
|
|
|
|
|
|
2021-05-02 11:26:08 -07:00
|
|
|
const assert = require('assert'),
|
2021-06-01 09:35:20 -07:00
|
|
|
database = require('./database.js'),
|
2023-02-25 00:31:35 +01:00
|
|
|
debug = require('debug')('box:eventlog'),
|
2021-06-01 09:35:20 -07:00
|
|
|
mysql = require('mysql'),
|
2019-01-17 13:12:26 +01:00
|
|
|
notifications = require('./notifications.js'),
|
2021-05-28 14:34:18 -07:00
|
|
|
safe = require('safetydance'),
|
2017-08-13 17:44:31 -07:00
|
|
|
uuid = require('uuid');
|
2016-04-29 23:49:56 -07:00
|
|
|
|
2021-11-17 12:21:46 -08:00
|
|
|
const EVENTLOG_FIELDS = [ 'id', 'action', 'sourceJson', 'dataJson', 'creationTime' ].join(',');
|
2016-04-30 11:49:51 -07:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
function postProcess(record) {
|
|
|
|
|
// usually we have sourceJson and dataJson, however since this used to be the JSON data type, we don't
|
2021-11-17 12:21:46 -08:00
|
|
|
record.source = safe.JSON.parse(record.sourceJson);
|
|
|
|
|
delete record.sourceJson;
|
|
|
|
|
record.data = safe.JSON.parse(record.dataJson);
|
|
|
|
|
delete record.dataJson;
|
2021-06-01 09:35:20 -07:00
|
|
|
|
|
|
|
|
return record;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// never throws, only logs because previously code did not take a callback
|
|
|
|
|
async function add(action, source, data) {
|
2016-04-29 23:49:56 -07:00
|
|
|
assert.strictEqual(typeof action, 'string');
|
2023-08-26 08:18:58 +05:30
|
|
|
assert.strictEqual(typeof source, 'object'); // an AuditSource
|
2016-04-29 23:49:56 -07:00
|
|
|
assert.strictEqual(typeof data, 'object');
|
2021-01-06 22:09:37 -08:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
const id = uuid.v4();
|
2021-11-17 12:21:46 -08:00
|
|
|
await database.query('INSERT INTO eventlog (id, action, sourceJson, dataJson) VALUES (?, ?, ?, ?)', [ id, action, JSON.stringify(source), JSON.stringify(data) ]);
|
2021-09-01 15:29:35 -07:00
|
|
|
await notifications.onEvent(id, action, source, data);
|
|
|
|
|
return id;
|
2021-01-06 22:09:37 -08:00
|
|
|
}
|
|
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
// never throws, only logs because previously code did not take a callback
|
|
|
|
|
async function upsertLoginEvent(action, source, data) {
|
2021-01-06 22:09:37 -08:00
|
|
|
assert.strictEqual(typeof action, 'string');
|
2023-08-26 08:18:58 +05:30
|
|
|
assert.strictEqual(typeof source, 'object'); // an AuditSource
|
2021-01-06 22:09:37 -08:00
|
|
|
assert.strictEqual(typeof data, 'object');
|
2016-04-29 23:49:56 -07:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
// can't do a real sql upsert, for frequent eventlog entries we only have to do 2 queries once a day
|
|
|
|
|
const queries = [{
|
2021-11-17 12:21:46 -08:00
|
|
|
query: 'UPDATE eventlog SET creationTime=NOW(), dataJson=? WHERE action = ? AND sourceJson LIKE ? AND DATE(creationTime)=CURDATE()',
|
2021-06-01 09:35:20 -07:00
|
|
|
args: [ JSON.stringify(data), action, JSON.stringify(source) ]
|
|
|
|
|
}, {
|
2021-11-17 12:21:46 -08:00
|
|
|
query: 'SELECT ' + EVENTLOG_FIELDS + ' FROM eventlog WHERE action = ? AND sourceJson LIKE ? AND DATE(creationTime)=CURDATE()',
|
2021-06-01 09:35:20 -07:00
|
|
|
args: [ action, JSON.stringify(source) ]
|
|
|
|
|
}];
|
|
|
|
|
|
2021-09-01 15:29:35 -07:00
|
|
|
const result = await database.transaction(queries);
|
|
|
|
|
if (result[0].affectedRows >= 1) return result[1][0].id;
|
2021-06-01 09:35:20 -07:00
|
|
|
|
2021-09-01 15:29:35 -07:00
|
|
|
// no existing eventlog found, create one
|
|
|
|
|
return await add(action, source, data);
|
2016-04-29 23:49:56 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
async function get(id) {
|
2016-04-29 23:49:56 -07:00
|
|
|
assert.strictEqual(typeof id, 'string');
|
|
|
|
|
|
2023-12-04 01:38:08 +01:00
|
|
|
const result = await database.query(`SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE id = ?`, [ id ]);
|
|
|
|
|
if (result.length === 0) return null;
|
|
|
|
|
|
|
|
|
|
return postProcess(result[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getActivationEvent() {
|
|
|
|
|
const result = await database.query(`SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE action = ? ORDER BY creationTime`, [ exports.ACTION_ACTIVATE ]);
|
2021-06-01 09:35:20 -07:00
|
|
|
if (result.length === 0) return null;
|
2016-04-29 23:49:56 -07:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
return postProcess(result[0]);
|
2016-04-29 23:49:56 -07:00
|
|
|
}
|
|
|
|
|
|
2021-08-20 11:27:35 -07:00
|
|
|
async function listPaged(actions, search, page, perPage) {
|
2018-03-05 11:14:36 +01:00
|
|
|
assert(Array.isArray(actions));
|
2016-05-06 16:49:17 +02:00
|
|
|
assert(typeof search === 'string' || search === null);
|
|
|
|
|
assert.strictEqual(typeof page, 'number');
|
|
|
|
|
assert.strictEqual(typeof perPage, 'number');
|
|
|
|
|
|
2024-06-12 10:46:23 +02:00
|
|
|
const data = [];
|
2021-06-01 09:35:20 -07:00
|
|
|
let query = `SELECT ${EVENTLOG_FIELDS} FROM eventlog`;
|
2016-05-06 16:49:17 +02:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
if (actions.length || search) query += ' WHERE';
|
2021-11-17 12:21:46 -08:00
|
|
|
if (search) query += ' (sourceJson LIKE ' + mysql.escape('%' + search + '%') + ' OR dataJson LIKE ' + mysql.escape('%' + search + '%') + ')';
|
2021-06-01 09:35:20 -07:00
|
|
|
|
|
|
|
|
if (actions.length && search) query += ' AND ( ';
|
|
|
|
|
actions.forEach(function (action, i) {
|
|
|
|
|
query += ' (action LIKE ' + mysql.escape(`%${action}%`) + ') ';
|
|
|
|
|
if (i < actions.length-1) query += ' OR ';
|
2016-05-06 16:49:17 +02:00
|
|
|
});
|
2021-06-01 09:35:20 -07:00
|
|
|
if (actions.length && search) query += ' ) ';
|
2016-07-25 12:36:43 -07:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
query += ' ORDER BY creationTime DESC LIMIT ?,?';
|
2017-07-21 16:13:44 +02:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
data.push((page-1)*perPage);
|
|
|
|
|
data.push(perPage);
|
2017-07-21 16:13:44 +02:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
const results = await database.query(query, data);
|
|
|
|
|
results.forEach(postProcess);
|
|
|
|
|
return results;
|
2017-07-21 16:13:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
async function cleanup(options) {
|
|
|
|
|
assert.strictEqual(typeof options, 'object');
|
2016-07-25 12:36:43 -07:00
|
|
|
|
2021-06-05 21:20:32 -07:00
|
|
|
const creationTime = options.creationTime;
|
2023-02-25 00:31:35 +01:00
|
|
|
debug(`cleanup: pruning events. creationTime: ${creationTime.toString()}`);
|
2016-07-25 12:36:43 -07:00
|
|
|
|
2023-02-25 00:31:35 +01:00
|
|
|
// only these actions are pruned
|
|
|
|
|
const actions = [
|
|
|
|
|
exports.ACTION_USER_LOGIN,
|
|
|
|
|
exports.ACTION_USER_LOGIN_GHOST,
|
|
|
|
|
exports.ACTION_USER_LOGOUT,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let query = `SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE creationTime <= ? AND (`;
|
2024-06-12 10:46:23 +02:00
|
|
|
const data = [ creationTime ];
|
2023-02-25 00:31:35 +01:00
|
|
|
actions.forEach(function (action, i) {
|
|
|
|
|
query += ' action = ? ';
|
|
|
|
|
data.push(action);
|
|
|
|
|
if (i < actions.length-1) query += ' OR ';
|
|
|
|
|
});
|
|
|
|
|
query += ' ) ';
|
|
|
|
|
|
|
|
|
|
const results = await database.query(query, data);
|
2016-07-25 12:36:43 -07:00
|
|
|
|
2021-06-01 09:35:20 -07:00
|
|
|
for (const result of results) {
|
|
|
|
|
await database.query('DELETE FROM notifications WHERE eventId=?', [ result.id ]); // remove notifications that reference the events as well
|
|
|
|
|
await database.query('DELETE FROM eventlog WHERE id=?', [ result.id ]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function clear() {
|
|
|
|
|
await database.query('DELETE FROM eventlog');
|
2016-07-25 12:36:43 -07:00
|
|
|
}
|