mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-13 16:45:27 +08:00
allow closing all active imap sessions of an user
This commit is contained in:
parent
7edaf8e0d0
commit
a7ec6f9158
6 changed files with 125 additions and 3 deletions
29
docs/api.md
29
docs/api.md
|
@ -283,7 +283,7 @@ After you have created an user you can use these credentials to log in to the IM
|
||||||
|
|
||||||
### Update user details
|
### Update user details
|
||||||
|
|
||||||
####/users/{user}
|
#### PUT /users/{user}
|
||||||
|
|
||||||
Updates the properties of an user. Only specify these fields that you want to be updated.
|
Updates the properties of an user. Only specify these fields that you want to be updated.
|
||||||
|
|
||||||
|
@ -322,6 +322,33 @@ Response for a successful operation:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Log out user from all IMAP sessions
|
||||||
|
|
||||||
|
#### PUT /users/{user}/logout
|
||||||
|
|
||||||
|
Forces closing all active IMAP session of an user
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- **user** (required) is the ID of the user
|
||||||
|
- **reason** is an optional message to be sent to the user with logout notification
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -XPUT "http://localhost:8080/users/59467f27535f8f0f067ba8e6/logout" -H 'content-type: application/json' -d '{
|
||||||
|
"reaosn": "Account was deleted"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Response for a successful operation:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Reset user password
|
### Reset user password
|
||||||
|
|
||||||
#### POST /users/{user}/password/reset
|
#### POST /users/{user}/password/reset
|
||||||
|
|
|
@ -119,7 +119,8 @@ function authenticate(connection, token, callback) {
|
||||||
username,
|
username,
|
||||||
'PLAIN'
|
'PLAIN'
|
||||||
);
|
);
|
||||||
connection.session.user = response.user;
|
|
||||||
|
connection.setUser(response.user);
|
||||||
connection.state = 'Authenticated';
|
connection.state = 'Authenticated';
|
||||||
|
|
||||||
callback(null, {
|
callback(null, {
|
||||||
|
|
|
@ -112,7 +112,8 @@ module.exports = {
|
||||||
username,
|
username,
|
||||||
'LOGIN'
|
'LOGIN'
|
||||||
);
|
);
|
||||||
this.session.user = response.user;
|
|
||||||
|
this.setUser(response.user);
|
||||||
this.state = 'Authenticated';
|
this.state = 'Authenticated';
|
||||||
|
|
||||||
callback(null, {
|
callback(null, {
|
||||||
|
|
|
@ -10,6 +10,7 @@ const crypto = require('crypto');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const EventEmitter = require('events').EventEmitter;
|
const EventEmitter = require('events').EventEmitter;
|
||||||
const packageInfo = require('../../package');
|
const packageInfo = require('../../package');
|
||||||
|
const errors = require('../../lib/errors.js');
|
||||||
|
|
||||||
const SOCKET_TIMEOUT = 30 * 60 * 1000;
|
const SOCKET_TIMEOUT = 30 * 60 * 1000;
|
||||||
|
|
||||||
|
@ -86,6 +87,13 @@ class IMAPConnection extends EventEmitter {
|
||||||
// increment connection count
|
// increment connection count
|
||||||
this._closing = false;
|
this._closing = false;
|
||||||
this._closed = false;
|
this._closed = false;
|
||||||
|
|
||||||
|
this._accountListener = message => {
|
||||||
|
if (message && message.action === 'LOGOUT') {
|
||||||
|
this.send('* BYE ' + (message.reason || 'Logout requested'));
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,6 +203,8 @@ class IMAPConnection extends EventEmitter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._server.notifier.removeListener(this.session, '*', this._accountListener);
|
||||||
|
|
||||||
this._parser = false;
|
this._parser = false;
|
||||||
|
|
||||||
this.state = 'Closed';
|
this.state = 'Closed';
|
||||||
|
@ -251,6 +261,8 @@ class IMAPConnection extends EventEmitter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errors.notifyConnection(this.this, err);
|
||||||
|
|
||||||
this._server.logger.error(
|
this._server.logger.error(
|
||||||
{
|
{
|
||||||
err,
|
err,
|
||||||
|
@ -731,6 +743,11 @@ class IMAPConnection extends EventEmitter {
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setUser(user) {
|
||||||
|
this.session.user = user;
|
||||||
|
this._server.notifier.addListener(this.session, '*', this._accountListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose to the world
|
// Expose to the world
|
||||||
|
|
|
@ -459,6 +459,52 @@ module.exports = (db, server, userHandler) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.put('/users/:user/logout', (req, res, next) => {
|
||||||
|
res.charSet('utf-8');
|
||||||
|
|
||||||
|
const schema = Joi.object().keys({
|
||||||
|
user: Joi.string()
|
||||||
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required(),
|
||||||
|
|
||||||
|
reason: Joi.string()
|
||||||
|
.empty('')
|
||||||
|
.max(128),
|
||||||
|
|
||||||
|
ip: Joi.string().ip({
|
||||||
|
version: ['ipv4', 'ipv6'],
|
||||||
|
cidr: 'forbidden'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = Joi.validate(req.params, schema, {
|
||||||
|
abortEarly: false,
|
||||||
|
convert: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
res.json({
|
||||||
|
error: result.error.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
userHandler.logout(result.value.user, result.value.reason || 'Logout requested from API', (err, success) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
success
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
server.post('/users/:user/quota/reset', (req, res, next) => {
|
server.post('/users/:user/quota/reset', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
|
|
|
@ -1242,6 +1242,36 @@ class UserHandler {
|
||||||
this.users.collection('authlog').insertOne(entry, callback);
|
this.users.collection('authlog').insertOne(entry, callback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logout(user, reason, callback) {
|
||||||
|
// register this address as the default address for that user
|
||||||
|
return this.users.collection('users').findOne({
|
||||||
|
_id: new ObjectID(user)
|
||||||
|
}, {
|
||||||
|
fields: {
|
||||||
|
_id: true
|
||||||
|
}
|
||||||
|
}, (err, userData) => {
|
||||||
|
if (err) {
|
||||||
|
log.error('DB', 'DBFAIL logout id=%s error=%s', user, err.message);
|
||||||
|
err.message = 'Database Error, failed to find user';
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
if (!userData) {
|
||||||
|
return callback(new Error('User not found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.messageHandler || !this.messageHandler.notifier) {
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messageHandler.notifier.fire(userData._id, '/', {
|
||||||
|
action: 'LOGOUT',
|
||||||
|
reason
|
||||||
|
});
|
||||||
|
return callback(null, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UserHandler;
|
module.exports = UserHandler;
|
||||||
|
|
Loading…
Add table
Reference in a new issue