Skip to content

Commit e30ba0c

Browse files
authored
fix: ZMS-44: Add require2faEnabled flag to user (#1035)
1 parent a3d1d2f commit e30ba0c

4 files changed

Lines changed: 23 additions & 1 deletion

File tree

lib/api/auth.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ module.exports = (db, server, userHandler) => {
171171
.try(Joi.array().items(Joi.string()), booleanSchema.allow(false))
172172
.required()
173173
.description('List of enabled 2FA mechanisms or false if not required'),
174+
require2faEnabled: booleanSchema
175+
.required()
176+
.description('If true then the account is flagged as requiring 2FA to be enabled'),
174177
requirePasswordChange: booleanSchema.required().description('Indicates if account password has been reset and should be replaced'),
175178
token: Joi.string().description(
176179
'If access token was requested then this is the value to use as access token when making API requests on behalf of logged in user.'
@@ -263,6 +266,7 @@ module.exports = (db, server, userHandler) => {
263266
address: authData.address,
264267
scope: authData.scope,
265268
require2fa: authData.require2fa,
269+
require2faEnabled: authData.require2faEnabled,
266270
requirePasswordChange: authData.requirePasswordChange
267271
};
268272

lib/api/users.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ module.exports = (db, server, userHandler, settingsHandler) => {
360360
requirePasswordChange: booleanSchema
361361
.default(false)
362362
.description('If true then requires the user to change password, useful if password for the account was autogenerated'),
363+
require2faEnabled: booleanSchema
364+
.default(false)
365+
.description('If true then the account is flagged as requiring 2FA to be enabled'),
363366

364367
imapMaxUpload: Joi.number().min(0).default(0).description('How many bytes can be uploaded via IMAP during 24 hour'),
365368
imapMaxDownload: Joi.number().min(0).default(0).description('How many bytes can be downloaded via IMAP during 24 hour'),
@@ -902,6 +905,9 @@ module.exports = (db, server, userHandler, settingsHandler) => {
902905
suspended: booleanSchema.required().description('If true then the user can not authenticate'),
903906
lastPwnedCheck: Joi.date().description('Date when the last check of password against the Pwned passwords list was done'),
904907
passwordPwned: booleanSchema.required().description('Specifies whether the user password has been found in Pwned passwords list'),
908+
require2faEnabled: booleanSchema
909+
.required()
910+
.description('If true then the account is flagged as requiring 2FA to be enabled'),
905911
requirePasswordChange: booleanSchema.required().description('Indicates if account password has been reset and should be replaced')
906912
}).$_setFlag('objectName', 'GetUserResponse')
907913
}
@@ -1153,6 +1159,7 @@ module.exports = (db, server, userHandler, settingsHandler) => {
11531159
suspended: !!userData.suspended,
11541160
lastPwnedCheck: userData.lastPwnedCheck,
11551161
passwordPwned: !!userData.passwordPwned,
1162+
require2faEnabled: !!userData.require2faEnabled,
11561163
requirePasswordChange: !!userData.requirePasswordChange
11571164
})
11581165
);
@@ -1258,6 +1265,7 @@ module.exports = (db, server, userHandler, settingsHandler) => {
12581265
receivedMax: Joi.number().min(0).description('How many messages can be received from MX during 60 seconds'),
12591266

12601267
disable2fa: booleanSchema.description('If true, then disables 2FA for this user'),
1268+
require2faEnabled: booleanSchema.description('If true then the account is flagged as requiring 2FA to be enabled'),
12611269

12621270
tags: Joi.array().items(Joi.string().trim().max(128)).description('A list of tags associated with this user'),
12631271

lib/user-handler.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ class UserHandler {
550550
tempPassword: true,
551551
password: true,
552552
enabled2fa: true,
553+
require2faEnabled: true,
553554
webauthn: true,
554555
disabled: true,
555556
suspended: true,
@@ -972,6 +973,7 @@ class UserHandler {
972973
address: userData.address,
973974
// if 2FA is enabled then require token validation
974975
require2fa: enabled2fa.length && !usingTemporaryPassword ? enabled2fa : false,
976+
require2faEnabled: !!userData.require2faEnabled,
975977
requirePasswordChange // true, if password was reset and using temporary password
976978
};
977979

@@ -1623,6 +1625,7 @@ class UserHandler {
16231625
retention: data.retention || 0,
16241626

16251627
disabledScopes,
1628+
require2faEnabled: !!data.require2faEnabled,
16261629

16271630
lastLogin: {
16281631
time: false,

test/api/users-test.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('API Users', function () {
3636
recipients: 2000,
3737
forwards: 2000,
3838
requirePasswordChange: false,
39+
require2faEnabled: true,
3940
imapMaxUpload: 5368709120,
4041
imapMaxDownload: 21474836480,
4142
pop3MaxDownload: 21474836480,
@@ -89,6 +90,7 @@ describe('API Users', function () {
8990
username: 'myuser2',
9091
scope: 'master',
9192
require2fa: false,
93+
require2faEnabled: true,
9294
requirePasswordChange: false
9395
});
9496
});
@@ -165,6 +167,7 @@ describe('API Users', function () {
165167
username: 'myuser2hash',
166168
scope: 'master',
167169
require2fa: false,
170+
require2faEnabled: false,
168171
requirePasswordChange: false
169172
});
170173
});
@@ -194,6 +197,7 @@ describe('API Users', function () {
194197
let response = await server.get(`/users/${user}`).expect(200);
195198
expect(response.body.success).to.be.true;
196199
expect(response.body.id).to.equal(user);
200+
expect(response.body.require2faEnabled).to.equal(true);
197201
});
198202

199203
it('should GET /users/{user} expect success / using a token', async () => {
@@ -230,7 +234,8 @@ describe('API Users', function () {
230234
const response = await server
231235
.put(`/users/${user}`)
232236
.send({
233-
name
237+
name,
238+
require2faEnabled: false
234239
})
235240
.expect(200);
236241

@@ -241,6 +246,7 @@ describe('API Users', function () {
241246
expect(getResponse.body.success).to.be.true;
242247
expect(getResponse.body.id).to.equal(user);
243248
expect(getResponse.body.name).to.equal(name);
249+
expect(getResponse.body.require2faEnabled).to.equal(false);
244250
});
245251

246252
it('should PUT /users/{user} expect success / and renew a token', async () => {
@@ -343,6 +349,7 @@ describe('API Users', function () {
343349
username: 'myuser2',
344350
scope: 'master',
345351
require2fa: false,
352+
require2faEnabled: false,
346353
// using a temporary password requires a password change
347354
requirePasswordChange: true
348355
});

0 commit comments

Comments
 (0)