mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-02-01 12:50:06 +08:00
added API endpoint to reset user password
This commit is contained in:
parent
ea880e4a2e
commit
463bc61aeb
4 changed files with 82 additions and 12 deletions
42
docs/api.md
42
docs/api.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue