647 lines
43 KiB
Handlebars
647 lines
43 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">
|
|
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
|
<script type="text/javascript" src="scripts/u2f-api.js"></script>
|
|
<title>{{{title}}} - Login</title>
|
|
<style>
|
|
a {
|
|
color: #036;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
#footer a {
|
|
color: #fff;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
#footer a:hover {
|
|
color: #fff;
|
|
text-decoration: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;color:black;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
|
|
<div id="container">
|
|
<div id="mastheadx"></div>
|
|
<div id="masthead" style="background:url(logo.png) 0px 0px;background-size:341px 50px;background-color:#036;background-repeat:no-repeat;height:50px;width:100%;overflow:hidden">
|
|
<div style="float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:6px">
|
|
<strong><font style="font-size:36px;font-family:Arial,Helvetica,sans-serif">{{{title}}}</font></strong>
|
|
</div>
|
|
<div style="float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:10px">
|
|
<strong><font style="font-size:12px;font-family:Arial,Helvetica,sans-serif">{{{title2}}}</font></strong>
|
|
</div>
|
|
</div>
|
|
<div id="page_content" style="overflow-y:scroll;position:absolute;bottom:32px;top:50px;width:100%;display:flex;align-items:center">
|
|
<div id="column_l" style="padding:10px;width:100%">
|
|
<table style="width:100%">
|
|
<tbody><tr>
|
|
<td align="center">
|
|
<div id="loginpanel" style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;clear:both;display:none">
|
|
<form method="post">
|
|
<input type="hidden" name="action" value="login">
|
|
<div id="message1"></div>
|
|
<div>
|
|
<b>Přihlásit</b>
|
|
</div>
|
|
<table>
|
|
<tbody><tr>
|
|
<td id="loginusername" align="right" width="100">Uživatel:</td>
|
|
<td><input id="username" type="text" maxlength="64" name="username" onchange="validateLogin(1)" onkeyup="validateLogin(1,event)"></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="right">Heslo:</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="showPassHint()" style="cursor:pointer">Zobrazit nápovědu</a></div></td>
|
|
<td align="right"><input id="loginButton" type="submit" value="Přihlásit" 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">Zapomenuté jméno/heslo?</span> <a onclick="xgo(3)" style="cursor:pointer">Reset účtu</a>.
|
|
</div>
|
|
<div id="newAccountDiv" style="display:none;padding:2px">
|
|
Nemáte účet? <a onclick="xgo(2)" style="cursor:pointer">Vytvořit</a>.
|
|
</div>
|
|
<input id="loginformargs" name="urlargs" type="hidden" value="">
|
|
</form>
|
|
</div>
|
|
<div id="createpanel" style="display:none">
|
|
<div style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;clear:both;position:relative">
|
|
<form method="post">
|
|
<input type="hidden" name="action" value="createaccount">
|
|
<div id="message2"></div>
|
|
<div>
|
|
<b>Vytvoření účtu</b>
|
|
</div>
|
|
<div id="passwordPolicyCallout" style="left:-5px;top:10px;width:100px;position:absolute;background-color:#FFC;border-radius:5px;padding:5px;box-shadow:0px 0px 15px #666;font-size:10px"></div>
|
|
<table>
|
|
<tbody><tr id="nuUserRow">
|
|
<td align="right" width="100">Uživatel:</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 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 align="right">Heslo:</td>
|
|
<td><input id="apassword1" type="password" name="password1" autocomplete="off" maxlength="256" onkeydown="haltReturn(event)" onchange="validateCreate(3)" onkeyup="validateCreate(3,event)"></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="right">Heslo:</td>
|
|
<td><input id="apassword2" type="password" name="password2" autocomplete="off" maxlength="256" onkeydown="haltReturn(event)" onchange="validateCreate(4)" onkeyup="validateCreate(4,event)"></td>
|
|
</tr>
|
|
<tr id="createPanelHint" style="display:none">
|
|
<td align="right">Nápověda k heslu:</td>
|
|
<td><input id="apasswordhint" type="text" name="apasswordhint" autocomplete="off" maxlength="256" onkeydown="haltReturn(event)" onchange="validateCreate(5)" onkeyup="validateCreate(5,event)"></td>
|
|
</tr>
|
|
<tr id="newAccountPass" title="Zadejte token pro vytvoření účtu">
|
|
<td align="right">Vytvoření tokenu:</td>
|
|
<td><input id="anewaccountpass" type="password" name="anewaccountpass" autocomplete="off" maxlength="256" onkeydown="haltReturn(event)" onchange="validateCreate(6)" onkeyup="validateCreate(6,event)"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="2">
|
|
<div style="float:right"><input id="createButton" type="submit" value="Vytvořit účet" disabled="disabled"></div>
|
|
<div id="passWarning" style="padding-top:6px"></div>
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
<hr><a onclick="xgo(1)" style="cursor:pointer">Zpět na přihlášení</a>
|
|
<input id="createformargs" name="urlargs" type="hidden" value="">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div id="resetpanel" style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
|
|
<form method="post">
|
|
<input type="hidden" name="action" value="resetaccount">
|
|
<div id="message3"></div>
|
|
<div>
|
|
<b>Reset hesla</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="Reset účtu" disabled="disabled"></div>
|
|
<div id="passWarning" style="padding-top:6px"></div>
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
<hr><a onclick="xgo(1)" style="cursor:pointer">Zpět na přihlášení</a>
|
|
<input id="resetformargs" name="urlargs" type="hidden" value="">
|
|
</form>
|
|
</div>
|
|
<div id="tokenpanel" style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
|
|
<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">Přihlašovací token:</td>
|
|
<td>
|
|
<input id="tokenInput" type="text" name="token" maxlength="50" onchange="checkToken(event)" onkeyup="checkToken(event)" onkeydown="checkToken(event)" onfocus="checkTokenTimer(1)" onblur="checkTokenTimer(0)">
|
|
<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">Zapamatovat toto zařízení 30 dní.</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="2">
|
|
<div style="float:right"><input id="tokenOkButton" type="submit" value="Přihlášení" disabled="disabled"></div>
|
|
<div style="float:right"><input style="display:none;float:right" id="securityKeyButton" type="button" value="Použít bezpečnostní klíč" onclick="useSecurityKey()"></div>
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
<hr><a onclick="xgo(1)" style="cursor:pointer">Zpět na přihlášení</a>
|
|
<input id="tokenformargs" name="urlargs" type="hidden" value="">
|
|
</form>
|
|
</div>
|
|
|
|
<div id="resettokenpanel" style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
|
|
<form method="post" autocomplete="off">
|
|
<input type="hidden" name="action" value="resetaccount">
|
|
<div id="message5"></div>
|
|
<table>
|
|
<tbody><tr>
|
|
<td align="right" width="100">Přihlašovací token:</td>
|
|
<td>
|
|
<input id="resetTokenInput" type="text" name="token" maxlength="50" onchange="resetCheckToken(event)" onpaste="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="Přihlášení" disabled="disabled"></div>
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
<hr><a onclick="xgo(1)" style="cursor:pointer">Zpět na přihlášení</a>
|
|
<input id="resettokenformargs" name="urlargs" type="hidden" value="">
|
|
</form>
|
|
</div>
|
|
|
|
<div id="resetpasswordpanel" style="position:relative;background-color:#979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
|
|
<form method="post">
|
|
<input type="hidden" name="action" value="resetpassword">
|
|
<div id="message6"></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>
|
|
<tbody><tr>
|
|
<td id="rnuPass1" width="100" align="right">Heslo:</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">Heslo:</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">Nápověda k heslu:</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 hesla" disabled="disabled"></div>
|
|
<div id="rpassWarning" style="padding-top:6px"></div>
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
<hr><a onclick="xgo(1)" style="cursor:pointer">Zpět na přihlášení</a>
|
|
<input id="resetpasswordformargs" name="urlargs" type="hidden" value="">
|
|
</form>
|
|
</div>
|
|
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
<div id="footer" style="height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px">
|
|
<table cellpadding="0" cellspacing="6" style="width:100%">
|
|
<tbody><tr>
|
|
<td style="text-align:left;color:white">{{{footer}}}</td>
|
|
<td style="text-align:right">{{{rootCertLink}}} <a href="terms">Podmínky & soukromí</a></td>
|
|
</tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
<div id="dialog" style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:180px;width:400px;display:none">
|
|
<div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0">
|
|
<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 style="margin-right:16px;margin-left:8px">
|
|
<div id="dialog1" style="margin:auto;text-align:center;margin:3px">
|
|
<div id="id_dialogMessage" style="padding:10px"></div>
|
|
</div>
|
|
<div id="dialog2" style="margin:auto;margin:3px">
|
|
<div id="id_dialogOptions"></div>
|
|
</div>
|
|
</div>
|
|
<div id="idx_dlgButtonBar" style="padding:10px;margin-bottom:20px">
|
|
<input id="idx_dlgCancelButton" type="button" value="Zrušit" style="float:right;width:80px;margin-left:5px" onclick="dialogclose(0)">
|
|
<input id="idx_dlgOkButton" type="button" value="OK" style="float:right;width:80px" onclick="dialogclose(1)">
|
|
</div>
|
|
</div>
|
|
<script>
|
|
'use strict';
|
|
var loginMode = '{{{loginmode}}}';
|
|
var newAccount = '{{{newAccount}}}';
|
|
var passhint = '{{{passhint}}}';
|
|
var newAccountPass = parseInt('{{{newAccountPass}}}');
|
|
var emailCheck = ('{{{emailcheck}}}' == 'true');
|
|
var features = parseInt('{{{features}}}');
|
|
var passRequirements = '{{{passRequirements}}}';
|
|
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 hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
|
var publicKeyCredentialRequestOptions = null;
|
|
var currentpanel = 0;
|
|
|
|
// Display the right server message
|
|
var messageid = parseInt('{{{messageid}}}');
|
|
var okmessages = ['', "Vydržte, email je na cestě."];
|
|
var failmessages = ["Nelze vytvořit účet.", "Maximální počet účtů dosažen.", "Existuje účet s tímto emailem.", "Neplatný token pro vytvoření účtu.", "Uživatel již existuje.", "Heslo odmítnuto, zkuste jiné.", "Neplatný email.", "Účet nenalezen.", "Neplatný token, zkuste znovu.", "Nelze poslat email.", "Účet uzamknut.", "Přístup zamítnut", "Neplatné přihlášení, zkontrolujte jméno a heslo.", "Vyžaduje se změna hesla.", "IP adresa blokována, zkuste to později."];
|
|
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;
|
|
}
|
|
|
|
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', "Zapomenuté heslo?");
|
|
QV('nuUserRow', false);
|
|
}
|
|
|
|
QV('createPanelHint', passRequirements.hint === true);
|
|
QV('resetpasswordpanelHint', passRequirements.hint === 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 ((passRequirements.hint === true) && (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); }
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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() {
|
|
if (passRequirements.hint === true) { messagebox("Nápověda k heslu", passhint); }
|
|
}
|
|
|
|
function xgo(x) {
|
|
QV('message1', false);
|
|
QV('message2', false);
|
|
QV('message3', false);
|
|
QV('message4', false);
|
|
QV('message5', false);
|
|
QV('message6', false);
|
|
go(x);
|
|
}
|
|
|
|
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('password').focus(); } else if (box == 2) { Q('loginButton').click(); } }
|
|
if (e != null) { haltEvent(e); }
|
|
}
|
|
|
|
function validateCreate(box,e) {
|
|
setDialogMode(0);
|
|
var ok = false;
|
|
if (features & 0x200000) { ok = true; } else { ok = (Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1) && (Q('ausername').value.indexOf('"') == -1) && (Q('ausername').value.indexOf(',') == -1); }
|
|
ok &= ((validateEmail(Q('aemail').value) == true) && (Q('apassword1').value.length > 0) && (Q('apassword2').value == Q('apassword1').value));
|
|
if ((newAccountPass == 1) && (Q('anewaccountpass').value.length == 0)) { ok = false; }
|
|
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>' + "Silné heslo" + '</b><span>'); }
|
|
else if (passStrength >= 60) { QH('passWarning', '<span style=color:blue><b>' + "Dobré heslo" + '</b><span>'); }
|
|
else { QH('passWarning', '<span style=color:red><b>' + "Slabé heslo" + '</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', '<span style=color:red><b>' + "Politia hesla" + '</b><span>'); // TODO: Display problem hint
|
|
QV('passwordPolicyCallout', true);
|
|
QH('passwordPolicyCallout', passwordPolicyText(Q('apassword1').value));
|
|
} else {
|
|
QH('passWarning', '');
|
|
QV('passwordPolicyCallout', false);
|
|
}
|
|
}
|
|
}
|
|
QE('createButton', ok);
|
|
if ((e != null) && (e.keyCode == 13)) {
|
|
if (box == 1) { Q('aemail').focus(); }
|
|
if (box == 2) { Q('apassword1').focus(); }
|
|
if (box == 3) { Q('apassword2').focus(); }
|
|
if (box == 4) { Q('apasswordhint').focus(); }
|
|
if (box == 5) { if (newAccountPass == 1) { Q('anewaccountpass').focus(); } else { Q('createButton').click(); } }
|
|
if (box == 6) { Q('createButton').click(); }
|
|
}
|
|
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 (!passRequirementsEx) {
|
|
// No password requirements, display password strength
|
|
var passStrength = checkPasswordStrength(Q('rapassword1').value);
|
|
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>' + "Silné heslo" + '</b><span>'); }
|
|
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>' + "Dobré heslo" + '</b><span>'); }
|
|
else { QH('rpassWarning', '<span style=color:red><b>' + "Slabé heslo" + '</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>' + "Politia hesla" + '</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);
|
|
QE('eresetButton', x);
|
|
if ((e != null) && (e.keyCode == 13) && (x == true)) { Q('eresetButton').click(); }
|
|
if (e != null) { haltEvent(e); }
|
|
}
|
|
|
|
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("Minimální délka {0}", passRequirements.min) + '<br />'; }
|
|
if (passRequirements.max && ((pass == null) || (pass.length > passRequirements.max))) { policy += format("Maximální délka {0}", passRequirements.max) + '<br />'; }
|
|
if (passRequirements.upper && ((pass == null) || (counts.upper < passRequirements.upper))) { policy += format("{0} velkých písmen", passRequirements.upper) + '<br />'; }
|
|
if (passRequirements.lower && ((pass == null) || (counts.lower < passRequirements.lower))) { policy += format("{0} malých písmen", passRequirements.lower) + '<br />'; }
|
|
if (passRequirements.numeric && ((pass == null) || (counts.numeric < passRequirements.numeric))) { policy += format("{0} čísel", passRequirements.numeric) + '<br />'; }
|
|
if (passRequirements.nonalpha && ((pass == null) || (counts.nonalpha < passRequirements.nonalpha))) { policy += format("{0} speciálních znaků", passRequirements.nonalpha) + '<br />'; }
|
|
policy += '</div>';
|
|
return policy;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
var xcheckTokenTimer = null;
|
|
function checkTokenTimer(enter) {
|
|
if ((enter == 0) && (xcheckTokenTimer != null)) { clearInterval(xcheckTokenTimer); xcheckTokenTimer = null; }
|
|
if ((enter == 1) && (xcheckTokenTimer == null)) { xcheckTokenTimer = setInterval(checkToken, 200); }
|
|
}
|
|
|
|
function checkToken() {
|
|
var t1 = Q('tokenInput').value, 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, 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);
|
|
}
|
|
|
|
function center() { QS('dialog').left = ((((getDocWidth() - 400) / 2)) + '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 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; }); };
|
|
|
|
</script>
|
|
|
|
</body></html> |