Added new maxfidokeys option.

Ylian Saint-Hilaire committed Feb 1, 2022 at 09:46 UTC ac2bf066c33b92791de85a3a7454f57a36c04ed6
3 files changed +28 -10
meshcentral-config-schema.json
+2 -1
@@ -449,7 +449,8 @@
449 "banCommonPasswords": { "type": "boolean", "default": false, "description": "Uses WildLeek to block use of the 10000 most commonly used passwords." },
450 "loginTokens": { "type": "boolean", "default": true, "description": "Allows users to create alternative username/passwords for their account." },
451 "twoFactorTimeout": { "type": "integer", "default": 300, "description": "Maximum about of time the to wait for a 2FA token on the login page in seconds." },
452 - "autofido2fa": { "type": "boolean", "default": false, "description": "If true and user account has FIDO key setup, 2FA login screen will automatically request FIDO 2FA." }
452 + "autofido2fa": { "type": "boolean", "default": false, "description": "If true and user account has FIDO key setup, 2FA login screen will automatically request FIDO 2FA." },
453 + "maxfidokeys": { "type": "integer", "default": null, "description": "Maximum number of FIDO/YubikeyOTP hardware 2FA keys that can be setup in a user account." }
454 }
455 },
456 "twoFactorCookieDurationDays": { "type": "integer", "default": 30, "description": "Number of days that a user is allowed to remember this device for when completing 2FA. Set this to 0 to remove this option." },
meshuser.js
+19 -7
@@ -548,7 +548,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
548 if (parent.parent.webpush != null) { serverinfo.vapidpublickey = parent.parent.webpush.vapidPublicKey; } // Web push public key
549 if (parent.parent.amtProvisioningServer != null) { serverinfo.amtProvServerMeshId = parent.parent.amtProvisioningServer.meshid; } // Device group that allows for bare-metal Intel AMT activation
550 if ((typeof domain.autoremoveinactivedevices == 'number') && (domain.autoremoveinactivedevices > 0)) { serverinfo.autoremoveinactivedevices = domain.autoremoveinactivedevices; } // Default number of days before inactive devices are removed
551 - if ((domain.passwordrequirements) && (domain.passwordrequirements.lock2factor == true)) { serverinfo.lock2factor = true; } // Indicate 2FA change are not allowed
551 + if (domain.passwordrequirements) {
552 + if (domain.passwordrequirements.lock2factor == true) { serverinfo.lock2factor = true; } // Indicate 2FA change are not allowed
553 + if (typeof domain.passwordrequirements.maxfidokeys == 'number') { serverinfo.maxfidokeys = domain.passwordrequirements.maxfidokeys; }
554 + }
555
556 // Build the mobile agent URL, this is used to connect mobile devices
557 var agentServerName = parent.getWebServerName(domain);
@@ -3375,8 +3378,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3378 }
3379 case 'otp-hkey-yubikey-add':
3380 {
3378 - // Do not allow this command if 2FA's are locked
3379 - if ((domain.passwordrequirements) && (domain.passwordrequirements.lock2factor == true)) return;
3381 + // Do not allow this command if 2FA's are locked or max keys reached
3382 + if (domain.passwordrequirements) {
3383 + if (domain.passwordrequirements.lock2factor == true) return;
3384 + if ((typeof domain.passwordrequirements.maxfidokeys == 'number') && (user.otphkeys) && (user.otphkeys.length >= domain.passwordrequirements.maxfidokeys)) return;
3385 + }
3386
3387 // Do not allow this command when logged in using a login token
3388 if (req.session.loginToken != null) break;
@@ -3491,8 +3497,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3497 }
3498 case 'webauthn-startregister':
3499 {
3494 - // Do not allow this command if 2FA's are locked
3495 - if ((domain.passwordrequirements) && (domain.passwordrequirements.lock2factor == true)) return;
3500 + // Do not allow this command if 2FA's are locked or max keys reached
3501 + if (domain.passwordrequirements) {
3502 + if (domain.passwordrequirements.lock2factor == true) return;
3503 + if ((typeof domain.passwordrequirements.maxfidokeys == 'number') && (user.otphkeys) && (user.otphkeys.length >= domain.passwordrequirements.maxfidokeys)) return;
3504 + }
3505
3506 // Do not allow this command when logged in using a login token
3507 if (req.session.loginToken != null) break;
@@ -3511,8 +3520,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3520 }
3521 case 'webauthn-endregister':
3522 {
3514 - // Do not allow this command if 2FA's are locked
3515 - if ((domain.passwordrequirements) && (domain.passwordrequirements.lock2factor == true)) return;
3523 + // Do not allow this command if 2FA's are locked or max keys reached
3524 + if (domain.passwordrequirements) {
3525 + if (domain.passwordrequirements.lock2factor == true) return;
3526 + if ((typeof domain.passwordrequirements.maxfidokeys == 'number') && (user.otphkeys) && (user.otphkeys.length >= domain.passwordrequirements.maxfidokeys)) return;
3527 + }
3528
3529 // Do not allow this command when logged in using a login token
3530 if (req.session.loginToken != null) break;
views/default.handlebars
+7 -2
@@ -2756,8 +2756,13 @@
2756 }
2757 x += '</div>';
2758 x += '<div><input type=button value="' + "Close" + '" onclick=setDialogMode(0) style=float:right></input>';
2759 - if ((features & 0x00020000) != 0) { x += '<input id=d2addkey3 type=button value="' + "Add Key" + '" onclick="account_addhkey(3);"></input>'; }
2760 - if ((features & 0x00004000) != 0) { x += '<input id=d2addkey2 type=button value="' + "Add YubiKey&reg; OTP" + '" onclick="account_addhkey(2);"></input>'; }
2759 + var hkeycount = (typeof userinfo.otphkeys == 'number') ? userinfo.otphkeys : 0;
2760 + if ((typeof serverinfo.maxfidokeys != 'number') || (serverinfo.maxfidokeys > hkeycount)) { // Check if we we reached maximum hardware keys
2761 + if ((features & 0x00020000) != 0) { x += '<input id=d2addkey3 type=button value="' + "Add Key" + '" onclick="account_addhkey(3);"></input>'; }
2762 + if ((features & 0x00004000) != 0) { x += '<input id=d2addkey2 type=button value="' + "Add YubiKey&reg; OTP" + '" onclick="account_addhkey(2);"></input>'; }
2763 + } else {
2764 + x += "Maximum keys reached.";
2765 + }
2766 x += '</div><br />';
2767 setDialogMode(2, "Manage Security Keys", 8, null, x, 'otpauth-hardware-manage');
2768 if (u2fSupported() == false) { QE('d2addkey1', false); }