727 lines
46 KiB
Handlebars
727 lines
46 KiB
Handlebars
<!DOCTYPE html><html dir="ltr" xmlns="http://www.w3.org/1999/xhtml"><head>
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||
<meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="format-detection" content="telephone=no">
|
||
<link rel="shortcut icon" type="image/x-icon" href="{{{domainurl}}}favicon.ico">
|
||
<link keeplink="1" type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS">
|
||
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
||
<script keeplink="1" type="text/javascript" src="scripts/u2f-api.js"></script>
|
||
<title>{{{title}}} - Login</title>
|
||
</head>
|
||
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide login">
|
||
<div id="container">
|
||
<div id="masthead">
|
||
<div class="title">{{{title}}}</div>
|
||
<div class="title2">{{{title2}}}</div>
|
||
</div>
|
||
<div id="topbar" class="noselect style3" style="height:24px">
|
||
<div id="uiMenuButton" title="User interface selection" onclick="showUserInterfaceSelectMenu()">
|
||
♦
|
||
<div id="uiMenu" style="display:none">
|
||
<div id="uiViewButton1" class="uiSelector" onclick="userInterfaceSelectMenu(1)" title="Left bar interface"><div class="uiSelector1"></div></div>
|
||
<div id="uiViewButton2" class="uiSelector" onclick="userInterfaceSelectMenu(2)" title="Top bar interface"><div class="uiSelector2"></div></div>
|
||
<div id="uiViewButton3" class="uiSelector" onclick="userInterfaceSelectMenu(3)" title="Interface à largeur fixe"><div class="uiSelector3"></div></div>
|
||
<div id="uiViewButton4" class="uiSelector" onclick="toggleNightMode()" title="Basculer mode nuit"><div class="uiSelector4"></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="column_l">
|
||
<h1>Bienvenue</h1>
|
||
<div id="welcomeText" style="display:none">Connectez-vous à vos ordinateurs à la maison ou au bureau depuis n'importe où dans le monde avec MeshCentral, le site web open source de surveillance et de gestion d’ordinateur à distance en temps réel. Vous devrez télécharger et installer un agent de gestion sur vos ordinateurs. Une fois installés, les ordinateurs apparaîtront dans la section "Mes appareils" de ce site et vous pourrez les surveiller et en prendre le contrôle.</div>
|
||
<table id="centralTable" style="">
|
||
<tbody><tr>
|
||
<td id="welcomeimage">
|
||
<picture>
|
||
<img alt="" src="welcome.jpg" style="border-radius:20px">
|
||
</picture>
|
||
</td>
|
||
<td id="logincell">
|
||
<div id="loginpanel" style="display:none">
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="login">
|
||
<div id="message1"></div>
|
||
<div>
|
||
<b>Log In</b>
|
||
</div>
|
||
<table>
|
||
<tbody><tr>
|
||
<td id="loginusername" align="right" width="100">Nom d'utilisateur:</td>
|
||
<td><input id="username" type="text" maxlength="64" name="username" onchange="validateLogin(1)" onkeyup="validateLogin(1,event)"></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="right">Mot de passe:</td>
|
||
<td><input id="password" type="password" maxlength="256" name="password" autocomplete="off" onchange="validateLogin(2)" onkeyup="validateLogin(2,event)"></td>
|
||
</tr>
|
||
<tr>
|
||
<td><div id="showPassHintLink" style="display:none"><a onclick="return showPassHint(event);" href="#" style="cursor:pointer">Dévoiler indice</a></div></td>
|
||
<td align="right"><input id="loginButton" type="submit" value="Log In" disabled="disabled"></td>
|
||
</tr>
|
||
</tbody></table>
|
||
<div id="hrAccountDiv" style="display:none"><hr></div>
|
||
<div id="resetAccountDiv" style="display:none;padding:2px">
|
||
<span id="resetAccountSpan">Forgot username/password?</span> <a onclick="return xgo(3,event);" href="#" style="cursor:pointer">Réinitialiser le compte</a>.
|
||
</div>
|
||
<div id="newAccountDiv" style="display:none;padding:2px">
|
||
Don't have an account? <a onclick="return xgo(2,event);" href="#" style="cursor:pointer">Create one</a>.
|
||
</div>
|
||
<input id="loginformargs" name="urlargs" type="hidden" value="">
|
||
</form>
|
||
</div>
|
||
<div id="createpanel" style="display:none;position:relative">
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="createaccount">
|
||
<div id="message2"></div>
|
||
<div>
|
||
<b>Account Creation</b>
|
||
</div>
|
||
<div id="passwordPolicyCallout" style="display:none"></div>
|
||
<table>
|
||
<tbody><tr id="nuUserRow">
|
||
<td id="nuUser" align="right" width="100">Nom d'utilisateur:</td>
|
||
<td><input id="ausername" type="text" name="username" onchange="validateCreate(1)" maxlength="64" onkeydown="haltReturn(event)" onkeyup="validateCreate(1,event)"></td>
|
||
</tr>
|
||
<tr>
|
||
<td id="nuEmail" align="right" width="100">Email:</td>
|
||
<td><input id="aemail" type="text" name="email" onchange="validateCreate(2)" maxlength="256" onkeydown="haltReturn(event)" onkeyup="validateCreate(2,event)"></td>
|
||
</tr>
|
||
<tr>
|
||
<td id="nuPass1" align="right">Mot de passe:</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">Mot de passe:</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 id="createPanelHint" style="display:none">
|
||
<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,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,event)" onkeyup="validateCreate(6,event)"></td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="2">
|
||
<div style="float:right"><input id="createButton" type="submit" value="Create Account" disabled="disabled"></div>
|
||
<div id="passWarning" style="padding-top:6px"></div>
|
||
</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<hr><a onclick="return xgo(1,event);" href="#" style="cursor:pointer">Back to login</a>
|
||
<input id="createformargs" name="urlargs" type="hidden" value="">
|
||
</form>
|
||
</div>
|
||
<div id="resetpanel" style="display:none">
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="resetaccount">
|
||
<div id="message3"></div>
|
||
<div>
|
||
<b>Account Reset</b>
|
||
</div>
|
||
<table>
|
||
<tbody><tr>
|
||
<td align="right" width="100">Email:</td>
|
||
<td><input id="remail" type="text" name="email" maxlength="256" onchange="validateReset()" onkeyup="validateReset(event)"></td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="2">
|
||
<div style="float:right"><input id="eresetButton" type="submit" value="Réinitialiser le Compte" disabled="disabled"></div>
|
||
<div id="passWarning" style="padding-top:6px"></div>
|
||
</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<hr><a onclick="return xgo(1,event);" href="#" style="cursor:pointer">Back to login</a>
|
||
<input id="resetformargs" name="urlargs" type="hidden" value="">
|
||
</form>
|
||
</div>
|
||
<div id="tokenpanel" style="display:none">
|
||
<form method="post" autocomplete="off">
|
||
<input type="hidden" name="action" value="tokenlogin">
|
||
<input type="hidden" name="hwstate" value="{{{hwstate}}}">
|
||
<div id="message4"></div>
|
||
<table>
|
||
<tbody><tr>
|
||
<td align="right" width="100">Login token:</td>
|
||
<td>
|
||
<input id="tokenInput" type="text" name="token" maxlength="50" onchange="checkToken(event)" onpaste="resetCheckToken(event)" onkeyup="checkToken(event)" onkeydown="checkToken(event)"><br>
|
||
<input id="hwtokenInput" type="text" name="hwtoken" style="display:none">
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="2" style="align-content:center">
|
||
<label><input id="tokenInputRemember" name="remembertoken" type="checkbox">Rappelez cet appareil pour 30 jours.</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="2">
|
||
<div style="float:right"><input id="tokenOkButton" type="submit" value="Login" disabled="disabled"></div>
|
||
<div style="float:right"><input style="display:none;float:right" id="securityKeyButton" type="button" value="Utiliser clé de sécurité" onclick="useSecurityKey()"></div>
|
||
</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<hr><a onclick="return xgo(1,event);" href="#" style="cursor:pointer">Back to login</a>
|
||
<input id="tokenformargs" name="urlargs" type="hidden" value="">
|
||
</form>
|
||
</div>
|
||
<div id="resettokenpanel" style="display:none">
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="resetaccount">
|
||
<div id="message5"></div>
|
||
<table>
|
||
<tbody><tr>
|
||
<td align="right" width="100">Login token:</td>
|
||
<td>
|
||
<input id="resetTokenInput" type="text" name="token" maxlength="50" onchange="resetCheckToken(event)" onkeyup="resetCheckToken(event)" onkeydown="resetCheckToken(event)">
|
||
<input id="resetHwtokenInput" type="text" name="hwtoken" style="display:none">
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="2">
|
||
<div style="float:right"><input id="resetTokenOkButton" type="submit" value="Login" disabled="disabled"></div>
|
||
</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<hr><a onclick="return xgo(1,event);" href="#" style="cursor:pointer">Back to login</a>
|
||
<input id="resettokenformargs" name="urlargs" type="hidden" value="">
|
||
</form>
|
||
</div>
|
||
<div id="resetpasswordpanel" style="display:none;position:relative">
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="resetpassword">
|
||
<div id="message6"></div>
|
||
<div id="rpasswordPolicyCallout" style="display:none"></div>
|
||
<table>
|
||
<tbody><tr>
|
||
<td id="rnuPass1" width="100" align="right">Mot de passe:</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">Mot de passe:</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 id="resetpasswordpanelHint" style="display:none">
|
||
<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="Réinitialiser le mot de passe" disabled="disabled"></div>
|
||
<div id="rpassWarning" style="padding-top:6px"></div>
|
||
</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<hr><a onclick="return xgo(1,event);" href="#" style="cursor:pointer">Back to login</a>
|
||
<input id="resetpasswordformargs" name="urlargs" type="hidden" value="">
|
||
</form>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<br>
|
||
</div>
|
||
<div id="footer">
|
||
<div class="footer1">{{{footer}}}</div>
|
||
<div class="footer2">
|
||
{{{rootCertLink}}}
|
||
<a href="terms">Terms & Privacy</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<div id="dialog" style="display:none">
|
||
<div id="dialogHeader">
|
||
<div id="id_dialogclose" style="float:right;padding:5px;cursor:pointer" onclick="setDialogMode()"><b>X</b></div>
|
||
<div id="id_dialogtitle" style="padding:5px"></div>
|
||
<div style="width:100%;margin:6px"></div>
|
||
</div>
|
||
<div id="dialogBody">
|
||
<div id="dialog1">
|
||
<div id="id_dialogMessage" style=""></div>
|
||
</div>
|
||
<div id="dialog2" style="">
|
||
<div id="id_dialogOptions"></div>
|
||
</div>
|
||
</div>
|
||
<div id="idx_dlgButtonBar" style="">
|
||
<input id="idx_dlgCancelButton" type="button" value="Annuler" style="" onclick="dialogclose(0)">
|
||
<input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)">
|
||
</div>
|
||
</div>
|
||
<script>
|
||
'use strict';
|
||
var passhint = '{{{passhint}}}';
|
||
var loginMode = '{{{loginmode}}}';
|
||
var newAccount = '{{{newAccount}}}';
|
||
var newAccountPass = parseInt('{{{newAccountPass}}}');
|
||
var emailCheck = ('{{{emailcheck}}}' == 'true');
|
||
var passRequirements = '{{{passRequirements}}}';
|
||
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
||
if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
|
||
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
|
||
var features = parseInt('{{{features}}}');
|
||
var welcomeText = decodeURIComponent('{{{welcometext}}}');
|
||
var currentpanel = 0;
|
||
var uiMode = parseInt(getstore('uiMode', '1'));
|
||
var webPageFullScreen = true;
|
||
var nightMode = (getstore('_nightMode', '0') == '1');
|
||
var publicKeyCredentialRequestOptions = null;
|
||
|
||
// Display the right server message
|
||
var messageid = parseInt('{{{messageid}}}');
|
||
var okmessages = ['', "Attends, le courrier est envoyé."];
|
||
var failmessages = ["Unable to create account.", "Account limit reached.", "Existing account with this email address.", "Invalid account creation token.", "Ce nom d'utilisateur existe déjà.", "Mot de passe rejeté, utilisez-en un autre.", "Invalid email.", "Account not found.", "Invalid token, try again.", "Unable to sent email.", "Account locked.", "Access denied.", "Login failed, check username and password.", "Changement de mot de passe demandé.", "IP address blocked, try again later."];
|
||
if (messageid > 0) {
|
||
var msg = '';
|
||
if ((messageid < 100) && (messageid < okmessages.length)) { msg = okmessages[messageid]; }
|
||
else if ((messageid >= 100) && ((messageid - 100) < failmessages.length)) { msg = failmessages[messageid - 100]; }
|
||
if (msg != '') {
|
||
if (messageid >= 100) { msg = ('<span class="msg error"><b style=color:#8C001A>' + msg + '<b></span><br /><br />'); } else { msg = ('<span class="msg success"><b>' + msg + '</b></span><br /><br />'); }
|
||
for (var i = 1; i < 7; i++) { QH('message' + i, msg); }
|
||
}
|
||
}
|
||
|
||
// If URL arguments are provided, add them to form posts
|
||
if (window.location.href.indexOf('?') > 0) {
|
||
var urlargs = window.location.href.substring(window.location.href.indexOf('?'));
|
||
Q('loginformargs').value = urlargs;
|
||
Q('createformargs').value = urlargs;
|
||
Q('resetformargs').value = urlargs;
|
||
Q('tokenformargs').value = urlargs;
|
||
Q('resettokenformargs').value = urlargs;
|
||
Q('resetpasswordformargs').value = urlargs;
|
||
}
|
||
|
||
//var webPageFullScreen = getstore('webPageFullScreen', true);
|
||
//if (webPageFullScreen == 'false') { webPageFullScreen = false; }
|
||
//if (webPageFullScreen == 'true') { webPageFullScreen = true; }
|
||
//toggleFullScreen();
|
||
|
||
function startup() {
|
||
if ((features & 32) == 0) {
|
||
// Guard against other site's top frames (web bugs).
|
||
var loc = null;
|
||
try { loc = top.location.toString().toLowerCase(); } catch (e) { }
|
||
if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
|
||
}
|
||
|
||
if (features & 0x200000) { // Email is username
|
||
QH('loginusername', "Email:");
|
||
QH('resetAccountSpan', "Mot de passe oublié?");
|
||
QV('nuUserRow', false);
|
||
}
|
||
|
||
if (nightMode) { QC('body').add('night'); }
|
||
|
||
QV('createPanelHint', passRequirements.hint === true);
|
||
QV('resetpasswordpanelHint', passRequirements.hint === true);
|
||
|
||
// Display the welcome text
|
||
if (welcomeText) { QH('welcomeText', welcomeText); }
|
||
QH('welcomeText', addTextLink('MeshCentral', Q('welcomeText').innerHTML, 'http://www.meshcommander.com/meshcentral2'));
|
||
QV('welcomeText', true);
|
||
|
||
window.onresize = center;
|
||
center();
|
||
|
||
validateLogin();
|
||
validateCreate();
|
||
if (loginMode.length != 0) { go(parseInt(loginMode)); } else { go(1); }
|
||
QV('newAccountDiv', (newAccount === '1') || (newAccount === 'true')); // If new accounts are not allowed, don't display the new account link.
|
||
if ((passhint != null) && (passhint.length > 0)) { QV('showPassHintLink', true); }
|
||
QV('newAccountPass', (newAccountPass == 1));
|
||
QV('resetAccountDiv', (emailCheck == true));
|
||
QV('hrAccountDiv', (emailCheck == true) || (newAccountPass == 1));
|
||
|
||
if (loginMode == '4') {
|
||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||
QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
|
||
}
|
||
|
||
if (loginMode == '5') {
|
||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||
if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; }
|
||
|
||
publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
||
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
||
publicKeyCredentialRequestOptions.allowCredentials.push(
|
||
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
||
);
|
||
}
|
||
|
||
// New WebAuthn hardware keys
|
||
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
||
function (rawAssertion) {
|
||
var assertion = {
|
||
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
|
||
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
|
||
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
|
||
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
|
||
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
|
||
};
|
||
Q('resetHwtokenInput').value = JSON.stringify(assertion);
|
||
QE('resetTokenOkButton', true);
|
||
Q('resetTokenOkButton').click();
|
||
},
|
||
function (error) { console.log('credentials-get error', error); }
|
||
);
|
||
}
|
||
}
|
||
|
||
// Setup the user interface in the right mode
|
||
userInterfaceSelectMenu();
|
||
}
|
||
|
||
// Use a hardware security key
|
||
function useSecurityKey() {
|
||
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||
if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; }
|
||
|
||
publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
||
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
||
publicKeyCredentialRequestOptions.allowCredentials.push(
|
||
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
||
);
|
||
}
|
||
|
||
// New WebAuthn hardware keys
|
||
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
||
function (rawAssertion) {
|
||
var assertion = {
|
||
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
|
||
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
|
||
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
|
||
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
|
||
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
|
||
};
|
||
Q('hwtokenInput').value = JSON.stringify(assertion);
|
||
QE('tokenOkButton', true);
|
||
Q('tokenOkButton').click();
|
||
},
|
||
function (error) { console.log('credentials-get error', error); }
|
||
);
|
||
}
|
||
}
|
||
|
||
function showPassHint(e) {
|
||
messagebox("Password Hint", passhint);
|
||
haltEvent(e);
|
||
return false;
|
||
}
|
||
|
||
function xgo(x, e) {
|
||
QV('message1', false);
|
||
QV('message2', false);
|
||
QV('message3', false);
|
||
QV('message4', false);
|
||
QV('message5', false);
|
||
QV('message6', false);
|
||
go(x);
|
||
haltEvent(e);
|
||
return false;
|
||
}
|
||
|
||
function go(x) {
|
||
currentpanel = x;
|
||
setDialogMode(0);
|
||
QV('showPassHintLink', false);
|
||
QV('loginpanel', x == 1);
|
||
QV('createpanel', x == 2);
|
||
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) { if (features & 0x200000) { Q('aemail').focus(); } else { Q('ausername').focus(); } } // Email is username
|
||
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) {
|
||
var ok = ((Q('username').value.length > 0) && (Q('username').value.indexOf(' ') == -1) && (Q('password').value.length > 0));
|
||
QE('loginButton', ok);
|
||
setDialogMode(0);
|
||
if ((e != null) && (e.keyCode == 13)) { if ((box == 1) && (Q('username').value != '')) { Q('password').focus(); } else if ((box == 2) && (Q('password').value != '')) { Q('loginButton').click(); } }
|
||
if (e != null) { haltEvent(e); }
|
||
}
|
||
|
||
function validateCreate(box, e) {
|
||
setDialogMode(0);
|
||
var userok = false;
|
||
if (features & 0x200000) { userok = true; } else { userok = (Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1) && (Q('ausername').value.indexOf('"') == -1) && (Q('ausername').value.indexOf(',') == -1); }
|
||
var emailok = (validateEmail(Q('aemail').value) == true);
|
||
var pass1ok = (Q('apassword1').value.length > 0);
|
||
var pass2ok = (Q('apassword2').value.length > 0) && (Q('apassword2').value == Q('apassword1').value);
|
||
var newAccOk = (newAccountPass == 0) || (Q('anewaccountpass').value.length > 0);
|
||
var ok = (userok && emailok && pass1ok && pass2ok && newAccOk);
|
||
|
||
// Color the fields
|
||
QS('nuUser').color = userok?'black':'#7b241c';
|
||
QS('nuEmail').color = emailok?'black':'#7b241c';
|
||
QS('nuPass1').color = pass1ok?'black':'#7b241c';
|
||
QS('nuPass2').color = pass2ok?'black':'#7b241c';
|
||
QS('nuToken').color = newAccOk?'black':'#7b241c';
|
||
|
||
if (Q('apassword1').value == '') {
|
||
QH('passWarning', '');
|
||
QV('passwordPolicyCallout', false);
|
||
} else {
|
||
if (!passRequirementsEx) {
|
||
// No password requirements, display password strength
|
||
var passStrength = checkPasswordStrength(Q('apassword1').value);
|
||
if (passStrength >= 80) { QH('passWarning', '<span style=color:green><b>' + "Mot de passe fort" + '</b><span>'); }
|
||
else if (passStrength >= 60) { QH('passWarning', '<span style=color:blue><b>' + "Bon mot de passe" + '</b><span>'); }
|
||
else { QH('passWarning', '<span style=color:red><b>' + "Mot de passe faible" + '</b><span>'); }
|
||
} else {
|
||
// Password requirements provided, use that
|
||
var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
|
||
if (passReq == false) {
|
||
ok = false;
|
||
QS('nuPass1').color = '#7b241c';
|
||
QS('nuPass2').color = '#7b241c';
|
||
QH('passWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>' + "Password Policy" + '</b><div>'); // This is also a link to the password policy
|
||
QV('passwordPolicyCallout', true);
|
||
QH('passwordPolicyCallout', passwordPolicyText(Q('apassword1').value));
|
||
} else {
|
||
QH('passWarning', '');
|
||
QV('passwordPolicyCallout', false);
|
||
}
|
||
}
|
||
}
|
||
if ((e != null) && (e.keyCode == 13)) {
|
||
|
||
if ((box == 1) && userok) { Q('aemail').focus(); }
|
||
if ((box == 2) && emailok) { Q('apassword1').focus(); }
|
||
if ((box == 3) && pass1ok) { Q('apassword2').focus(); }
|
||
if ((box == 4) && pass2ok) { if (passRequirements.hint === true) { Q('apasswordhint').focus(); } else { box = 5; } }
|
||
if (box == 5) { if (newAccountPass == 1) { Q('anewaccountpass').focus(); } else { box = 6; } }
|
||
if (box == 6) { Q('createButton').click(); }
|
||
}
|
||
if (e != null) { haltEvent(e); }
|
||
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 (!passRequirementsEx) {
|
||
// No password requirements, display password strength
|
||
var passStrength = checkPasswordStrength(Q('rapassword1').value);
|
||
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>' + "Mot de passe fort" + '</b><span>'); }
|
||
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>' + "Bon mot de passe" + '</b><span>'); }
|
||
else { QH('rpassWarning', '<span style=color:red><b>' + "Mot de passe faible" + '</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);
|
||
if (passRequirements.min && ((pass == null) || (pass.length < passRequirements.min))) { policy += format("Minimum length of {0}", passRequirements.min) + '<br />'; }
|
||
if (passRequirements.max && ((pass == null) || (pass.length > passRequirements.max))) { policy += format("Longueur maximale de {0}", passRequirements.max) + '<br />'; }
|
||
if (passRequirements.upper && ((pass == null) || (counts.upper < passRequirements.upper))) { policy += format("{0} upper case", passRequirements.upper) + '<br />'; }
|
||
if (passRequirements.lower && ((pass == null) || (counts.lower < passRequirements.lower))) { policy += format("{0} lower case", passRequirements.lower) + '<br />'; }
|
||
if (passRequirements.numeric && ((pass == null) || (counts.numeric < passRequirements.numeric))) { policy += format("{0} numeric", passRequirements.numeric) + '<br />'; }
|
||
if (passRequirements.nonalpha && ((pass == null) || (counts.nonalpha < passRequirements.nonalpha))) { policy += format("{0} non-alphanumeric", passRequirements.nonalpha) + '<br />'; }
|
||
policy += '</div>';
|
||
return policy;
|
||
}
|
||
|
||
function showPasswordPolicy() {
|
||
messagebox("Password Policy", passwordPolicyText());
|
||
}
|
||
|
||
function validateReset(e) {
|
||
setDialogMode(0);
|
||
var x = validateEmail(Q('remail').value);
|
||
QE('eresetButton', x);
|
||
if ((e != null) && (e.keyCode == 13) && (x == true)) {
|
||
Q('eresetButton').click();
|
||
}
|
||
if (e != null) { haltEvent(e); }
|
||
}
|
||
|
||
// Return a password strength score
|
||
function checkPasswordStrength(password) {
|
||
var r = 0, letters = {}, varCount = 0, variations = { digits: /\d/.test(password), lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), nonWords: /\W/.test(password) }
|
||
if (!password) return 0;
|
||
for (var i = 0; i< password.length; i++) { letters[password[i]] = (letters[password[i]] || 0) + 1; r += 5.0 / letters[password[i]]; }
|
||
for (var c in variations) { varCount += (variations[c] == true) ? 1 : 0; }
|
||
return parseInt(r + (varCount - 1) * 10);
|
||
}
|
||
|
||
// Check password requirements
|
||
function checkPasswordRequirements(password, requirements) {
|
||
if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
|
||
if (requirements.min) { if (password.length < requirements.min) return false; }
|
||
if (requirements.max) { if (password.length > requirements.max) return false; }
|
||
var counts = strCount(password);
|
||
if (requirements.numeric && (counts.numeric < requirements.numeric)) return false;
|
||
if (requirements.lower && (counts.lower < requirements.lower)) return false;
|
||
if (requirements.upper && (counts.upper < requirements.upper)) return false;
|
||
if (requirements.nonalpha && (counts.nonalpha < requirements.nonalpha)) return false;
|
||
return true;
|
||
}
|
||
|
||
function strCount(password) {
|
||
var counts = { numeric: 0, lower: 0, upper: 0, nonalpha: 0 };
|
||
if (typeof password != 'string') return counts;
|
||
for (var i = 0; i < password.length; i++) {
|
||
if (/\d/.test(password[i])) { counts.numeric++; }
|
||
if (/[a-z]/.test(password[i])) { counts.lower++; }
|
||
if (/[A-Z]/.test(password[i])) { counts.upper++; }
|
||
if (/\W/.test(password[i])) { counts.nonalpha++; }
|
||
}
|
||
return counts;
|
||
}
|
||
|
||
function checkToken() {
|
||
var t1 = Q('tokenInput').value;
|
||
var t2 = t1.split(' ').join('');
|
||
if (t1 != t2) { Q('tokenInput').value = t2; }
|
||
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44));
|
||
}
|
||
|
||
function resetCheckToken() {
|
||
var t1 = Q('resetTokenInput').value;
|
||
var t2 = t1.split(' ').join('');
|
||
if (t1 != t2) { Q('resetTokenInput').value = t2; }
|
||
QE('resetTokenOkButton', (Q('resetTokenInput').value.length == 6) || (Q('resetTokenInput').value.length == 8) || (Q('resetTokenInput').value.length == 44));
|
||
}
|
||
|
||
//
|
||
// POPUP DIALOG
|
||
//
|
||
|
||
// undefined = Hidden, 1 = Generic Message
|
||
var xxdialogMode;
|
||
var xxdialogFunc;
|
||
var xxdialogButtons;
|
||
var xxdialogTag;
|
||
var xxcurrentView = 0;
|
||
|
||
// Display a dialog box
|
||
// Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only)
|
||
function setDialogMode(x, y, b, f, c, tag) {
|
||
xxdialogMode = x;
|
||
xxdialogFunc = f;
|
||
xxdialogButtons = b;
|
||
xxdialogTag = tag;
|
||
QE('idx_dlgOkButton', true);
|
||
QV('idx_dlgOkButton', b & 1);
|
||
QV('idx_dlgCancelButton', b & 2);
|
||
QV('id_dialogclose', (b & 2) || (b & 8));
|
||
QV('idx_dlgButtonBar', b & 7);
|
||
if (y) QH('id_dialogtitle', y);
|
||
for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
|
||
QV('dialog', x);
|
||
if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
|
||
}
|
||
|
||
function dialogclose(x) {
|
||
var f = xxdialogFunc;
|
||
var b = xxdialogButtons;
|
||
var t = xxdialogTag;
|
||
setDialogMode();
|
||
if (((b & 8) || x) && f) f(x, t);
|
||
}
|
||
|
||
// Toggle the web page to full screen
|
||
function toggleFullScreen(toggle) {
|
||
//if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
|
||
if (webPageFullScreen == false) {
|
||
// By adding body class, it will change a style of all ellements using CSS selector
|
||
// No need for JS anymore and it will be consistent style for all the templates.
|
||
QC('body').remove('fullscreen');
|
||
} else {
|
||
QC('body').add('fullscreen');
|
||
}
|
||
QV('body', true);
|
||
center();
|
||
}
|
||
|
||
// Toggle user interface menu
|
||
function showUserInterfaceSelectMenu() {
|
||
Q('uiViewButton1').classList.remove('uiSelectorSel');
|
||
Q('uiViewButton2').classList.remove('uiSelectorSel');
|
||
Q('uiViewButton3').classList.remove('uiSelectorSel');
|
||
try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
|
||
QV('uiMenu', (QS('uiMenu').display == 'none'));
|
||
if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
|
||
}
|
||
|
||
function userInterfaceSelectMenu(s) {
|
||
if (s) { uiMode = s; putstore('uiMode', uiMode); }
|
||
webPageFullScreen = (uiMode < 3);
|
||
//webPageStackMenu = (uiMode > 1);
|
||
toggleFullScreen(0);
|
||
//toggleStackMenu(0);
|
||
}
|
||
|
||
function toggleNightMode() {
|
||
nightMode = !nightMode;
|
||
if (nightMode) { QC('body').add('night'); } else { QC('body').remove('night'); }
|
||
putstore('_nightMode', (nightMode ? '1' : '0'));
|
||
}
|
||
|
||
function center() {
|
||
/* Now we use CSS media to achive the same effect as deleted JS */
|
||
if (webPageFullScreen == false) {
|
||
QS('centralTable')['margin-top'] = '';
|
||
} else {
|
||
var h = ((Q('column_l').clientHeight) / 2) - 220;
|
||
if (h < 0) h = 0;
|
||
QS('centralTable')['margin-top'] = h + 'px';
|
||
}
|
||
}
|
||
function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
|
||
function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); }
|
||
function getDocWidth() { if (window.innerWidth) return window.innerWidth; if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth != 0) return document.documentElement.clientWidth; return document.getElementsByTagName('body')[0].clientWidth; }
|
||
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
function haltReturn(e) { if (e.keyCode == 13) { haltEvent(e); } }
|
||
function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } // New version
|
||
function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } }
|
||
function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
|
||
function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
|
||
function addTextLink(subtext, text, link) { var i = text.toLowerCase().indexOf(subtext.toLowerCase()); if (i == -1) { return text; } return text.substring(0, i) + '<a href=\"' + link + '\">' + subtext + '</a>' + text.substring(i + subtext.length); }
|
||
|
||
</script>
|
||
|
||
</body></html> |