Implement self-service account deletion
This commit is contained in:
parent
37c6fa3f79
commit
aa2348656d
13 changed files with 426 additions and 19 deletions
161
src/web/routes/account/delete-account.js
Normal file
161
src/web/routes/account/delete-account.js
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import { sendPug } from '../../pug';
|
||||
import Config from '../../../config';
|
||||
import { eventlog } from '../../../logger';
|
||||
const verifySessionAsync = require('bluebird').promisify(
|
||||
require('../../../session').verifySession
|
||||
);
|
||||
|
||||
const LOGGER = require('@calzoneman/jsli')('web/routes/account/delete-account');
|
||||
|
||||
export default function initialize(
|
||||
app,
|
||||
csrfVerify,
|
||||
channelDb,
|
||||
userDb,
|
||||
emailConfig,
|
||||
emailController
|
||||
) {
|
||||
app.get('/account/delete', async (req, res) => {
|
||||
if (!await authorize(req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await showDeletePage(res, {});
|
||||
});
|
||||
|
||||
app.post('/account/delete', async (req, res) => {
|
||||
if (!await authorize(req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
csrfVerify(req);
|
||||
|
||||
if (!req.body.confirmed) {
|
||||
await showDeletePage(res, { missingConfirmation: true });
|
||||
return;
|
||||
}
|
||||
|
||||
let user;
|
||||
try {
|
||||
user = await userDb.verifyLoginAsync(res.locals.loginName, req.body.password);
|
||||
} catch (error) {
|
||||
if (error.message === 'Invalid username/password combination') {
|
||||
res.status(403);
|
||||
await showDeletePage(res, { wrongPassword: true });
|
||||
} else if (error.message === 'User does not exist' ||
|
||||
error.message.match(/Invalid username/)) {
|
||||
LOGGER.error('User does not exist after authorization');
|
||||
res.status(503);
|
||||
await showDeletePage(res, { internalError: true });
|
||||
} else {
|
||||
res.status(503);
|
||||
LOGGER.error('Unknown error in verifyLogin: %s', error.stack);
|
||||
await showDeletePage(res, { internalError: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let channels = await channelDb.listUserChannelsAsync(user.name);
|
||||
if (channels.length > 0) {
|
||||
await showDeletePage(res, { channelCount: channels.length });
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
LOGGER.error('Unknown error in listUserChannels: %s', error.stack);
|
||||
await showDeletePage(res, { internalError: true });
|
||||
}
|
||||
|
||||
try {
|
||||
await userDb.requestAccountDeletion(user.id);
|
||||
eventlog.log(`[account] ${req.ip} requested account deletion for ${user.name}`);
|
||||
} catch (error) {
|
||||
LOGGER.error('Unknown error in requestAccountDeletion: %s', error.stack);
|
||||
await showDeletePage(res, { internalError: true });
|
||||
}
|
||||
|
||||
if (emailConfig.getDeleteAccount().isEnabled() && user.email) {
|
||||
await sendEmail(user);
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
'Skipping account deletion email notification for %s',
|
||||
user.name
|
||||
);
|
||||
}
|
||||
|
||||
res.clearCookie('auth', { domain: Config.get('http.root-domain-dotted') });
|
||||
res.locals.loggedIn = false;
|
||||
res.locals.loginName = null;
|
||||
sendPug(
|
||||
res,
|
||||
'account-deleted',
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
async function showDeletePage(res, flags) {
|
||||
let locals = Object.assign({ channelCount: 0 }, flags);
|
||||
|
||||
if (res.locals.loggedIn) {
|
||||
let channels = await channelDb.listUserChannelsAsync(
|
||||
res.locals.loginName
|
||||
);
|
||||
locals.channelCount = channels.length;
|
||||
} else {
|
||||
res.status(401);
|
||||
}
|
||||
|
||||
sendPug(
|
||||
res,
|
||||
'account-delete',
|
||||
locals
|
||||
);
|
||||
}
|
||||
|
||||
async function authorize(req, res) {
|
||||
try {
|
||||
if (!res.locals.loggedIn) {
|
||||
res.status(401);
|
||||
await showDeletePage(res, {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req.signedCookies || !req.signedCookies.auth) {
|
||||
throw new Error('Missing auth cookie');
|
||||
}
|
||||
|
||||
await verifySessionAsync(req.signedCookies.auth);
|
||||
return true;
|
||||
} catch (error) {
|
||||
res.status(401);
|
||||
sendPug(
|
||||
res,
|
||||
'account-delete',
|
||||
{ authFailed: true, reason: error.message }
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function sendEmail(user) {
|
||||
LOGGER.info(
|
||||
'Sending email notification for account deletion %s <%s>',
|
||||
user.name,
|
||||
user.email
|
||||
);
|
||||
|
||||
try {
|
||||
await emailController.sendAccountDeletion({
|
||||
username: user.name,
|
||||
address: user.email
|
||||
});
|
||||
} catch (error) {
|
||||
LOGGER.error(
|
||||
'Sending email notification failed for %s <%s>: %s',
|
||||
user.name,
|
||||
user.email,
|
||||
error.stack
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +203,15 @@ module.exports = {
|
|||
require('./routes/contact')(app, webConfig);
|
||||
require('./auth').init(app);
|
||||
require('./account').init(app, globalMessageBus, emailConfig, emailController);
|
||||
require('./routes/account/delete-account')(
|
||||
app,
|
||||
csrf.verify,
|
||||
require('../database/channels'),
|
||||
require('../database/accounts'),
|
||||
emailConfig,
|
||||
emailController
|
||||
);
|
||||
|
||||
require('./acp').init(app, ioConfig);
|
||||
require('../google2vtt').attach(app);
|
||||
require('./routes/google_drive_userscript')(app);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue