feat(audit-core, xo-server-audit): ability to delete a range of records (#5277)

This commit is contained in:
badrAZ
2020-10-19 13:09:08 +02:00
committed by GitHub
parent 48eeab974c
commit f41cca45aa
5 changed files with 82 additions and 6 deletions
+1
View File
@@ -30,6 +30,7 @@
"rimraf": "^3.0.0" "rimraf": "^3.0.0"
}, },
"dependencies": { "dependencies": {
"@xen-orchestra/log": "^0.2.0",
"core-js": "^3.6.4", "core-js": "^3.6.4",
"golike-defer": "^0.4.1", "golike-defer": "^0.4.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
+54 -1
View File
@@ -2,9 +2,12 @@
import 'core-js/features/symbol/async-iterator' import 'core-js/features/symbol/async-iterator'
import assert from 'assert' import assert from 'assert'
import createLogger from '@xen-orchestra/log'
import defer from 'golike-defer' import defer from 'golike-defer'
import hash from 'object-hash' import hash from 'object-hash'
const log = createLogger('xo:audit-core')
export class Storage { export class Storage {
constructor() { constructor() {
this._lock = Promise.resolve() this._lock = Promise.resolve()
@@ -65,8 +68,17 @@ export class AuditCore {
@defer @defer
async add($defer, subject, event, data) { async add($defer, subject, event, data) {
const time = Date.now() const time = Date.now()
$defer(await this._storage.acquireLock())
return this._addUnsafe({
data,
event,
subject,
time,
})
}
async _addUnsafe({ data, event, subject, time }) {
const storage = this._storage const storage = this._storage
$defer(await storage.acquireLock())
// delete "undefined" properties and normalize data with JSON.stringify // delete "undefined" properties and normalize data with JSON.stringify
const record = JSON.parse( const record = JSON.parse(
@@ -139,4 +151,45 @@ export class AuditCore {
await this._storage.del(id) await this._storage.del(id)
} }
} }
@defer
async deleteRangeAndRewrite($defer, newest, oldest) {
assert.notStrictEqual(newest, undefined)
assert.notStrictEqual(oldest, undefined)
const storage = this._storage
$defer(await storage.acquireLock())
assert.notStrictEqual(await storage.get(newest), undefined)
const oldestRecord = await storage.get(oldest)
assert.notStrictEqual(oldestRecord, undefined)
const lastId = await storage.getLastId()
const recentRecords = []
for await (const record of this.getFrom(lastId)) {
if (record.id === newest) {
break
}
recentRecords.push(record)
}
for await (const record of this.getFrom(newest)) {
await storage.del(record.id)
if (record.id === oldest) {
break
}
}
await storage.setLastId(oldestRecord.previousId)
for (const record of recentRecords) {
try {
await this._addUnsafe(record)
await storage.del(record.id)
} catch (error) {
log.error(error)
}
}
}
} }
@@ -17,9 +17,10 @@ interface Record {
} }
export class AuditCore { export class AuditCore {
constructor(storage: Storage) {} constructor(storage: Storage) { }
public add(subject: any, event: string, data: any): Promise<Record> {} public add(subject: any, event: string, data: any): Promise<Record> { }
public checkIntegrity(oldest: string, newest: string): Promise<number> {} public checkIntegrity(oldest: string, newest: string): Promise<number> { }
public getFrom(newest?: string): AsyncIterator {} public getFrom(newest?: string): AsyncIterator { }
public deleteFrom(newest: string): Promise<void> {} public deleteFrom(newest: string): Promise<void> { }
public deleteRangeAndRewrite(newest: string, oldest: string): Promise<void> { }
} }
+3
View File
@@ -27,3 +27,6 @@
> - major: if the change breaks compatibility > - major: if the change breaks compatibility
> >
> In case of conflict, the highest (lowest in previous list) `$version` wins. > In case of conflict, the highest (lowest in previous list) `$version` wins.
- @xen-orchestra/audit-core minor
- xo-server-audit minor
+18
View File
@@ -20,6 +20,7 @@ const DEFAULT_BLOCKED_LIST = {
'acl.getCurrentPermissions': true, 'acl.getCurrentPermissions': true,
'audit.checkIntegrity': true, 'audit.checkIntegrity': true,
'audit.clean': true, 'audit.clean': true,
'audit.deleteRange': true,
'audit.generateFingerprint': true, 'audit.generateFingerprint': true,
'audit.getRecords': true, 'audit.getRecords': true,
'backup.list': true, 'backup.list': true,
@@ -238,11 +239,21 @@ class AuditXoPlugin {
clean.permission = 'admin' clean.permission = 'admin'
clean.description = 'Clean audit database' clean.description = 'Clean audit database'
const deleteRange = this._deleteRangeAndRewrite.bind(this)
deleteRange.description =
'Delete a range of records and rewrite the records chain'
deleteRange.permission = 'admin'
deleteRange.params = {
newest: { type: 'string' },
oldest: { type: 'string', optional: true },
}
cleaners.push( cleaners.push(
this._xo.addApiMethods({ this._xo.addApiMethods({
audit: { audit: {
checkIntegrity, checkIntegrity,
clean, clean,
deleteRange,
exportRecords, exportRecords,
generateFingerprint, generateFingerprint,
getRecords, getRecords,
@@ -428,6 +439,13 @@ class AuditXoPlugin {
throw error throw error
} }
} }
async _deleteRangeAndRewrite({ newest, oldest = newest }) {
await this._auditCore.deleteRangeAndRewrite(newest, oldest)
if (this._uploadLastHashJob !== undefined) {
await this._uploadLastHash()
}
}
} }
AuditXoPlugin.prototype._getRecordsStream = asyncIteratorToStream( AuditXoPlugin.prototype._getRecordsStream = asyncIteratorToStream(