diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 19570517..40cda4f7 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -451,7 +451,8 @@ "loginTokens": { "type": "boolean", "default": true, "description": "Allows users to create alternative username/passwords for their account." }, "twoFactorTimeout": { "type": "integer", "default": 300, "description": "Maximum about of time the to wait for a 2FA token on the login page in seconds." }, "autofido2fa": { "type": "boolean", "default": false, "description": "If true and user account has FIDO key setup, 2FA login screen will automatically request FIDO 2FA." }, - "maxfidokeys": { "type": "integer", "default": null, "description": "Maximum number of FIDO/YubikeyOTP hardware 2FA keys that can be setup in a user account." } + "maxfidokeys": { "type": "integer", "default": null, "description": "Maximum number of FIDO/YubikeyOTP hardware 2FA keys that can be setup in a user account." }, + "allowaccountreset": { "type": "boolean", "default": true, "description": "If set to false, the account reset option on the login screen will not be available to users." } } }, "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." }, diff --git a/package.json b/package.json index 5bf0153e..2c0f731d 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ "sample-config-advanced.json" ], "dependencies": { + "@yetzt/nedb": "^1.8.0", + "archiver": "^4.0.2", "body-parser": "^1.19.0", "cbor": "~5.2.0", "compression": "^1.7.4", @@ -43,13 +45,23 @@ "express": "^4.17.0", "express-handlebars": "^3.1.0", "express-ws": "^4.0.0", + "image-size": "^1.0.1", "ipcheck": "^0.1.0", + "loadavg-windows": "^1.1.1", "minimist": "^1.2.5", "multiparty": "^4.2.1", - "@yetzt/nedb": "^1.8.0", "node-forge": "^1.0.0", + "node-rdpjs-2": "^0.3.5", + "node-windows": "^0.1.4", + "nodemailer": "^6.7.2", + "otplib": "^10.2.3", + "pg": "^8.7.1", + "pgtools": "^0.3.2", + "ssh2": "^1.6.0", + "web-push": "^3.4.5", "ws": "^5.2.3", - "yauzl": "^2.10.0" + "yauzl": "^2.10.0", + "yubikeyotp": "^0.2.0" }, "engines": { "node": ">=10.0.0" diff --git a/webserver.js b/webserver.js index 482fe56d..aa57dea8 100644 --- a/webserver.js +++ b/webserver.js @@ -1144,7 +1144,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF } // Check if email address needs to be confirmed - var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) + const emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) if (emailcheck && (user.emailVerified !== true)) { parent.debug('web', 'Redirecting using ' + user.name + ' to email check login page'); req.session.messageid = 3; // "Email verification required" message @@ -1165,7 +1165,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF } // Check if email address needs to be confirmed - var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) + const emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) if (emailcheck && (user.emailVerified !== true)) { parent.debug('web', 'Redirecting using ' + user.name + ' to email check login page'); req.session.messageid = 3; // "Email verification required" message @@ -1459,7 +1459,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if (req.session.loginToken != null) { res.sendStatus(404); return; } // Do not allow this command when logged in using a login token // Check everything is ok - if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) { + const allowAccountReset = ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.allowaccountreset !== false)); + if ((allowAccountReset === false) || (domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) { parent.debug('web', 'handleResetPasswordRequest: checks failed'); delete req.session.u2f; delete req.session.loginmode; @@ -1568,7 +1569,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF function handleResetAccountRequest(req, res, direct) { const domain = checkUserIpAddress(req, res); if (domain == null) { return; } - if ((domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.args.lanonly == true) || (obj.parent.certificates.CommonName == null) || (obj.parent.certificates.CommonName.indexOf('.') == -1)) { parent.debug('web', 'handleResetAccountRequest: check failed'); res.sendStatus(404); return; } + const allowAccountReset = ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.allowaccountreset !== false)); + if ((allowAccountReset === false) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.args.lanonly == true) || (obj.parent.certificates.CommonName == null) || (obj.parent.certificates.CommonName.indexOf('.') == -1)) { parent.debug('web', 'handleResetAccountRequest: check failed'); res.sendStatus(404); return; } if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key if (req.session.loginToken != null) { res.sendStatus(404); return; } // Do not allow this command when logged in using a login token @@ -2903,7 +2905,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF delete req.session.messageid; delete req.session.passhint; } - var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) + const allowAccountReset = ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.allowaccountreset !== false)); + const emailcheck = (allowAccountReset && (domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) // Check if we are allowed to create new users using the login screen var newAccountsAllowed = true; @@ -6456,7 +6459,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF var user = obj.users[userid]; if ((err == null) && (user)) { // Check if a 2nd factor is needed - var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) + const emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) // See if we support two-factor trusted cookies var twoFactorCookieDays = 30; @@ -6586,7 +6589,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // Check if inner authentication is requested if (req.headers['x-meshauth'] === '*') { func(ws, req, domain, null); return; } - var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) + const emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) // A web socket session can be authenticated in many ways (Default user, session, user/pass and cookie). Check authentication here. if ((req.query.user != null) && (req.query.pass != null)) {