MeshCentral/views/login2.handlebars

1042 lines
71 KiB
Handlebars
Raw Normal View History

<!DOCTYPE html>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head lang="en">
<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" />
2020-10-28 17:46:40 -04:00
<meta name="robots" content="noindex,nofollow">
<link rel="manifest" href="{{{domainurl}}}manifest.json">
<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" />
<link rel="apple-touch-icon" href="/favicon-303x303.png" />
<script type="text/javascript" src="scripts/common-0.0.1{{min}}.js"></script>
<script keeplink=1 type="text/javascript" src="scripts/u2f-api{{min}}.js"></script>
<title>{{{title}}} - Login</title>
<style>
#body {
background-color: cadetblue;
background: linear-gradient(to bottom right, #369, #036);
}
#flink a:link {
color: #c8c8c8;
}
#flink a:visited {
color: #c8c8c8;
}
#flink a:hover {
color: #c8c8c8;
}
#flink a:active {
color: #c8c8c8;
}
#welcomeText a:link {
color: #FFF;
}
#welcomeText a:visited {
color: #FFF;
}
#welcomeText a:hover {
color: #FFF;
}
#welcomeText a:active {
color: #FFF;
}
</style>
</head>
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide login">
<div id="backgroundImage" style="position:absolute;left:0;bottom:0;z-index:-1;height:60%;width:100%;opacity:0.1;background-image:url('welcome.png');background-repeat:no-repeat;background-position:left bottom;background-size:contain"></div>
<table id="centralTable" class="container" style="height:100%;z-index:1">
<tr>
<td id="logincell">
{{{titlehtml}}}
<img id="loginPicture" />
<div style="font-size:46px;font-family:Arial,Helvetica,sans-serif;font-weight:bold;padding-bottom:10px;color:#c8c8c8;text-shadow: 2px 2px 2px #000;">{{{title1}}}<sup style="font-size:20px"> {{{title2}}}</sup></div>
<div id=loginpanel style="display:none;box-shadow:1px 1px 4px #000">
<form id=loginpanelform method=post>
<input type=hidden name=action value=login />
<div id=message1></div>
2020-12-19 21:35:22 -05:00
<div id="loginuserpassdiv" style="display:none">
<table style="width:100%">
<tr>
<td>
<input id=username title="Username" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" {{{autocomplete}}}="username" placeholder="Username" type=text maxlength=64 name=username onchange=validateLogin(1) onkeyup=validateLogin(1,event) />
2020-12-19 21:35:22 -05:00
</td>
</tr>
<tr>
<td>
<input id=password title="Password" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" {{{autocomplete}}}="current-password" placeholder="Password" type=password maxlength=256 name=password onchange=validateLogin(2) onkeyup=validateLogin(2,event) />
2020-12-19 21:35:22 -05:00
</td>
</tr>
<tr>
<td>
<div id=showPassHintLink title="Password Hint" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC;display:none"><a onclick="return showPassHint(event);" href="#" style="cursor:pointer">Show Hint</a></div>
</td>
</tr>
<tr>
<td>
<input id=loginButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" onclick="submitButtonClicked('loginpanelform')" type=button value="Log In" disabled="disabled" />
2020-12-19 21:35:22 -05:00
</td>
</tr>
<tr id="topLanguageSelectRow" style="display:none"><td id="topLanguageSelect"></td></tr>
2020-12-19 21:35:22 -05:00
</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>Reset account</a>.
</div>
<div id="newAccountDiv" style="display:none;padding:2px;padding-top:10px">
Don&#39;t have an account? <a onclick="return xgo(2,event);" href="#" style=cursor:pointer>Create one</a>.
</div>
</div>
<input id=loginformargs name="urlargs" type="hidden" value="" />
<div id="authStrategies" style="display:none">
2020-12-19 21:35:22 -05:00
<hr id="loginuserpasshr" style="display:none" />
<div style="margin-bottom:8px">Log in using an existing account</div>
2021-01-09 05:40:46 -05:00
<a id="auth-twitter" href="auth-twitter" style="display:none"><img src="images/login/twitter32.png" srcset="images/login/twitter64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Twitter" /></a>
<a id="auth-google" href="auth-google" style="display:none"><img src="images/login/google32.png" srcset="images/login/google64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Google" /></a>
<a id="auth-github" href="auth-github" style="display:none"><img src="images/login/github32.png" srcset="images/login/github64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using GitHub" /></a>
<a id="auth-azure" href="auth-azure" style="display:none"><img src="images/login/azure32.png" srcset="images/login/azure64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Azure" /></a>
2022-04-06 12:39:13 -04:00
<a id="auth-oidc" href="auth-oidc" style="display:none"><img src="images/login/oidc32.png" srcset="images/login/oidc64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using OpenID Connect" /></a>
Migrate to openid client (#5856) * Create forksync.yml * update oidc to use openid-client * update oidc module requirements * working oidc+ includes all oauth2 clients automatically migrated. azure will need some kind of fix for the uid * update openid-client install checks * created overarching schema for OIDC * bug fixs for azure login * update schema prepare schema for unified oidc module * update 'oidc' to strategy variable * working azure+ groups groups from azure are in, you can use memberOf or transitiveMemberOf in config (Graphs API) * clean up old config import + working google oidc previous config map was recursive nonsense, changed to multiple IFs * added convertStrArray * de-expanded scope put all other auth strategies back to normal and fixed oidc strategy * swap back to using authlog debugger * Update meshcentral-config-schema.json * working google oidc + groups * working azure+groups (again) * init oidc docs very incomplete but basic config is present * add oidc * more work on docs * add scope and claim options plus fixed a few bugs and faults in my logic used logs correctly * further cleanup debug * more debug cleanup * continue documentation push fixed minor debug bugs also * more work on docs missing links, need to get azure preset docs, probably more. * done with docs its good enough for now * minor fix + presets get correct icon * fix google oidc not visible at login * fix bug with emailVerified property * fix logout bug + debug cleanup * fix strategy logout bug +cleanup * fixed preset login icon * fix alert + fix schema * terminate lines * Dutch language update 1.0.85 line up polish translation * Fixed guest web relay session revocation (#4667) * Updated French translation. * Add hook to allow adding custom api endpoints to Express routing * Updated German translation. * Update meshcentral-config-schema.json (change formatting) This way it is easier to edit and maintain * Fixed schema. * fix meshcentral-config-schema.json * add language selector to login (#5648) * add language selector to login * add showLanguageSelect to pick top or bottom boxe * remove additionalProperties: false in schema to allow comments #5697 Signed-off-by: si458 <simonsmith5521@gmail.com> * fix notes in docs * Fix web relay session handling and redirection due to bad merge * Added option to check HTTP origin. * add links and fix typo * move groups after strategy * Update version split in docs * Fix preset issuer URL in OIDC strategy * Update clientid and clientsecret to client_id and client_secret * Update meshcentral-config-schema.json and fix bad rebase * Update meshcentral-config-schema.json * fix bad rebase * fix bad rebase * Add 'connect-flash' to passport dependencies * Remove unnecessary passport dependencies - fix bad rebase * Fix auth strategy bug and remove console.log statement * Set groupType to the preset name if it exists, otherwise use the strategy name * remove finally block from * Refactor authentication logging in handleStrategyLogin to include strategy name --------- Signed-off-by: si458 <simonsmith5521@gmail.com> Co-authored-by: petervanv <58996467+petervanv@users.noreply.github.com> Co-authored-by: Ylian Saint-Hilaire <ysainthilaire@hotmail.com> Co-authored-by: Martin Mädler <martin.maedler@gmail.com> Co-authored-by: Fausto Gutierrez <28719096+faustogut@users.noreply.github.com> Co-authored-by: Simon Smith <simonsmith5521@gmail.com>
2024-03-03 19:03:27 -05:00
<a id="auth-oidc-azure" href="auth-oidc" style="display:none"><img src="images/login/azure32.png" srcset="images/login/azure64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in with Azure using OpenID Connect" /></a>
<a id="auth-oidc-google" href="auth-oidc" style="display:none"><img src="images/login/google32.png" srcset="images/login/google64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in with Google using OpenID Connect" /></a>
2021-01-09 05:40:46 -05:00
<a id="auth-jumpcloud" href="auth-jumpcloud" style="display:none"><img src="images/login/jumpcloud32.png" srcset="images/login/jumpcloud64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using JumpCloud" /></a>
<a id="auth-intel" href="auth-intel" style="display:none"><img src="images/login/intel32.png" srcset="images/login/intel64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Intel" /></a>
<a id="auth-saml" href="auth-saml" style="display:none"><img src="images/login/generic32.png" srcset="images/login/generic64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Single Sign-in" /></a>
</div>
</form>
</div>
<div id=createpanel style="display:none;position:relative">
<form id=createpanelform 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>
<tr id="nuUserRow">
<td id="nuUser" align=right width=100>Username:</td>
<td><input id=ausername type=text {{{autocomplete}}}="username" 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 {{{autocomplete}}}="email" inputmode="email" name=email onchange=validateCreate(2) maxlength=256 onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
</tr>
<tr>
<td id="nuPass1" align=right>Password:</td>
<td><input id=apassword1 type=password name=password1 {{{autocomplete}}}="new-password" 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}}}="new-password" 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 id=newAccountCaptchaImg title="CAPTCHA image">
<td></td>
<td colspan=2><img src="{{{newAccountCaptchaImage}}}" loading="lazy" /></td>
</tr>
<tr id=newAccountCaptcha title="Security check">
<td id="nuCaptcha" align=right>Security Check:</td>
<td><input id=anewaccountcaptcha type=text name=anewaccountcaptcha {{{autocomplete}}}=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(7,event) onkeyup=validateCreate(7,event) /></td>
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=createButton type="button" onclick="submitButtonClicked('createpanelform')" value="Create Account" disabled="disabled" /></div>
<div id=passWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=createformargs name="urlargs" type="hidden" value="" />
<input id=createformcaptcha name="captchaargs" type="hidden" value="{{{newAccountCaptcha}}}" />
</form>
</div>
<div id=resetpanel style="display:none">
<form id=resetpanelform method=post>
<input type=hidden name=action value=resetaccount />
<div id=message3></div>
<div>
<b>Account Reset</b>
</div>
<table style="width:100%;margin-top:4px;margin-bottom:4px">
<tr>
<td>
<input id=remail title="Email" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" {{{autocomplete}}}="username" placeholder="Email" type=text maxlength=256 name=email onchange=validateReset() onkeyup=validateReset(event) />
</td>
</tr>
<tr>
<td>
<input id=eresetButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" type="button" onclick="submitButtonClicked('resetpanelform')" value="Reset Account" disabled="disabled" />
</td>
</tr>
</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 id=tokenpanelform method=post {{{autocomplete}}}=off>
<input type=hidden name=action value=tokenlogin />
<input type=hidden name=hwstate value="{{{hwstate}}}" />
<div id=message4></div>
<table style="width:100%">
<tr>
<td>
<input id=tokenInput {{{autocomplete}}}="one-time-code" title="Token" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" placeholder="Token" type=text maxlength=50 name=token onchange=checkToken(event) onpaste=checkToken(event) onkeyup=checkToken(event) onkeydown=checkToken(event) /><br />
<input id=hwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
<td>
<div>
<input id=tokenOkButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" type="button" onclick="submitButtonClicked('tokenpanelform')" value="Log In" disabled="disabled" />
</div>
</td>
</tr>
<tr>
<td style="align-content:center;padding-top:10px">
<label id=tokenInputRememberLabel><input id=tokenInputRemember name=remembertoken type=checkbox /><span id=tokenInputRememberSpan></span></label>
</td>
</tr>
<tr id="2farow">
<td>
<hr />
<div>
2021-01-09 05:40:46 -05:00
<img id=securityKeyButton src="images/login/2fa-key-48.png" srcset="images/login/2fa-key-96.png 2x" title="Use Security Key" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSecurityKey(1)" />
<img id=smsKeyButton src="images/login/2fa-sms-48.png" srcset="images/login/2fa-sms-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSMSToken(1)" />
<img id=msgKeyButton src="images/login/2fa-messaging-48.png" srcset="images/login/2fa-messaging-96.png 2x" title="Messaging" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useMsgToken(1)" />
2021-01-09 05:40:46 -05:00
<img id=emailKeyButton src="images/login/2fa-mail-48.png" srcset="images/login/2fa-mail-96.png 2x" title="Email" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useEmailToken(1)" />
2021-04-14 02:23:09 -04:00
<img id=pushKeyButton src="images/login/2fa-push-48.png" srcset="images/login/2fa-push-96.png 2x" title="Device Authentication" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="usePushToken(1)" />
</div>
</td>
</tr>
</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 id=resettokenpanelform method=post>
<input type=hidden name=action value=resetaccount />
<div id=message5></div>
<table style="width:100%">
<tr>
<td>
<input id=resetTokenInput {{{autocomplete}}}="one-time-code" title="Token" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" placeholder="Token" type=text maxlength=50 name=token onchange=resetCheckToken(event) onpaste=resetCheckToken(event) onkeyup=resetCheckToken(event) onkeydown=resetCheckToken(event) /><br />
<input id=resetHwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
<td>
<div>
<input id=resetTokenOkButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" type="button" onclick="submitButtonClicked('resettokenpanelform')" value="Log In" disabled="disabled" />
</div>
</td>
</tr>
<tr id="2farow2">
<td colspan=2>
<hr />
<div>
2021-01-09 05:40:46 -05:00
<img id=securityKeyButton2 src="images/login/2fa-key-48.png" srcset="images/login/2fa-key-96.png 2x" title="Use Security Key" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSecurityKey(2)" />
<img id=smsKeyButton2 src="images/login/2fa-sms-48.png" srcset="images/login/2fa-sms-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSMSToken(2)" />
<img id=msgKeyButton2 src="images/login/2fa-msg-48.png" srcset="images/login/2fa-msg-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useMsgToken(2)" />
2021-01-09 05:40:46 -05:00
<img id=emailKeyButton2 src="images/login/2fa-mail-48.png" srcset="images/login/2fa-mail-96.png 2x" title="Email" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useEmailToken(2)" />
2021-04-14 02:23:09 -04:00
<img id=pushKeyButton2 src="images/login/2fa-push-48.png" srcset="images/login/2fa-push-96.png 2x" title="Device Authentication" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="usePushToken(2)" />
</div>
</td>
</tr>
</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 id=resetpasswordpanelform method=post>
<input type=hidden name=action value=resetpassword />
<div id=message6></div>
<div id="rpasswordPolicyCallout" style="display:none"></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 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="button" onclick="submitButtonClicked('resetpasswordpanelform')" value="Reset Password" disabled="disabled" /></div>
<div id=rpassWarning style="padding-top:6px"></div>
</td>
</tr>
</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>
<div id=checkemailpanel style="display:none;position:relative">
<form id=checkemailpanelform method=post>
<input type=hidden name=action value=checkemail />
<div id=message7></div>
<table id="checkCheckOperations" style="width:100%;display:none">
<tr>
<td>
<div id="unconfirmedEmail"></div><br />
</td>
</tr>
<tr>
<td>
<div>
<input id=changeEmailButton type=button value="Change Email Address" onclick="changeEmailAddress()" />
<input id=checkEmailButton2 type=button value="Resend Confirmation Email" onclick="resentEmailConfirmation()" />
<input id=checkEmailButton type="button" onclick="submitButtonClicked('checkemailpanelform')" style="display:none" />
<input id=checkEmailVal name=email type="hidden" value="" />
</div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=checkemailformargs name="urlargs" type="hidden" value="" />
</form>
</div>
2021-04-14 02:23:09 -04:00
<div id=waitpushpanel style="display:none">
<form id=waitpushpanelform method=post>
2021-04-14 16:28:51 -04:00
<input type=hidden name=action value=pushlogin />
<div id=message8></div>
<table style="width:100%">
<tr>
<td style="align-content:center;padding-top:10px">
<img id="waitpushpanelimage" src="images/login/push-150.png" srcset="images/login/push-300.png 2x" style="opacity:0.3" loading="lazy" width="265" height="150" />
</td>
</tr>
<tr>
<td style="align-content:center;padding-top:10px">
<label id=tokenInputRememberLabel2><input id=tokenInputRemember2 name=remembertoken type=checkbox /><span id=tokenInputRememberSpan2></span></label>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=pushtokenformargs name="urlargs" type="hidden" value="" />
<input id=pushtokenInput name="hwstate" type="hidden" value="" />
<input id=pushOkButton onclick="submitButtonClicked('waitpushpanelform')" style="display:none" />
2021-04-14 16:28:51 -04:00
</form>
2021-04-14 02:23:09 -04:00
</div>
</td>
</tr>
2020-09-03 19:17:14 -04:00
<tr id="welcomeTextRow"><td><div id="welcomeText" style="color:white;text-align:center;margin-left:20px;margin-right:20px"></div></td></tr>
<tr id="bottomLanguageSelectRow" style="display:none;text-align:center"><td id="bottomLanguageSelect"></td></tr>
<tr style="height:20px">
<td>
<div>
2020-09-03 19:17:14 -04:00
<div id="flink" style="float:right;margin-right:4px;color:#c8c8c8">{{{rootCertLink}}}&nbsp;<a href=terms>Terms &amp; Privacy</a></div>
<div id="flink" style="margin-left:4px;color:#c8c8c8">{{{footer}}}</div>
</div>
</td>
</tr>
</table>
<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="Cancel" style="" onclick="dialogclose(0)">
<input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)">
</div>
</div>
<script>
'use strict';
2022-06-08 13:47:23 -04:00
var random = '{{{randomlength}}}' // Random length string for BREACH mitigation
2021-05-10 15:19:53 -04:00
var welcomePictureFullScreen = (decodeURIComponent('{{{welcomePictureFullScreen}}}') === 'true');
2020-12-19 21:35:22 -05:00
var passlogin = '{{{passlogin}}}';
var passhint = '{{{passhint}}}';
var loginMode = '{{{loginmode}}}';
var newAccount = '{{{newAccount}}}';
var newAccountPass = parseInt('{{{newAccountPass}}}');
var newAccountCaptcha = '{{{newAccountCaptcha}}}';
var emailCheck = '{{{emailcheck}}}';
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 publicKeyCredentialRequestOptions = null;
var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
var otpmsg = (decodeURIComponent('{{{otpmsg}}}') === 'true');
2021-04-14 02:23:09 -04:00
var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(',');
var tokenTimeout = parseInt('{{{tokenTimeout}}}');
2021-04-14 16:28:51 -04:00
var websocket = null;
var formSubmitted = false;
var serverLangs = '{{{renderLanguages}}}'.split(',');
var loclist = { 'af': "Afrikaans", 'sq': "Albanian", 'ar': "Arabic (Standard)", 'ar-dz': "Arabic (Algeria)", 'ar-bh': "Arabic (Bahrain)", 'ar-eg': "Arabic (Egypt)", 'ar-iq': "Arabic (Iraq)", 'ar-jo': "Arabic (Jordan)", 'ar-kw': "Arabic (Kuwait)", 'ar-lb': "Arabic (Lebanon)", 'ar-ly': "Arabic (Libya)", 'ar-ma': "Arabic (Morocco)", 'ar-om': "Arabic (Oman)", 'ar-qa': "Arabic (Qatar)", 'ar-sa': "Arabic (Saudi Arabia)", 'ar-sy': "Arabic (Syria)", 'ar-tn': "Arabic (Tunisia)", 'ar-ae': "Arabic (U.A.E.)", 'ar-ye': "Arabic (Yemen)", 'an': "Aragonese", 'hy': "Armenian", 'as': "Assamese", 'ast': "Asturian", 'az': "Azerbaijani", 'eu': "Basque", 'bg': "Bulgarian", 'be': "Belarusian", 'bn': "Bengali", 'bs': "Bosnian", 'br': "Breton", 'my': "Burmese", 'ca': "Catalan", 'ch': "Chamorro", 'ce': "Chechen", 'zh': "Chinese", 'zh-hk': "Chinese (Hong Kong)", 'zh-cn': "Chinese (PRC)", 'zh-sg': "Chinese (Singapore)", 'zh-tw': "Chinese (Taiwan)", 'cv': "Chuvash", 'co': "Corsican", 'cr': "Cree", 'hr': "Croatian", 'cs': "Czech", 'da': "Danish", 'nl': "Dutch (Standard)", 'nl-be': "Dutch (Belgian)", 'en': "English", 'en-au': "English (Australia)", 'en-bz': "English (Belize)", 'en-ca': "English (Canada)", 'en-ie': "English (Ireland)", 'en-jm': "English (Jamaica)", 'en-nz': "English (New Zealand)", 'en-ph': "English (Philippines)", 'en-za': "English (South Africa)", 'en-tt': "English (Trinidad & Tobago)", 'en-gb': "English (United Kingdom)", 'en-us': "English (United States)", 'en-zw': "English (Zimbabwe)", 'eo': "Esperanto", 'et': "Estonian", 'fo': "Faeroese", 'fa': "Farsi (Persian)", 'fj': "Fijian", 'fi': "Finnish", 'fr': "French (Standard)", 'fr-be': "French (Belgium)", 'fr-ca': "French (Canada)", 'fr-fr': "French (France)", 'fr-lu': "French (Luxembourg)", 'fr-mc': "French (Monaco)", 'fr-ch': "French (Switzerland)", 'fy': "Frisian", 'fur': "Friulian", 'gd': "Gaelic (Scots)", 'gd-ie': "Gaelic (Irish)", 'gl': "Galacian", 'ka': "Georgian", 'de': "German (Standard)", 'de-at': "German (Austria)", 'de-de': "German (Germany)", 'de-li': "German (Liechtenstein)", 'de-lu': "German (Luxembourg)", 'de-ch': "German (Switzerland)", 'el': "Greek", 'gu': "Gujurati", 'ht': "Haitian", 'he': "Hebrew", 'hi': "Hindi", 'hu': "Hungarian", 'is': "Icelandic", 'id': "Indonesian", 'iu': "Inuktitut", 'ga': "Irish", 'it': "Italian (Standard)", 'it-ch': "Italian (Switzerland)", 'ja': "Japanese", 'kn': "Kannada", 'ks': "Kashmiri", 'kk': "Kazakh", 'km': "Khmer", 'ky': "Kirghiz", 'tlh': "Klingon", 'ko': "Korean", 'ko-kp': "Korean (North Korea)", 'ko-kr': "Korean (South Korea)", 'la': "Latin", 'lv': "Latvian", 'lt': "Lithuanian", 'lb': "Luxembourgish", 'mk': "FYRO Macedonian", 'ms': "Malay", 'ml': "Malayalam", 'mt': "Maltese", 'mi': "Maori", 'mr': "Marathi", 'mo': "Moldavian", 'nv': "Navajo", 'ng': "Ndonga", 'ne': "Nepali", 'no': "Norwegian", 'nb': "Norwegian (Bokmal)", 'nn': "Norwegian (Nynorsk)", 'oc': "Occitan", 'or': "Oriya", 'om': "Oromo", 'fa-ir': "Persian/Iran", 'pl': "Polish", 'pt': "Portuguese", 'pt-br': "Portuguese (Brazil)", 'pa': "Punjabi", 'pa-in': "Punjabi (India)", 'pa-pk': "Punjabi (Pakistan)", 'qu': "Quechua", 'rm': "Rhaeto-Romanic", 'ro': "Romanian", 'ro-mo': "Romanian (Moldavia)", 'ru': "Russian", 'ru-mo': "Russian (Moldavia)", 'sz': "Sami (Lappish)", 'sg': "Sango", 'sa': "Sanskrit", 'sc': "Sardinian", 'sd': "Sindhi", 'si': "Singhalese", 'sr': "Serbian", 'sk': "Slovak", 'sl': "Slovenian", 'so': "Somani", 'sb': "Sorbian", 'es': "Spanish", 'es-ar': "Spanish (Argentina)", 'es-bo': "Spanish (Bolivia)", 'es-cl': "Spanish (Chile)", 'es-co': "Spanish (Colombia)", 'es-cr': "Spanish (Costa Rica)", 'es-do': "Spanish (Dominican Republic)", 'es-ec': "Spanish (Ecuador)", 'es-sv': "Spanish (El Salvador)", 'es-gt': "Spanish (Guatemala)", 'es-hn': "Spanish (Honduras)", 'es-mx': "Spanish (Mexico)", 'es-ni': "Spanish (Nicaragua)", 'es-pa': "Spanish (Panama)", 'es-py': "Spanish (Paraguay)", 'es-pe': "Spanish (Peru)", 'es-pr': "Spanish (Puerto Rico)", 'es-es': "Spanish (Spain)", 'es-uy': "Spanish (Uruguay)", 'es-ve': "Spanish (Venezuela)", 'sx': "Sutu", 'sw': "Swahili", 'sv'
var loclistex = { 'zh-chs': "Chinese (Simplified)", 'zh-cht': "Chinese (Traditional)" };
var showLanguageSelect = '{{{showLanguageSelect}}}';
function startup() {
if (decodeURIComponent('{{{loginpicture}}}') == 'true') { Q('loginPicture').src = "loginlogo.png"; }
2020-09-03 19:17:14 -04:00
QV('welcomeTextRow', welcomeText != '');
QH('welcomeText', welcomeText);
2021-05-10 15:19:53 -04:00
// Change the background image to full screen
if (welcomePictureFullScreen) {
QS('backgroundImage').height = '100%';
QS('backgroundImage').opacity = '1';
QS('backgroundImage')['background-position'] = 'center center';
QS('backgroundImage')['background-size'] = 'cover';
2021-05-10 15:19:53 -04:00
}
// Display the right server message
var i;
var messageid = parseInt('{{{messageid}}}');
var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification...", "Message sent."];
var failmessages = ["Unable to create account.", "Account limit reached.", "Existing account with this email address.", "Invalid account creation token.", "Username already exists.", "Password rejected, use a different one.", "Invalid email.", "Account not found.", "Invalid token, try again.", "Unable to sent email.", "Account locked.", "Access denied.", "Login failed, check username and password.", "Password change requested.", "IP address blocked, try again later.", "Server under maintenance.", "Unable to send device notification.", "Invalid security check."];
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 />'); }
2021-04-14 02:23:09 -04:00
for (i = 1; i < 9; i++) { QH('message' + i, msg); }
}
}
// Display flash error Messages
var flashErrors = JSON.parse('{{{flashErrors}}}');
if (flashErrors && (flashErrors.length > 0)) {
var msg = '';
for (i = 0; i < flashErrors.length; i++) {
if (flashErrors[i]) {
msg += '<span class="msg error"><b style=color:#8C001A>' + flashErrors[i] + '<b></span><br /><br />';
}
}
QH('message1', msg);
QV('message1', true);
}
// Fix links if a loginKey if used
var urlargs = parseUriArgs();
//if (urlargs.key) { Q('termsLinkFooter').href += '?key=' + urlargs.key; }
// Show Language Select Box if needed
if (showLanguageSelect === 'top' || showLanguageSelect === 'bottom') {
var x = '<select id=d2langselect style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px" onChange="changeLanguage()">';
x += '<option value="*">Use Browser Language</option>';
for (var i in serverLangs) {
var lang = serverLangs[i];
x += '<option value="' + lang + '"' + ((urlargs.lang == lang)?' selected':'') + '>' + lang + ' - ' + (loclist[lang]?loclist[lang]:loclistex[lang]) + '</option>';
}
QH(showLanguageSelect+'LanguageSelect', x);
if (showLanguageSelect === 'top') QS(showLanguageSelect+'LanguageSelect')['padding-top'] = '5px';
QV(showLanguageSelect+'LanguageSelectRow', true);
}
2020-12-19 21:35:22 -05:00
// Show user/pass login
passlogin = (passlogin == 'true');
if (urlargs.passlogin === 1) { passlogin = true; }
if (urlargs.passlogin === 0) { passlogin = false; }
QV('loginuserpassdiv', passlogin)
QV('loginuserpasshr', passlogin)
// Setup two factor cookie time
if (twoFactorCookieDays > 0) {
QV('tokenInputRememberLabel', true);
QH('tokenInputRememberSpan', format("Remember this device for {0} days.", twoFactorCookieDays));
2021-04-14 02:23:09 -04:00
QV('tokenInputRememberLabel2', true);
QH('tokenInputRememberSpan2', format("Remember this device for {0} days.", twoFactorCookieDays));
} else {
QV('tokenInputRememberLabel', false);
2021-04-14 02:23:09 -04:00
QV('tokenInputRememberLabel2', false);
}
// If URL arguments are provided, add them to form posts
if (window.location.href.indexOf('?') > 0) {
var xurlargs = window.location.href.substring(window.location.href.indexOf('?'));
Q('loginformargs').value = xurlargs;
Q('createformargs').value = xurlargs;
Q('resetformargs').value = xurlargs;
Q('tokenformargs').value = xurlargs;
2021-04-14 16:28:51 -04:00
Q('pushtokenformargs').value = xurlargs;
Q('resettokenformargs').value = xurlargs;
Q('resetpasswordformargs').value = xurlargs;
Q('checkemailformargs').value = xurlargs;
}
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
Q('username').placeholder = "Email";
QH('resetAccountSpan', "Forgot password?");
QV('nuUserRow', false);
}
QV('createPanelHint', passRequirements.hint === true);
QV('resetpasswordpanelHint', passRequirements.hint === true);
// Setup authentication strategies
if (authStrategies != '') {
QV('authStrategies', true);
if (authStrategies.indexOf('twitter') >= 0) { QV('auth-twitter', true); }
if (authStrategies.indexOf('google') >= 0) { QV('auth-google', true); }
if (authStrategies.indexOf('github') >= 0) { QV('auth-github', true); }
if (authStrategies.indexOf('azure') >= 0) { QV('auth-azure', true); }
2022-04-06 12:39:13 -04:00
if (authStrategies.indexOf('oidc') >= 0) { QV('auth-oidc', true); }
Migrate to openid client (#5856) * Create forksync.yml * update oidc to use openid-client * update oidc module requirements * working oidc+ includes all oauth2 clients automatically migrated. azure will need some kind of fix for the uid * update openid-client install checks * created overarching schema for OIDC * bug fixs for azure login * update schema prepare schema for unified oidc module * update 'oidc' to strategy variable * working azure+ groups groups from azure are in, you can use memberOf or transitiveMemberOf in config (Graphs API) * clean up old config import + working google oidc previous config map was recursive nonsense, changed to multiple IFs * added convertStrArray * de-expanded scope put all other auth strategies back to normal and fixed oidc strategy * swap back to using authlog debugger * Update meshcentral-config-schema.json * working google oidc + groups * working azure+groups (again) * init oidc docs very incomplete but basic config is present * add oidc * more work on docs * add scope and claim options plus fixed a few bugs and faults in my logic used logs correctly * further cleanup debug * more debug cleanup * continue documentation push fixed minor debug bugs also * more work on docs missing links, need to get azure preset docs, probably more. * done with docs its good enough for now * minor fix + presets get correct icon * fix google oidc not visible at login * fix bug with emailVerified property * fix logout bug + debug cleanup * fix strategy logout bug +cleanup * fixed preset login icon * fix alert + fix schema * terminate lines * Dutch language update 1.0.85 line up polish translation * Fixed guest web relay session revocation (#4667) * Updated French translation. * Add hook to allow adding custom api endpoints to Express routing * Updated German translation. * Update meshcentral-config-schema.json (change formatting) This way it is easier to edit and maintain * Fixed schema. * fix meshcentral-config-schema.json * add language selector to login (#5648) * add language selector to login * add showLanguageSelect to pick top or bottom boxe * remove additionalProperties: false in schema to allow comments #5697 Signed-off-by: si458 <simonsmith5521@gmail.com> * fix notes in docs * Fix web relay session handling and redirection due to bad merge * Added option to check HTTP origin. * add links and fix typo * move groups after strategy * Update version split in docs * Fix preset issuer URL in OIDC strategy * Update clientid and clientsecret to client_id and client_secret * Update meshcentral-config-schema.json and fix bad rebase * Update meshcentral-config-schema.json * fix bad rebase * fix bad rebase * Add 'connect-flash' to passport dependencies * Remove unnecessary passport dependencies - fix bad rebase * Fix auth strategy bug and remove console.log statement * Set groupType to the preset name if it exists, otherwise use the strategy name * remove finally block from * Refactor authentication logging in handleStrategyLogin to include strategy name --------- Signed-off-by: si458 <simonsmith5521@gmail.com> Co-authored-by: petervanv <58996467+petervanv@users.noreply.github.com> Co-authored-by: Ylian Saint-Hilaire <ysainthilaire@hotmail.com> Co-authored-by: Martin Mädler <martin.maedler@gmail.com> Co-authored-by: Fausto Gutierrez <28719096+faustogut@users.noreply.github.com> Co-authored-by: Simon Smith <simonsmith5521@gmail.com>
2024-03-03 19:03:27 -05:00
if (authStrategies.indexOf('oidc-azure') >= 0) { QV('auth-oidc-azure', true); }
if (authStrategies.indexOf('oidc-google') >= 0) { QV('auth-oidc-google', true); }
if (authStrategies.indexOf('jumpcloud') >= 0) { QV('auth-jumpcloud', true); }
if (authStrategies.indexOf('intel') >= 0) { QV('auth-intel', true); }
if (authStrategies.indexOf('saml') >= 0) { QV('auth-saml', true); }
}
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('newAccountCaptcha', (newAccountCaptcha != ''));
QV('newAccountCaptchaImg', (newAccountCaptcha != ''));
QV('resetAccountDiv', (emailCheck == 'true'));
QV('hrAccountDiv', (emailCheck == 'true') || (newAccountPass == 1));
if (loginMode == '4') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
2022-10-25 17:17:54 -04:00
var emailkey = otpemail && (messageid != 2) && (messageid != 4) && (messageid != 6);
var smskey = otpsms && (messageid != 2) && (messageid != 4) && (messageid != 6);
var msgkey = otpmsg && (messageid != 2) && (messageid != 4) && (messageid != 6);
var pushkey = otppush && (messageid != 2) && (messageid != 4) && (messageid != 6);
QV('securityKeyButton', twofakey);
QV('emailKeyButton', emailkey);
QV('smsKeyButton', smskey);
QV('msgKeyButton', msgkey);
2021-04-14 02:23:09 -04:00
QV('pushKeyButton', pushkey);
QV('2farow', twofakey || emailkey || smskey || msgkey || pushkey);
// If hardware key is an option, trigger it now
if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
}
if (loginMode == '5') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
2022-10-23 11:49:54 -04:00
var emailkey = otpemail && (messageid != 2) && (messageid != 4) && (messageid != 6);
var smskey = otpsms && (messageid != 2) && (messageid != 4) && (messageid != 6);
var msgkey = otpmsg && (messageid != 2) && (messageid != 4) && (messageid != 6);
var pushkey = otppush && (messageid != 2) && (messageid != 4) && (messageid != 6);
QV('securityKeyButton2', twofakey);
QV('emailKeyButton2', emailkey);
QV('smsKeyButton2', smskey);
QV('msgKeyButton2', msgkey);
2021-04-14 02:23:09 -04:00
QV('pushKeyButton', pushkey);
QV('2farow2', twofakey || emailkey || smskey || msgkey || pushkey);
// If hardware key is an option, trigger it now
if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); }
}
2021-04-14 16:28:51 -04:00
if (loginMode == '8') {
// Perform websocket connection to server to wait for device authentication
websocket = new WebSocket(passhint);
websocket.onopen = function (e) { QS('waitpushpanelimage')['opacity'] = '1'; }
websocket.onmessage = function (e) {
if (typeof e.data != 'string') { this.close(); }
var r = null;
try { r = JSON.parse(e.data); } catch (ex) { }
if (r.sent === true) {
// Request was sent
QH('message8', '<span class="msg success"><b>' + format("Request sent, {0}.", r.code) + '</b></span><br /><br />');
} else if (r.sent === false) {
// Request failed to send
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Failed to send request." + '<b></span><br /><br />');
2021-04-14 17:54:07 -04:00
QV('tokenInputRememberLabel2', false);
2021-04-14 16:28:51 -04:00
this.close();
} else if (r.approved === true) {
// Request approved
this.close();
QV('tokenInputRememberLabel2', false);
QH('message8', '<span class="msg success"><b>' + "Request Accepted." + '</b></span><br /><br />');
Q('pushtokenInput').value = r.token;
Q('pushOkButton').click();
} else {
// Request rejected
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Access Rejected." + '<b></span><br /><br />');
2021-04-14 17:54:07 -04:00
QV('tokenInputRememberLabel2', false);
2021-04-14 16:28:51 -04:00
this.close();
}
2021-04-14 16:28:51 -04:00
}
websocket.onclose = function (e) { QS('waitpushpanelimage')['opacity'] = '0.3'; }
websocket.onerror = function (e) {
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Connection Error" + '<b></span><br /><br />');
QS('waitpushpanelimage')['opacity'] = '0.5';
}
}
}
function changeLanguage() {
var lang = Q('d2langselect').value;
var urlParams = new URLSearchParams(window.location.search);
if(lang=='*'){ urlParams.delete('lang') } else { urlParams.set('lang', lang); }
var newURL = window.location.pathname + '?' + urlParams.toString();
window.location.href = newURL;
}
// Use a hardware security key
function useSecurityKey(panelAction) {
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', 'internal'] }
);
}
// 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))),
};
if (panelAction == 1) {
Q('hwtokenInput').value = JSON.stringify(assertion);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = JSON.stringify(assertion);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
},
function (error) { console.log('credentials-get error', error); }
);
}
}
function useEmailToken(panelAction) {
if (otpemail != true) return;
setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registered email address?", panelAction);
}
function useEmailKeyEx(b, panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**email**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**email**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
function useSMSToken(panelAction) {
if (otpsms != true) return;
setDialogMode(1, "Secure Login", 3, useSMSTokenEx, "Send token to registered phone number?", panelAction);
}
function useSMSTokenEx(b, panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**sms**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**sms**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
function useMsgToken(panelAction) {
if (otpmsg != true) return;
setDialogMode(1, "Secure Login", 3, useMsgTokenEx, "Send token to messaging application?", panelAction);
}
function useMsgTokenEx(b, panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**msg**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**msg**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
2021-04-14 02:23:09 -04:00
function usePushToken(panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**push**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**push**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
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);
QV('message7', 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);
QV('checkemailpanel', x == 7);
2021-04-14 02:23:09 -04:00
QV('waitpushpanel', x == 8);
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(); }
if (x == 7) {
QH('unconfirmedEmail', passhint);
QV('checkCheckOperations', Q('unconfirmedEmail').innerHTML != '');
QH('checkEmailVal', passhint);
}
}
function submitButtonClicked(v) {
if (!formSubmitted) { formSubmitted = true; document.getElementById(v).submit(); }
}
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 newCaptchaOk = (newAccountCaptcha == '') || (Q('anewaccountcaptcha').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';
QS('nuCaptcha').color = newCaptchaOk ? '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>' + "Strong Password" + '</b><span>'); }
else if (passStrength >= 60) { QH('passWarning', '<span style=color:blue><b>' + "Good Password" + '</b><span>'); }
else { QH('passWarning', '<span style=color:red><b>' + "Weak Password" + '</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) { if (newAccountCaptcha != '') { Q('anewaccountcaptcha').focus(); } else { box = 7; } }
if (box == 7) { 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>' + "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);
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("Maximum length of {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(e) {
var t1 = Q('tokenInput').value;
var t2 = t1.split(' ').join('');
if (t1 != t2) { Q('tokenInput').value = t2; }
var enabled = (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44);
QE('tokenOkButton', enabled);
if (enabled && (e != null) && (e.keyCode == 13)) { submitButtonClicked('tokenpanelform'); }
}
function resetCheckToken(e) {
var t1 = Q('resetTokenInput').value;
var t2 = t1.split(' ').join('');
if (t1 != t2) { Q('resetTokenInput').value = t2; }
var enabled = (Q('resetTokenInput').value.length == 6) || (Q('resetTokenInput').value.length == 8) || (Q('resetTokenInput').value.length == 44);
QE('resetTokenOkButton', enabled);
if (enabled && (e != null) && (e.keyCode == 13)) { submitButtonClicked('resettokenpanelform'); }
}
function changeEmailAddress() {
var email = Q('unconfirmedEmail').innerHTML;
var x = addHtmlValue("Email", '<input id=dp1email style=width:230px maxlength=256 value="' + email + '" {{{autocomplete}}}=off onchange=validateEmailAddress() onkeyup=validateEmailAddress() />');
setDialogMode(1, "Email Confirmation", 3, changeEmailAddressEx, x);
validateEmailAddress();
}
function validateEmailAddress() {
QE('idx_dlgOkButton', (validateEmail(Q('dp1email').value) == true));
}
function changeEmailAddressEx() {
Q('checkEmailVal').value = Q('dp1email').value;
QH('unconfirmedEmail', Q('dp1email').value);
Q('checkEmailButton').click();
}
function resentEmailConfirmation() {
Q('checkEmailVal').value = Q('unconfirmedEmail').innerHTML;
Q('checkEmailButton').click();
}
//
// 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 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); }
function addHtmlValue(t, v) { return '<table><td style=width:120px;text-align:left>' + t + '<td><b>' + v + '</b></table>'; }
</script>
</body>
</html>