added API endpoint to reset user password

This commit is contained in:
Andris Reinman 2017-08-05 15:55:29 +03:00
parent ea880e4a2e
commit 463bc61aeb
4 changed files with 82 additions and 12 deletions

View file

@ -321,6 +321,33 @@ Response for a successful operation:
}
```
### Reset user password
#### POST /users/{user}/password/reset
Generates a new temporary password and resets 2FA if set. Once user password is reset, then authentication results
will include `requirePasswordChange: true` parameter. This means that the user should not be able to perform regular
actions before the password has been changed.
**Parameters**
- **user** (required) is the ID of the user
**Example**
```
curl -XPOST "http://localhost:8080/users/59467f27535f8f0f067ba8e6/password/reset" -H 'content-type: application/json' -d '{}'
```
Response for a successful operation:
```json
{
"success":true,
"password": "somesecretvalue"
}
```
## Authentication
### Authenticate an user
@ -343,7 +370,8 @@ Authenticates an user
- **id** is the id of the authenticated user
- **username** is the user name of the logged in user (useful if you logged in used)
- **scope** is the scope this authentication is valid for
- **require2fa** if `true` the the user should also [provide a 2FA token](#verify-2fa) before the user is allowed to proceed
- **require2fa** if `true` then the user should also [provide a 2FA token](#verify-2fa) before the user is allowed to proceed
- **requirePasswordChange** if `true` then the user should be forced to change their password
**Example**
@ -364,7 +392,8 @@ Response for a successful operation:
"id": "5971da1754cfdc7f0983b2ec",
"username": "testuser",
"scope": "pop3",
"require2fa": false
"require2fa": false,
"requirePasswordChange": false
}
```
@ -417,10 +446,11 @@ Wild Duck supports TOTP based 2FA. If 2FA is enabled then users are requested to
2FA checks do not happen magically, your application must be 2FA aware:
1. Authenticate user with the [/authenticate](#authenticate-an-user) call
2. If authentication ends with `require2fa:false` then do nothing, otherwise continue with Step 3.
3. Request TOTP token from the user before allowing to perform other actions
4. Check the token with [/user/{user}/2fa?token=123456](#check-2fa)
5. If token verification succeeds then user is authenticated
2. If authentication result includes `requirePasswordChange:true` then force user to change their password
3. If authentication result includes `require2fa:false` then do nothing, the user is now authenticated. Otherwise continue with Step 4.
4. Request TOTP token from the user before allowing to perform other actions
5. Check the token with [/user/{user}/2fa?token=123456](#check-2fa)
6. If token verification succeeds then user is authenticated
### Setup 2FA

View file

@ -58,7 +58,8 @@ module.exports = (db, server, userHandler) => {
id: authData.user,
username: authData.username,
scope: authData.scope,
require2fa: authData.require2fa
require2fa: authData.require2fa,
requirePasswordChange: authData.requirePasswordChange
});
return next();

View file

@ -413,7 +413,7 @@ module.exports = (db, server, userHandler) => {
return next();
}
let user = new ObjectID(result.value.iuserd);
let user = new ObjectID(result.value.user);
db.users.collection('users').findOne({
_id: user
@ -506,6 +506,42 @@ module.exports = (db, server, userHandler) => {
});
});
});
server.post('/users/:user/password/reset', (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required()
});
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message
});
return next();
}
let user = new ObjectID(result.value.user);
userHandler.reset(user, (err, password) => {
if (err) {
res.json({
error: err.message
});
return next();
}
res.json({
success: true,
password
});
return next();
});
});
};
function checkPubKey(pubKey, done) {

View file

@ -389,6 +389,8 @@ class UserHandler {
created: new Date(),
requirePasswordChange: false,
// until setup value is not true, this account is not usable
activated: false,
disabled: true,
@ -564,7 +566,7 @@ class UserHandler {
});
}
reset(username, callback) {
reset(user, callback) {
let password = generatePassword.generate({
length: 12,
uppercase: true,
@ -573,7 +575,7 @@ class UserHandler {
});
return this.users.collection('users').findOneAndUpdate({
username
_id: user
}, {
$set: {
enabled2fa: false,
@ -583,12 +585,12 @@ class UserHandler {
}
}, {}, (err, result) => {
if (err) {
log.error('DB', 'UPDATEFAIL username=%s error=%s', username, err.message);
log.error('DB', 'UPDATEFAIL id=%s error=%s', user, err.message);
return callback(new Error('Database Error, failed to reset user credentials'));
}
if (!result || !result.value) {
return callback(new Error('Could not update user'));
return callback(new Error('Could not update user ' + user));
}
return callback(null, password);
@ -847,6 +849,7 @@ class UserHandler {
}
if (key === 'password') {
$set.password = bcrypt.hashSync(data[key], consts.BCRYPT_ROUNDS);
$set.requirePasswordChange = false;
$set.passwordChange = new Date();
passwordChanged = true;
return;