mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-09-11 08:55:32 +03:00
Added server password timeout and reset on next login.
This commit is contained in:
parent
f79e4a81af
commit
65e6c1925c
@ -316,6 +316,7 @@ function run(argv) {
|
||||
// Display Intel AMT versions
|
||||
var amtMeiModule = require('amt-mei');
|
||||
var amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.getVersion(function (val) {
|
||||
console.log("MEI Version = " + val.BiosVersion.toString());
|
||||
@ -326,6 +327,7 @@ function run(argv) {
|
||||
// Display Intel AMT list of trusted hashes
|
||||
var amtMeiModule = require('amt-mei');
|
||||
var amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.getHashHandles(function (handles) {
|
||||
exitOnCount = handles.length;
|
||||
@ -341,6 +343,7 @@ function run(argv) {
|
||||
mestate = {};
|
||||
var amtMeiModule = require('amt-mei');
|
||||
var amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.getVersion(function (result) { if (result) { for (var version in result.Versions) { if (result.Versions[version].Description == 'AMT') { mestate.ver = result.Versions[version].Version; } } } });
|
||||
amtMei.getProvisioningState(function (result) { if (result) { mestate.ProvisioningState = result; } });
|
||||
@ -381,6 +384,7 @@ function run(argv) {
|
||||
mestate = {};
|
||||
var amtMeiModule = require('amt-mei');
|
||||
var amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.getVersion(function (result) { console.log('getVersion: ' + JSON.stringify(result)); });
|
||||
amtMei.getProvisioningState(function (result) { console.log('getProvisioningState: ' + JSON.stringify(result)); });
|
||||
@ -666,6 +670,7 @@ function startMeshCommander() {
|
||||
function deactivateCCM() {
|
||||
var amtMeiModule = require('amt-mei');
|
||||
var amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.unprovision(1, function (status) { if (status == 0) { console.log('Success'); } else { console.log('Error ' + status); } exit(1); });
|
||||
}
|
||||
@ -708,6 +713,7 @@ function getAmtUuid() {
|
||||
if (settings.hostname == null) {
|
||||
var amtMeiModule = require('amt-mei');
|
||||
var amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.getUuid(function (result) { if ((result == null) || (result.uuid == null)) { console.log('Failed.'); } else { console.log(result.uuid); } exit(1); });
|
||||
} else {
|
||||
@ -829,6 +835,7 @@ function getAmtInfo(func, tag) {
|
||||
getAmtInfoFetchingTimer = null;
|
||||
var amtMeiModule = require('amt-mei');
|
||||
amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
}, 3000);
|
||||
amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
|
||||
@ -890,6 +897,7 @@ function startLms(func) {
|
||||
|
||||
var amtMeiModule = require('amt-mei');
|
||||
amtMei = new amtMeiModule();
|
||||
if (amtMei == null) { console.log("Intel(R) AMT not supported or insufficient access rights."); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
//console.log("PTHI Connected.");
|
||||
|
||||
|
@ -58,7 +58,7 @@ CheckInstallAgent() {
|
||||
# Linux x86, 64 bit
|
||||
machineid=6
|
||||
fi
|
||||
if [ $machinetype == 'x86' ] || [ $machinetype == 'i686' ]
|
||||
if [ $machinetype == 'x86' ] || [ $machinetype == 'i686' ] || [ $machinetype == 'i586' ]
|
||||
then
|
||||
# Linux x86, 32 bit
|
||||
machineid=5
|
||||
|
42
meshuser.js
42
meshuser.js
@ -470,7 +470,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
|
||||
switch (cmd) {
|
||||
case 'help': {
|
||||
r = 'Available commands: help, args, resetserver, showconfig, usersessions, tasklimiter, setmaxtasks, cores.';
|
||||
r = 'Available commands: help, args, resetserver, showconfig, usersessions, tasklimiter, setmaxtasks, cores, migrationagents, swarmstats.';
|
||||
break;
|
||||
}
|
||||
case 'args': {
|
||||
@ -536,6 +536,31 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
r = JSON.stringify(removeAllUnderScore(config), null, 4);
|
||||
break;
|
||||
}
|
||||
case 'migrationagents': {
|
||||
if (obj.parent.parent.swarmserver == null) {
|
||||
r = 'Swarm server not running.';
|
||||
} else {
|
||||
for (var i in obj.parent.parent.swarmserver.migrationAgents) {
|
||||
var arch = obj.parent.parent.swarmserver.migrationAgents[i];
|
||||
for (var j in arch) { var agent = arch[j]; r += 'Arch ' + agent.arch + ', Ver ' + agent.ver + ', Size ' + agent.binary.length + '<br />'; }
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'swarmstats': {
|
||||
if (obj.parent.parent.swarmserver == null) {
|
||||
r = 'Swarm server not running.';
|
||||
} else {
|
||||
for (var i in obj.parent.parent.swarmserver.stats) {
|
||||
if (typeof obj.parent.parent.swarmserver.stats[i] == 'object') {
|
||||
r += i + ' ' + JSON.stringify(obj.parent.parent.swarmserver.stats[i]) + '<br />';
|
||||
} else {
|
||||
r += i + ' ' + obj.parent.parent.swarmserver.stats[i] + '<br />';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { // This is an unknown command, return an error message
|
||||
r = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
|
||||
break;
|
||||
@ -822,11 +847,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
if (hint.length > 250) hint = hint.substring(0, 250);
|
||||
user.salt = salt;
|
||||
user.hash = hash;
|
||||
user.passhint = req.body.apasswordhint;
|
||||
user.passhint = hint;
|
||||
user.passchange = Math.floor(Date.now() / 1000);
|
||||
delete user.passtype;
|
||||
obj.db.SetUser(user);
|
||||
obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||
obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||
|
||||
// Send user notification of password change
|
||||
displayNotificationMessage('Password changed.');
|
||||
@ -854,19 +879,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
// Compute the password hash & save it
|
||||
require('./pass').hash(command.pass, function (err, salt, hash) {
|
||||
if (!err) {
|
||||
var annonceChange = false;
|
||||
chguser.salt = salt;
|
||||
chguser.hash = hash;
|
||||
chguser.passhint = command.hint;
|
||||
chguser.passchange = Math.floor(Date.now() / 1000);
|
||||
if (command.resetNextLogin == true) { chguser.passchange = -1; } else { chguser.passchange = Math.floor(Date.now() / 1000); }
|
||||
delete chguser.passtype; // Remove the password type if one was present.
|
||||
if (command.removeMultiFactor == true) {
|
||||
if (chguser.otpsecret) { delete chguser.otpsecret; annonceChange = true; }
|
||||
if (chguser.otphkeys) { delete chguser.otphkeys; annonceChange = true; }
|
||||
if (chguser.otpkeys) { delete chguser.otpkeys; annonceChange = true; }
|
||||
if (chguser.otpsecret) { delete chguser.otpsecret; }
|
||||
if (chguser.otphkeys) { delete chguser.otphkeys; }
|
||||
if (chguser.otpkeys) { delete chguser.otpkeys; }
|
||||
}
|
||||
obj.db.SetUser(chguser);
|
||||
if (annonceChange == true) { obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Removed 2nd factor auth.', domain: domain.id }); }
|
||||
obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Changed account credentials.', domain: domain.id });
|
||||
} else {
|
||||
// Report that the password change failed
|
||||
// TODO
|
||||
|
@ -48,7 +48,7 @@
|
||||
"_NewAccountEmailDomains": [ "sample.com" ],
|
||||
"Footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>",
|
||||
"_CertUrl": "https://192.168.2.106:443/",
|
||||
"_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1 },
|
||||
"_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90 },
|
||||
"_AgentNoProxy": true,
|
||||
"_GeoLocation": true,
|
||||
"_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
|
||||
|
@ -23,10 +23,10 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
|
||||
obj.legacyAgentConnections = {};
|
||||
obj.migrationAgents = {};
|
||||
obj.agentActionCount = {};
|
||||
const common = require('./common.js');
|
||||
//const net = require('net');
|
||||
obj.stats = { blockedConnect: 0, connectCount: 0, clientCertConnectCount: 0, noCertConnectCount: 0, bytesIn: 0, bytesOut: 0, httpGetRequest: 0, pushedAgents: {}, close: 0, onclose: 0 }
|
||||
const tls = require('tls');
|
||||
const forge = require('node-forge');
|
||||
const common = require('./common.js');
|
||||
|
||||
const LegacyMeshProtocol = {
|
||||
NODEPUSH: 1, // Used to send a node block to another peer.
|
||||
@ -149,26 +149,41 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
|
||||
// Called when a legacy agent connects to this server
|
||||
function onConnection(socket) {
|
||||
// Check for blocked IP address
|
||||
if (checkSwarmIpAddress(socket, obj.args.swarmallowedip) == false) { Debug(1, "SWARM:New blocked agent connection"); return; }
|
||||
if (checkSwarmIpAddress(socket, obj.args.swarmallowedip) == false) { obj.stats.blockedConnect++; Debug(1, "SWARM:New blocked agent connection"); return; }
|
||||
obj.stats.connectCount++;
|
||||
|
||||
socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "", socket: socket };
|
||||
socket.setEncoding('binary');
|
||||
socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000);
|
||||
Debug(1, 'SWARM:New legacy agent connection');
|
||||
|
||||
if ((socket.tag.clientCert == null) || (socket.tag.clientCert.subject == null)) { obj.stats.noCertConnectCount++; } else { obj.stats.clientCertConnectCount++; }
|
||||
|
||||
socket.addListener("data", function (data) {
|
||||
if (args.swarmdebug) { var buf = Buffer.from(data, "binary"); console.log('SWARM <-- (' + buf.length + '):' + buf.toString('hex')); } // Print out received bytes
|
||||
obj.stats.bytesIn += data.length;
|
||||
socket.tag.accumulator += data;
|
||||
|
||||
// Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port.
|
||||
if (socket.tag.first == true) {
|
||||
if (socket.tag.accumulator.length < 3) return;
|
||||
if (socket.tag.accumulator.substring(0, 3) == 'GET') { /*console.log("Swarm Connection, HTTP GET detected: " + socket.remoteAddress);*/ socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>MeshCentral2 legacy swarm server.<br />MeshCentral1 mesh agents should connect here for updates.</body></html>'); socket.end(); return; }
|
||||
if (socket.tag.accumulator.substring(0, 3) == 'GET') {
|
||||
obj.stats.httpGetRequest++;
|
||||
/*console.log("Swarm Connection, HTTP GET detected: " + socket.remoteAddress);*/
|
||||
socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>MeshCentral2 legacy swarm server.<br />MeshCentral1 mesh agents should connect here for updates.</body></html>');
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
socket.tag.first = false;
|
||||
}
|
||||
|
||||
// A client certificate is required
|
||||
if (!socket.tag.clientCert.subject) { /*console.log("Swarm Connection, no client cert: " + socket.remoteAddress);*/ socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.'); socket.end(); return; }
|
||||
if ((socket.tag.clientCert == null) || (socket.tag.clientCert.subject == null)) {
|
||||
/*console.log("Swarm Connection, no client cert: " + socket.remoteAddress);*/
|
||||
socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.');
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Parse all of the agent binary command data we can
|
||||
@ -215,6 +230,10 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
|
||||
socket.tag.updatePtr = 0;
|
||||
//console.log('Performing legacy agent update from ' + nodeblock.agentversion + '.' + nodeblock.agenttype + ' to ' + socket.tag.update.ver + '.' + socket.tag.update.arch + ' on ' + nodeblock.agentname + '.');
|
||||
|
||||
// Update stats
|
||||
if (obj.stats.pushedAgents[nodeblock.agenttype] == null) { obj.stats.pushedAgents[nodeblock.agenttype] = {}; }
|
||||
if (obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] == null) { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] = 0; } else { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion]++; }
|
||||
|
||||
// Start the agent download using the task limiter so not to flood the server. Low priority task
|
||||
obj.parent.taskLimiter.launch(function (socket, taskid, taskLimiterQueue) {
|
||||
if (socket.xclosed == 1) {
|
||||
@ -286,6 +305,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
|
||||
}
|
||||
|
||||
socket.addListener("close", function () {
|
||||
obj.stats.onclose++;
|
||||
Debug(1, 'Swarm:Connection closed');
|
||||
if (socket.pingTimer != null) { clearInterval(socket.pingTimer); delete socket.pingTimer; }
|
||||
if (socket.tag && (typeof socket.tag.taskid == 'number')) {
|
||||
@ -358,6 +378,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Disconnect legacy agent connection
|
||||
obj.close = function (socket) {
|
||||
obj.stats.close++;
|
||||
try { socket.close(); } catch (e) { }
|
||||
socket.xclosed = 1;
|
||||
};
|
||||
@ -368,6 +389,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
|
||||
};
|
||||
|
||||
function Write(socket, data) {
|
||||
obj.stats.bytesOut += data.length;
|
||||
if (args.swarmdebug) {
|
||||
// Print out sent bytes
|
||||
var buf = Buffer.from(data, "binary");
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -243,7 +243,7 @@
|
||||
<div style="margin-left:9px;margin-bottom:8px">
|
||||
<div style="margin-top:5px"><span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a></span></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><span id="p2nextPasswordUpdateTime"></span></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a></div>
|
||||
</div>
|
||||
<br style=clear:both />
|
||||
@ -654,8 +654,21 @@
|
||||
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
||||
QV('manageAuthApp', features & 4096);
|
||||
QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)));
|
||||
|
||||
if (typeof userinfo.passchange == 'number') {
|
||||
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
|
||||
var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
|
||||
if (seconds < 0) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if (seconds < 3600) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 60) + ' minute' + addLetterS(Math.floor(seconds / 60)) + '.'); }
|
||||
else if (seconds < 86400) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 3600) + ' hour' + addLetterS(Math.floor(seconds / 3600)) + '.'); }
|
||||
else { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 86400) + ' day' + addLetterS(Math.floor(seconds / 86400)) + '.'); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addLetterS(x) { return (x > 1) ? 's' : ''; }
|
||||
|
||||
function onMessage(server, message) {
|
||||
switch (message.action) {
|
||||
case 'serverinfo': {
|
||||
|
@ -260,7 +260,7 @@
|
||||
<p style="margin-left:40px">
|
||||
<span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a><br /></span>
|
||||
<a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a><br />
|
||||
<a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><br />
|
||||
<a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
|
||||
<a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a><br />
|
||||
</p>
|
||||
<br style=clear:both />
|
||||
@ -1190,8 +1190,21 @@
|
||||
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
||||
QV('authKeySetupCheck', userinfo.otphkeys > 0);
|
||||
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
|
||||
|
||||
if (typeof userinfo.passchange == 'number') {
|
||||
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
|
||||
var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
|
||||
if (seconds < 0) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if (seconds < 3600) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 60) + ' minute' + addLetterS(Math.floor(seconds / 60)) + '.'); }
|
||||
else if (seconds < 86400) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 3600) + ' hour' + addLetterS(Math.floor(seconds / 3600)) + '.'); }
|
||||
else { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 86400) + ' day' + addLetterS(Math.floor(seconds / 86400)) + '.'); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addLetterS(x) { return (x > 1) ? 's' : ''; }
|
||||
|
||||
function onMessage(server, message) {
|
||||
switch (message.action) {
|
||||
case 'serverstats': {
|
||||
@ -1767,7 +1780,9 @@
|
||||
}
|
||||
case 'login': {
|
||||
// Update the last login time
|
||||
if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) { users['user/' + domain + '/' + message.event.username.toLowerCase()].login = Math.floor(message.event.time / 1000); }
|
||||
if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) {
|
||||
users['user/' + domain + '/' + message.event.username.toLowerCase()].login = Math.floor(new Date(message.event.time).getTime() / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'scanamtdevice': {
|
||||
@ -6584,6 +6599,8 @@
|
||||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
||||
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
||||
if (user.passchange == -1) { x += addDeviceAttribute('Password', 'Will be changed on next login.'); }
|
||||
else if (user.passchange) { x += addDeviceAttribute('Password', 'Last changed: ' + new Date(user.passchange * 1000).toLocaleString()); }
|
||||
|
||||
var linkCount = 0, linkCountStr = '<i>None<i>';
|
||||
if (user.links) {
|
||||
@ -6674,6 +6691,7 @@
|
||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||
x += addHtmlValue('Password hint', '<input id=p4hint type=text style=width:230px maxlength=256></input>');
|
||||
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||||
x += '<div><input id=p4resetNextLogin type=checkbox />Force password reset on next login.</div>';
|
||||
if (multiFactor == 1) { x += '<div><input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.</div>'; }
|
||||
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
||||
showCreateNewAccountDialogValidate(1);
|
||||
@ -6683,7 +6701,7 @@
|
||||
function p30showUserChangePassDialogEx(b, tag) {
|
||||
var removeMultiFactor = false;
|
||||
if ((tag == 1) && (Q('p4twoFactorRemove').checked == true)) { removeMultiFactor = true; }
|
||||
if (Q('p4pass1').value == Q('p4pass2').value) { meshserver.send({ action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value, hint: Q('p4hint').value, removeMultiFactor: removeMultiFactor }); }
|
||||
if (Q('p4pass1').value == Q('p4pass2').value) { meshserver.send({ action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value, hint: Q('p4hint').value, removeMultiFactor: removeMultiFactor, resetNextLogin: Q('p4resetNextLogin').checked }); }
|
||||
}
|
||||
|
||||
function p30showDeleteUserDialog() {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -188,6 +188,35 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id=resetpasswordpanel style="position:relative;background-color: #979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
|
||||
<form action=resetpassword method=post>
|
||||
<div id=message6>
|
||||
{{{message}}}
|
||||
</div>
|
||||
<div id="rpasswordPolicyCallout" style="left:-10px;width:100px;display:none;position:absolute;background-color:#FFC;border-radius:5px;padding:5px;box-shadow:0px 0px 15px #666;font-size:10px"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="rnuPass1" width=100 align=right>Password:</td>
|
||||
<td><input id=rapassword1 type=password name=rpassword1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(3,event) onkeyup=validatePassReset(3,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuPass2" align=right>Password:</td>
|
||||
<td><input id=rapassword2 type=password name=rpassword2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(4,event) onkeyup=validatePassReset(4,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuHint" align=right>Password Hint:</td>
|
||||
<td><input id=rapasswordhint type=text name=rpasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(5,event) onkeyup=validatePassReset(5,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div style=float:right><input id=resetPassButton type=submit value="Reset Password" disabled="disabled" /></div>
|
||||
<div id=rpassWarning style="padding-top:6px"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@ -288,6 +317,7 @@
|
||||
QV('message3', false);
|
||||
QV('message4', false);
|
||||
QV('message5', false);
|
||||
QV('message6', false);
|
||||
go(x);
|
||||
}
|
||||
|
||||
@ -299,10 +329,13 @@
|
||||
QV('resetpanel', x == 3);
|
||||
QV('tokenpanel', x == 4);
|
||||
QV('resettokenpanel', x == 5);
|
||||
QV('resetpasswordpanel', x == 6);
|
||||
if (x == 1) { Q('username').focus(); }
|
||||
if (x == 2) { Q('ausername').focus(); }
|
||||
if (x == 3) { Q('remail').focus(); }
|
||||
if (x == 4) { Q('tokenInput').focus(); }
|
||||
if (x == 5) { Q('resetTokenInput').focus(); }
|
||||
if (x == 6) { Q('rapassword1').focus(); }
|
||||
}
|
||||
|
||||
function validateLogin(box, e) {
|
||||
@ -355,6 +388,52 @@
|
||||
if (e != null) { haltEvent(e); }
|
||||
}
|
||||
|
||||
function validatePassReset(box, e) {
|
||||
setDialogMode(0);
|
||||
var pass1ok = (Q('rapassword1').value.length > 0);
|
||||
var pass2ok = (Q('rapassword2').value.length > 0) && (Q('rapassword2').value == Q('rapassword1').value);
|
||||
var ok = (pass1ok && pass2ok);
|
||||
|
||||
// Color the fields
|
||||
QS('rnuPass1').color = pass1ok ? 'black' : '#7b241c';
|
||||
QS('rnuPass2').color = pass2ok ? 'black' : '#7b241c';
|
||||
|
||||
if (Q('rapassword1').value == '') {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
} else {
|
||||
if (passRequirements == null || passRequirements == '') {
|
||||
// No password requirements, display password strength
|
||||
var passStrength = checkPasswordStrength(Q('rapassword1').value);
|
||||
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>Strong Password</b><span>'); }
|
||||
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>Good Password</b><span>'); }
|
||||
else { QH('rpassWarning', '<span style=color:red><b>Weak Password</b><span>'); }
|
||||
} else {
|
||||
// Password requirements provided, use that
|
||||
var passReq = checkPasswordRequirements(Q('rapassword1').value, passRequirements);
|
||||
if (passReq == false) {
|
||||
ok = false;
|
||||
QS('rnuPass1').color = '#7b241c';
|
||||
QS('rnuPass2').color = '#7b241c';
|
||||
QH('rpassWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
|
||||
QV('rpasswordPolicyCallout', true);
|
||||
QH('rpasswordPolicyCallout', passwordPolicyText(Q('rapassword1').value));
|
||||
} else {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((e != null) && (e.keyCode == 13)) {
|
||||
if (box == 2) { Q('rapassword1').focus(); }
|
||||
if (box == 3) { Q('rapassword2').focus(); }
|
||||
if (box == 4) { Q('rapasswordhint').focus(); }
|
||||
if (box == 6) { Q('resetPassButton').click(); }
|
||||
}
|
||||
if (e != null) { haltEvent(e); }
|
||||
QE('resetPassButton', ok);
|
||||
}
|
||||
|
||||
function validateReset(e) {
|
||||
setDialogMode(0);
|
||||
var x = validateEmail(Q('remail').value);
|
||||
|
@ -167,19 +167,19 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="nuPass1" align=right>Password:</td>
|
||||
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3) onkeyup=validateCreate(3,event) /></td>
|
||||
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3,event) onkeyup=validateCreate(3,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="nuPass2" align=right>Password:</td>
|
||||
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4) onkeyup=validateCreate(4,event) /></td>
|
||||
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4,event) onkeyup=validateCreate(4,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="nuHint" align=right>Password Hint:</td>
|
||||
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5) onkeyup=validateCreate(5,event) /></td>
|
||||
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5,event) onkeyup=validateCreate(5,event) /></td>
|
||||
</tr>
|
||||
<tr id=newAccountPass title="Enter the account creation token">
|
||||
<td id="nuToken" align=right>Creation Token:</td>
|
||||
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6) onkeyup=validateCreate(6,event) /></td>
|
||||
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6,event) onkeyup=validateCreate(6,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
@ -258,6 +258,35 @@
|
||||
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
<div id=resetpasswordpanel style="position:relative;background-color: #979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
|
||||
<form action=resetpassword method=post>
|
||||
<div id=message6>
|
||||
{{{message}}}
|
||||
</div>
|
||||
<div id="rpasswordPolicyCallout" style="left:-10px;width:100px;display:none;position:absolute;background-color:#FFC;border-radius:5px;padding:5px;box-shadow:0px 0px 15px #666;font-size:10px"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="rnuPass1" width=100 align=right>Password:</td>
|
||||
<td><input id=rapassword1 type=password name=rpassword1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(3,event) onkeyup=validatePassReset(3,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuPass2" align=right>Password:</td>
|
||||
<td><input id=rapassword2 type=password name=rpassword2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(4,event) onkeyup=validatePassReset(4,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuHint" align=right>Password Hint:</td>
|
||||
<td><input id=rapasswordhint type=text name=rpasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(5,event) onkeyup=validatePassReset(5,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div style=float:right><input id=resetPassButton type=submit value="Reset Password" disabled="disabled" /></div>
|
||||
<div id=rpassWarning style="padding-top:6px"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -373,6 +402,7 @@
|
||||
QV('message3', false);
|
||||
QV('message4', false);
|
||||
QV('message5', false);
|
||||
QV('message6', false);
|
||||
go(x);
|
||||
}
|
||||
|
||||
@ -384,10 +414,13 @@
|
||||
QV('resetpanel', x == 3);
|
||||
QV('tokenpanel', x == 4);
|
||||
QV('resettokenpanel', x == 5);
|
||||
QV('resetpasswordpanel', x == 6);
|
||||
if (x == 1) { Q('username').focus(); }
|
||||
if (x == 2) { Q('ausername').focus(); }
|
||||
if (x == 3) { Q('remail').focus(); }
|
||||
if (x == 4) { Q('tokenInput').focus(); }
|
||||
if (x == 5) { Q('resetTokenInput').focus(); }
|
||||
if (x == 6) { Q('rapassword1').focus(); }
|
||||
}
|
||||
|
||||
function validateLogin(box, e) {
|
||||
@ -452,6 +485,52 @@
|
||||
QE('createButton', ok);
|
||||
}
|
||||
|
||||
function validatePassReset(box, e) {
|
||||
setDialogMode(0);
|
||||
var pass1ok = (Q('rapassword1').value.length > 0);
|
||||
var pass2ok = (Q('rapassword2').value.length > 0) && (Q('rapassword2').value == Q('rapassword1').value);
|
||||
var ok = (pass1ok && pass2ok);
|
||||
|
||||
// Color the fields
|
||||
QS('rnuPass1').color = pass1ok ? 'black' : '#7b241c';
|
||||
QS('rnuPass2').color = pass2ok ? 'black' : '#7b241c';
|
||||
|
||||
if (Q('rapassword1').value == '') {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
} else {
|
||||
if (passRequirements == null || passRequirements == '') {
|
||||
// No password requirements, display password strength
|
||||
var passStrength = checkPasswordStrength(Q('rapassword1').value);
|
||||
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>Strong Password</b><span>'); }
|
||||
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>Good Password</b><span>'); }
|
||||
else { QH('rpassWarning', '<span style=color:red><b>Weak Password</b><span>'); }
|
||||
} else {
|
||||
// Password requirements provided, use that
|
||||
var passReq = checkPasswordRequirements(Q('rapassword1').value, passRequirements);
|
||||
if (passReq == false) {
|
||||
ok = false;
|
||||
QS('rnuPass1').color = '#7b241c';
|
||||
QS('rnuPass2').color = '#7b241c';
|
||||
QH('rpassWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
|
||||
QV('rpasswordPolicyCallout', true);
|
||||
QH('rpasswordPolicyCallout', passwordPolicyText(Q('rapassword1').value));
|
||||
} else {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((e != null) && (e.keyCode == 13)) {
|
||||
if (box == 2) { Q('rapassword1').focus(); }
|
||||
if (box == 3) { Q('rapassword2').focus(); }
|
||||
if (box == 4) { Q('rapasswordhint').focus(); }
|
||||
if (box == 6) { Q('resetPassButton').click(); }
|
||||
}
|
||||
if (e != null) { haltEvent(e); }
|
||||
QE('resetPassButton', ok);
|
||||
}
|
||||
|
||||
function passwordPolicyText(pass) {
|
||||
var policy = '<div style=text-align:left>';
|
||||
var counts = strCount(pass);
|
||||
|
126
webserver.js
126
webserver.js
@ -419,7 +419,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
var user = obj.users[userid];
|
||||
|
||||
// Check if this user has 2-step login active
|
||||
if (checkUserOneTimePasswordRequired(domain, user)) {
|
||||
if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user)) {
|
||||
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
|
||||
if (result == false) {
|
||||
var randomWaitTime = 0;
|
||||
@ -439,14 +439,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
}, randomWaitTime);
|
||||
} else {
|
||||
// Login succesful
|
||||
completeLoginRequest(req, res, domain, user, userid);
|
||||
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Login succesful
|
||||
completeLoginRequest(req, res, domain, user, userid);
|
||||
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword);
|
||||
} else {
|
||||
// Login failed, wait a random delay
|
||||
setTimeout(function () {
|
||||
@ -466,10 +466,22 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
});
|
||||
}
|
||||
|
||||
function completeLoginRequest(req, res, domain, user, userid) {
|
||||
function completeLoginRequest(req, res, domain, user, userid, xusername, xpassword) {
|
||||
// Check if we need to change the password
|
||||
if ((typeof user.passchange == 'number') && ((user.passchange == -1) || ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.reset == 'number') && (user.passchange + (domain.passwordrequirements.reset * 86400) < Math.floor(Date.now() / 1000))))) {
|
||||
// Request a password change
|
||||
req.session.loginmode = '6';
|
||||
req.session.error = '<b style=color:#8C001A>Password change requested.</b>';
|
||||
req.session.resettokenusername = xusername;
|
||||
req.session.resettokenpassword = xpassword;
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save login time
|
||||
user.login = Math.floor(Date.now() / 1000);
|
||||
obj.db.SetUser(user);
|
||||
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'login', msg: 'Account login', domain: domain.id });
|
||||
|
||||
// Regenerate session when signing in to prevent fixation
|
||||
//req.session.regenerate(function () {
|
||||
@ -506,8 +518,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
res.redirect(domain.url);
|
||||
}
|
||||
//});
|
||||
|
||||
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id });
|
||||
}
|
||||
|
||||
function handleCreateAccountRequest(req, res) {
|
||||
@ -521,7 +531,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
var i = -1;
|
||||
if (typeof req.body.email == 'string') { i = req.body.email.indexOf('@'); }
|
||||
if (i == -1) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
@ -529,7 +539,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase();
|
||||
for (var i in domain.newaccountemaildomains) { if (emaildomain == domain.newaccountemaildomains[i].toLowerCase()) { emailok = true; } }
|
||||
if (emailok == false) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
@ -539,33 +549,33 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// Check if we exceed the maximum number of user accounts
|
||||
obj.db.isMaxType(domain.limits.maxuseraccounts, 'user', domain.id, function (maxExceed) {
|
||||
if (maxExceed) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Account limit reached.</b>';
|
||||
console.log('max', req.session);
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~' || !obj.common.checkPasswordRequirements(req.body.password1, domain.passwordrequirements)) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
// Check if this email was already verified
|
||||
obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
|
||||
if (docs.length > 0) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
// Check if there is domain.newAccountToken, check if supplied token is valid
|
||||
if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>';
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
// Check if user exists
|
||||
if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) {
|
||||
req.session.loginmode = 2;
|
||||
req.session.loginmode = '2';
|
||||
req.session.error = '<b style=color:#8C001A>Username already exists.</b>';
|
||||
} else {
|
||||
var hint = req.body.apasswordhint;
|
||||
@ -599,6 +609,82 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
});
|
||||
}
|
||||
|
||||
// Called to process an account password reset
|
||||
function handleResetPasswordRequest(req, res) {
|
||||
const domain = checkUserIpAddress(req, res);
|
||||
|
||||
// Check everything is ok
|
||||
if ((domain == null) || (domain.auth == 'sspi') || (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')) {
|
||||
delete req.session.loginmode;
|
||||
delete req.session.tokenusername;
|
||||
delete req.session.tokenpassword;
|
||||
delete req.session.resettokenusername;
|
||||
delete req.session.resettokenpassword;
|
||||
delete req.session.tokenemail;
|
||||
delete req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.passhint;
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Authenticate the user
|
||||
obj.authenticate(req.session.resettokenusername, req.session.resettokenpassword, domain, function (err, userid, passhint) {
|
||||
if (userid) {
|
||||
// Login
|
||||
var user = obj.users[userid];
|
||||
|
||||
// If we have password requirements, check this here.
|
||||
if (!obj.common.checkPasswordRequirements(req.body.rpassword1, domain.passwordrequirements)) {
|
||||
req.session.loginmode = '6';
|
||||
req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the password is the same as the previous one
|
||||
require('./pass').hash(req.body.rpassword1, user.salt, function (err, hash) {
|
||||
if (user.hash == hash) {
|
||||
// This is the same password, request a password change again
|
||||
req.session.loginmode = '6';
|
||||
req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
// Update the password, use a different salt.
|
||||
require('./pass').hash(req.body.rpassword1, function (err, salt, hash) {
|
||||
if (err) throw err;
|
||||
user.salt = salt;
|
||||
user.hash = hash;
|
||||
user.passhint = req.body.rpasswordhint;
|
||||
user.passchange = Math.floor(Date.now() / 1000);
|
||||
delete user.passtype;
|
||||
obj.db.SetUser(user);
|
||||
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', msg: 'User password reset', domain: domain.id });
|
||||
|
||||
// Login succesful
|
||||
req.session.userid = userid;
|
||||
req.session.domainid = domain.id;
|
||||
completeLoginRequest(req, res, domain, obj.users[userid], userid, req.session.tokenusername, req.session.tokenpassword);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Failed, error out.
|
||||
delete req.session.loginmode;
|
||||
delete req.session.tokenusername;
|
||||
delete req.session.tokenpassword;
|
||||
delete req.session.resettokenusername;
|
||||
delete req.session.resettokenpassword;
|
||||
delete req.session.tokenemail;
|
||||
delete req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.passhint;
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Called to process an account reset request
|
||||
function handleResetAccountRequest(req, res) {
|
||||
const domain = checkUserIpAddress(req, res);
|
||||
@ -610,13 +696,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Check the email stirng format
|
||||
if (!email || checkEmail(email) == false) {
|
||||
req.session.loginmode = 3;
|
||||
req.session.loginmode = '3';
|
||||
req.session.error = '<b style=color:#8C001A>Invalid email.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) {
|
||||
if ((err != null) || (docs.length == 0)) {
|
||||
req.session.loginmode = 3;
|
||||
req.session.loginmode = '3';
|
||||
req.session.error = '<b style=color:#8C001A>Account not found.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
@ -635,11 +721,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
delete req.session.tokenemail;
|
||||
if (obj.parent.mailserver != null) {
|
||||
obj.parent.mailserver.sendAccountResetMail(domain, user.name, user.email);
|
||||
req.session.loginmode = 1;
|
||||
req.session.loginmode = '1';
|
||||
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
req.session.loginmode = 3;
|
||||
req.session.loginmode = '3';
|
||||
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
|
||||
res.redirect(domain.url);
|
||||
}
|
||||
@ -649,11 +735,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// No second factor, send email to perform recovery.
|
||||
if (obj.parent.mailserver != null) {
|
||||
obj.parent.mailserver.sendAccountResetMail(domain, user.name, user.email);
|
||||
req.session.loginmode = 1;
|
||||
req.session.loginmode = '1';
|
||||
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
|
||||
res.redirect(domain.url);
|
||||
} else {
|
||||
req.session.loginmode = 3;
|
||||
req.session.loginmode = '3';
|
||||
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
|
||||
res.redirect(domain.url);
|
||||
}
|
||||
@ -1074,7 +1160,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if (err != null) message = '<p class="msg error">' + err + '</p>';
|
||||
if (msg != null) message = '<p class="msg success">' + msg + '</p>';
|
||||
if (passhint != null) passhint = EscapeHtml(passhint);
|
||||
|
||||
var emailcheck = ((obj.parent.mailserver != null) && (domain.auth != 'sspi'));
|
||||
|
||||
if (obj.args.minify && !req.query.nominify) {
|
||||
@ -2271,6 +2356,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
obj.app.post(url + 'changepassword', handlePasswordChangeRequest);
|
||||
obj.app.post(url + 'deleteaccount', handleDeleteAccountRequest);
|
||||
obj.app.post(url + 'createaccount', handleCreateAccountRequest);
|
||||
obj.app.post(url + 'resetpassword', handleResetPasswordRequest);
|
||||
obj.app.post(url + 'resetaccount', handleResetAccountRequest);
|
||||
obj.app.get(url + 'checkmail', handleCheckMailRequest);
|
||||
obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
|
||||
|
Loading…
Reference in New Issue
Block a user