Audit logs react to license updates (with logs)

This commit is contained in:
Rémi de Juvigny 2023-02-24 15:48:36 +01:00
parent 0dcfbde89f
commit 43ceb02092
4 changed files with 117 additions and 13 deletions

View File

@ -1,15 +1,13 @@
'use strict';
const { features } = require('@strapi/strapi/lib/utils/ee');
const executeCERegister = require('../../server/register');
const migrateAuditLogsTable = require('./migrations/audit-logs-table');
const createAuditLogsService = require('./services/audit-logs');
module.exports = async ({ strapi }) => {
const auditLogsIsAllowed = features.isEnabled('audit-logs');
const auditLogsIsEnabled = strapi.config.get('server.auditLogs.enabled', true);
const auditLogsIsEnabled = strapi.config.get('admin.auditLogs.enabled', true);
if (auditLogsIsAllowed && auditLogsIsEnabled) {
if (auditLogsIsEnabled) {
strapi.hook('strapi::content-types.beforeSync').register(migrateAuditLogsTable);
const auditLogsService = createAuditLogsService(strapi);
strapi.container.register('audit-logs', auditLogsService);

View File

@ -53,6 +53,24 @@ const getEventMap = (defaultEvents) => {
}, {});
};
const getRetentionDays = (strapi) => {
const licenseRetentionDays = features.get('audit-logs')?.options.retentionDays;
const userRetentionDays = strapi.config.get('admin.auditLogs.retentionDays');
// For enterprise plans, use 90 days by default, but allow users to override it
if (licenseRetentionDays == null) {
return userRetentionDays ?? DEFAULT_RETENTION_DAYS;
}
// Allow users to override the license retention days, but not to increase it
if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
return userRetentionDays;
}
// User didn't provide a retention days value, use the license one
return licenseRetentionDays;
};
const createAuditLogsService = (strapi) => {
// NOTE: providers should be able to replace getEventMap to add or remove events
const eventMap = getEventMap(defaultEvents);
@ -99,10 +117,45 @@ const createAuditLogsService = (strapi) => {
return {
async register() {
const retentionDays =
features.get('audit-logs')?.options.retentionDays ?? DEFAULT_RETENTION_DAYS;
console.log('audit logs register');
// Handle license being enabled
if (!this._eeEnableUnsubscribe) {
this._eeEnableUnsubscribe = strapi.eventHub.once('ee.enable', () => {
console.log('listened to ee.enable');
// Recreate the service to use the new license info
this.destroy();
this.register();
});
}
// Handle license being updated
this._eeUpdateUnsubscribe = strapi.eventHub.on('ee.update', () => {
console.log('listened to ee.update');
// Recreate the service to use the new license info
this.destroy();
this.register();
});
// Handle license being disabled
this._eeDisableUnsubscribe = strapi.eventHub.on('ee.disable', () => {
console.log('listened to ee.disable');
// Turn off service when the license gets disabled
// Only the ee.enable listener remains active to recreate the service
this.destroy();
});
// Check current state of license
if (!features.isEnabled('audit-logs')) {
return this;
}
// Start saving events
this._provider = await localProvider.register({ strapi });
this._eventHubUnsubscribe = strapi.eventHub.subscribe(handleEvent.bind(this));
// Manage audit logs auto deletion
const retentionDays = getRetentionDays(strapi);
console.log('registering audit logs service', retentionDays);
this._deleteExpiredJob = scheduleJob('0 0 * * *', () => {
const expirationDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000);
this._provider.deleteExpiredEvents(expirationDate);
@ -143,6 +196,14 @@ const createAuditLogsService = (strapi) => {
},
unsubscribe() {
if (this._eeUpdateUnsubscribe) {
this._eeUpdateUnsubscribe();
}
if (this._eeDisableUnsubscribe) {
this._eeDisableUnsubscribe();
}
if (this._eventHubUnsubscribe) {
this._eventHubUnsubscribe();
}
@ -155,6 +216,7 @@ const createAuditLogsService = (strapi) => {
},
destroy() {
console.log('audit logs destroy');
return this.unsubscribe();
},
};

View File

@ -1,12 +1,12 @@
'use strict';
const { pick } = require('lodash/fp');
const { pick, isEqual } = require('lodash/fp');
const { readLicense, verifyLicense, fetchLicense, LicenseCheckError } = require('./license');
const { eeStoreModel } = require('./ee-store');
const { shiftCronExpression } = require('../lib/utils/cron');
const ONE_MINUTE = 1000 * 60;
const ONE_MINUTE = 1 * 60;
const ee = {
enabled: false,
@ -14,10 +14,33 @@ const ee = {
};
const disable = (message) => {
// Prevent emitting ee.disable if it was already disabled
const shouldEmitEvent = ee.enabled !== false;
ee.logger?.warn(`${message} Switching to CE.`);
// Only keep the license key for potential re-enabling during a later check
ee.licenseInfo = pick('licenseKey', ee.licenseInfo);
// Prevent emitting ee.disable if it was already disabled
console.log('disabling EE');
ee.enabled = false;
if (shouldEmitEvent) {
// Notify EE features that they should be disabled
strapi.eventHub.emit('ee.disable');
}
};
const enable = () => {
// Prevent emitting ee.disable if it was already disabled
const shouldEmitEvent = ee.enabled !== true;
ee.enabled = true;
if (shouldEmitEvent) {
// Notify EE features that they should be disabled
strapi.eventHub.emit('ee.enable');
}
};
let initialized = false;
@ -42,7 +65,7 @@ const init = (licenseDir, logger) => {
if (license) {
ee.licenseInfo = verifyLicense(license);
ee.enabled = true;
enable();
}
} catch (error) {
disable(error.message);
@ -55,6 +78,7 @@ const init = (licenseDir, logger) => {
* Store the result in database to avoid unecessary requests, and will fallback to that in case of a network failure.
*/
const onlineUpdate = async ({ strapi }) => {
console.log('onlineUpdate');
const { get, commit, rollback } = await strapi.db.transaction();
const transaction = get();
@ -90,8 +114,22 @@ const onlineUpdate = async ({ strapi }) => {
if (license) {
try {
ee.licenseInfo = verifyLicense(license);
// Verify license and check if its info changed
const newLicenseInfo = verifyLicense(license);
const fieldsToCompare = ['licenseKey', 'features', 'plan'];
const licenseInfoChanged = !isEqual(
pick(fieldsToCompare, newLicenseInfo),
pick(fieldsToCompare, ee.licenseInfo)
);
// Store the new license info
ee.licenseInfo = newLicenseInfo;
validateInfo();
// Notify EE features
if (licenseInfoChanged) {
strapi.eventHub.emit('ee.update');
}
} catch (error) {
disable(error.message);
}
@ -126,7 +164,8 @@ const validateInfo = () => {
return disable('License expired.');
}
ee.enabled = true;
// Prevent emitting ee.enable if it was already enabled
enable();
};
const checkLicense = async ({ strapi }) => {
@ -137,7 +176,9 @@ const checkLicense = async ({ strapi }) => {
if (!shouldStayOffline) {
await onlineUpdate({ strapi });
strapi.cron.add({ [shiftCronExpression('0 0 */12 * * *')]: onlineUpdate });
// strapi.cron.add({ [shiftCronExpression('0 0 */12 * * *')]: onlineUpdate });
strapi.cron.add({ [shiftCronExpression('*/10 * * * * *')]: onlineUpdate });
} else {
if (!ee.licenseInfo.expireAt) {
return disable('Your license does not have offline support.');

View File

@ -4,7 +4,10 @@ const auditLogContentType = require('./content-types/audit-log');
const provider = {
async register({ strapi }) {
strapi.container.get('content-types').add('admin::', { 'audit-log': auditLogContentType });
const contentTypes = strapi.container.get('content-types');
if (!contentTypes.keys().includes('admin::audit-log')) {
strapi.container.get('content-types').add('admin::', { 'audit-log': auditLogContentType });
}
// Return the provider object
return {