added new API endpoint to get info about deleted users

This commit is contained in:
Andris Reinman 2021-06-21 15:17:31 +03:00
parent c50c5097f0
commit 5536bc0f93
5 changed files with 162 additions and 2 deletions

View file

@ -1,6 +1,6 @@
# If enabled then WildDuck exposes a LMTP interface for pushing messages to mail store
# NB! If you are using WildDuck plugin for Haraka then LMTP is not needed
enabled=true
enabled=false
port=2424
# by default bind to localhost only

View file

@ -2350,6 +2350,29 @@ paths:
type: string
'/users/{id}/restore':
get:
tags:
- Users
summary: Return recovery info for a deleted user
operationId: restoreUserInfo
parameters:
- name: sess
in: query
description: Session identifier for the logs
schema:
type: string
- name: ip
in: query
description: IP address for the logs
schema:
type: string
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/RecoverInfoResponse'
post:
tags:
- Users
@ -4109,6 +4132,50 @@ components:
description: Unique ID (24 byte hex)
example: '609d201236d1d936948f23b1'
RecoverInfoResponse:
type: object
required:
- success
- user
- username
- storageUsed
- tags
- deleted
- recoverableAddresses
properties:
success:
type: boolean
description: Indicates successful response
example: true
user:
type: string
description: ID of the deleted User
example: '609d201236d1d936948f23b1'
username:
type: string
description: Username of the User
example: andris
storageUsed:
type: number
description: Calculated quota usage for the user
example: 2423070
tags:
type: array
items:
type: string
description: List of tags associated with the User
example: ['domain:andrisreinman.com']
deleted:
type: string
description: Datestring of the time the user was deleted
format: date-time
recoverableAddresses:
type: array
items:
type: string
description: List of email addresses that can be restored
example: ['andris@andrisreinman.com']
GetAllowedDomainResponse:
required:
- success

View file

@ -192,7 +192,6 @@ const acquireCert = async (domain, acmeOptions, certificateData, certHandler) =>
}
let lock = await getLock(domainOpLockKey, 10 * 60 * 1000, 3 * 60 * 1000);
console.log(lock);
if (!lock.success) {
return certificateData;
}

View file

@ -1401,6 +1401,66 @@ module.exports = (db, server, userHandler) => {
})
);
server.get(
'/users/:user/restore',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
sess: sessSchema,
ip: sessIPSchema
});
const result = schema.validate(req.params, {
abortEarly: false,
convert: true
});
if (result.error) {
res.status(400);
res.json({
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
});
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('users'));
} else {
req.validate(roles.can(req.role).readAny('users'));
}
let user = new ObjectID(result.value.user);
let userInfo;
try {
userInfo = await userHandler.restoreInfo(user);
} catch (err) {
res.status(err.responseCode || 500); // TODO: use response code specific status
res.json({
error: err.message,
code: err.code
});
return next();
}
res.json(
Object.assign(
{
success: !!userInfo
},
userInfo
)
);
return next();
})
);
server.post(
'/users/:user/restore',
tools.asyncifyJson(async (req, res, next) => {

View file

@ -3551,6 +3551,40 @@ class UserHandler {
return result;
}
// Return information about deleted user
async restoreInfo(user) {
let result = {
user: user.toString()
};
let existingAccount = await this.users.collection('deletedusers').findOne({ _id: user });
if (!existingAccount) {
let err = new Error('Delete account was not found');
err.responseCode = 404;
err.code = 'AccountNotFound';
throw err;
}
result.username = existingAccount.username;
result.storageUsed = existingAccount.storageUsed;
result.tags = existingAccount.tags;
result.deleted = existingAccount.deleteInfo.deletedAt.toISOString();
// Step 2. restore addresses
let recoverableAddresses = [];
for (let address of existingAccount.deleteInfo.addresses || []) {
let existingAddress = await this.users.collection('addresses').findOne(address);
if (!existingAddress || existingAddress.user.equals(user)) {
recoverableAddresses.push(address.address);
}
}
result.recoverableAddresses = recoverableAddresses;
return result;
}
// This method restores a user that is queued for deletion
async restore(user, options) {
options = options || {};