allow closing all active imap sessions of an user

This commit is contained in:
Andris Reinman 2017-10-02 16:30:27 +03:00
parent 7edaf8e0d0
commit a7ec6f9158
6 changed files with 125 additions and 3 deletions

View file

@ -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

View file

@ -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, {

View file

@ -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, {

View file

@ -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

View file

@ -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');

View file

@ -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;