Add fix for missing deletion of notes on user-deletion request

Depending on how the system was setup, this bug lead to keep user's data
around even after a successful deletion of user'S account. This patch
will make sure the missing database constraints are implemented and
missed out deletions are executed.

This bug was introduced to insufficent testing after implementing the
feature initially. It was well tested, using the app process itself, but
the migrations where missed out. I'm currently not sure, if there was
also a change in how sequelize handles cassaded deletion, since I'm
unter the impression that before switching to sequelize 5, this feature
has worked. But I haven't verified this.

No matter what, the cleanup process is rather straight forward and will
be invoked on migration, but can also be done manually using the new
`bin/cleanup` script.

This change will result in a release 1.6.1.

Signed-off-by: Sheogorath <sheogorath@shivering-isles.com>
This commit is contained in:
Sheogorath 2020-03-21 16:07:14 +01:00
parent 8ce7b28563
commit a9d98d4b52
No known key found for this signature in database
GPG key ID: C9B1C80737B9CE18
2 changed files with 117 additions and 0 deletions

56
bin/cleanup Executable file
View file

@ -0,0 +1,56 @@
#!/usr/bin/env node
const logger = require("../lib/logger");
const models = require('../lib/models')
logger.info("Cleaning up notes that should already have been removed. Sorry.")
async function cleanup() {
await models.Note.findAll({
include: [{
model: models.User,
as: 'owner'
}]
}).then(async function(notes) {
for(let i =0, noteCount = notes.length; i< noteCount; i++) {
const item = notes[i]
if(item.ownerId != null && !item.owner) {
await models.Note.destroy({
where: {
id: item.id
}})
await models.Revision.destroy({
where: {
noteId: item.id
}
})
await models.Author.destroy({
where: {
noteId: item.id
}
})
logger.info(`Deleted note ${item.id} from user ${item.ownerId}`)
}
}
})
await models.Author.findAll({
include: [{
model: models.User,
as: 'user'
}]
}).then(async function(authors) {
for(let i =0, authorCount = authors.length; i< authorCount; i++) {
const item = authors[i]
if(item.userId != null && !item.user) {
await models.Author.destroy({
where: {
id: item.id
}})
logger.info(`Deleted authorship ${item.id} from user ${item.userId}`)
}
}
})
process.exit(0)
}
cleanup()

View file

@ -0,0 +1,61 @@
'use strict'
const { spawnSync } = require('child_process')
const path = require('path')
module.exports = {
up: function (queryInterface, Sequelize) {
const cleanup = spawnSync('./bin/cleanup', { cwd: path.resolve(__dirname, '../../') })
if (cleanup.status !== 0) {
throw new Error('Unable to cleanup')
}
return queryInterface.addConstraint('Notes', ['ownerId'], {
type: 'foreign key',
name: 'Notes_owner_fkey',
references: {
table: 'Users',
field: 'id'
},
onDelete: 'cascade'
}).then(function () {
return queryInterface.addConstraint('Revisions', ['noteId'], {
type: 'foreign key',
name: 'Revisions_note_fkey',
references: {
table: 'Notes',
field: 'id'
},
onDelete: 'cascade'
})
}).then(function () {
return queryInterface.addConstraint('Authors', ['noteId'], {
type: 'foreign key',
name: 'Author_note_fkey',
references: {
table: 'Notes',
field: 'id'
},
onDelete: 'cascade'
})
}).then(function () {
return queryInterface.addConstraint('Authors', ['userId'], {
type: 'foreign key',
name: 'Author_user_fkey',
references: {
table: 'Users',
field: 'id'
},
onDelete: 'cascade'
})
})
},
down: function (queryInterface, Sequelize) {
return queryInterface.removeConstraint('Notes', 'Notes_owner_fkey')
.then(function () {
return queryInterface.removeConstraint('Revisions', 'Revisions_note_fkey')
}).then(function () {
return queryInterface.removeConstraint('Authors', 'Author_note_fkey')
}).then(function () {
return queryInterface.removeConstraint('Authors', 'Author_user_fkey')
})
}
}