mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-06 21:24:37 +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
|
||||
|
||||
####/users/{user}
|
||||
#### PUT /users/{user}
|
||||
|
||||
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
|
||||
|
||||
#### POST /users/{user}/password/reset
|
||||
|
|
|
@ -119,7 +119,8 @@ function authenticate(connection, token, callback) {
|
|||
username,
|
||||
'PLAIN'
|
||||
);
|
||||
connection.session.user = response.user;
|
||||
|
||||
connection.setUser(response.user);
|
||||
connection.state = 'Authenticated';
|
||||
|
||||
callback(null, {
|
||||
|
|
|
@ -112,7 +112,8 @@ module.exports = {
|
|||
username,
|
||||
'LOGIN'
|
||||
);
|
||||
this.session.user = response.user;
|
||||
|
||||
this.setUser(response.user);
|
||||
this.state = 'Authenticated';
|
||||
|
||||
callback(null, {
|
||||
|
|
|
@ -10,6 +10,7 @@ const crypto = require('crypto');
|
|||
const os = require('os');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const packageInfo = require('../../package');
|
||||
const errors = require('../../lib/errors.js');
|
||||
|
||||
const SOCKET_TIMEOUT = 30 * 60 * 1000;
|
||||
|
||||
|
@ -86,6 +87,13 @@ class IMAPConnection extends EventEmitter {
|
|||
// increment connection count
|
||||
this._closing = 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;
|
||||
}
|
||||
|
||||
this._server.notifier.removeListener(this.session, '*', this._accountListener);
|
||||
|
||||
this._parser = false;
|
||||
|
||||
this.state = 'Closed';
|
||||
|
@ -251,6 +261,8 @@ class IMAPConnection extends EventEmitter {
|
|||
return;
|
||||
}
|
||||
|
||||
errors.notifyConnection(this.this, err);
|
||||
|
||||
this._server.logger.error(
|
||||
{
|
||||
err,
|
||||
|
@ -731,6 +743,11 @@ class IMAPConnection extends EventEmitter {
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
setUser(user) {
|
||||
this.session.user = user;
|
||||
this._server.notifier.addListener(this.session, '*', this._accountListener);
|
||||
}
|
||||
}
|
||||
|
||||
// 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) => {
|
||||
res.charSet('utf-8');
|
||||
|
||||
|
|
|
@ -1242,6 +1242,36 @@ class UserHandler {
|
|||
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;
|
||||
|
|
Loading…
Add table
Reference in a new issue