Newer
Older
<!DOCTYPE html>
<html>
<head>
{% for src in js %}
<script type="text/javascript" src="{{ src }}"></script>
{% endfor %}
{% for src in css %}
<link type="text/css" rel="stylesheet" href="{{ src }}">
{% endfor %}
<script type="text/javascript" src="{{ url_for( 'files.send_app_files', subpath = 'webauthn.js' ) }}"></script>
<script type="text/javascript" src="{{ url_for( 'files.send_app_files', subpath = 'functions.js' ) }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url_for( 'files.send_app_files', subpath = 'app.css' ) }}">
<script type="text/javascript">
baseurl = "{{ baseurl }}";
homeurl = "{{ url_for( 'base.home' ) }}";
begin_activate_url = "{{ url_for( 'login.webauthn_begin_activate' ) }}";
verify_url = "{{ url_for( 'login.webauthn_verify' ) }}";
begin_assertion_url = "{{ url_for( 'login.webauthn_begin_assertion' ) }}";
verify_assertion_url = "{{ url_for( 'login.webauthn_verify_assertion' ) }}";
<h1 style="margin-bottom: 0px">ICNML</h1>
<h4 style="margin-top: 0px">International Close Non-Matches Library</h4>
<div class="ui-widget-header ui-corner-top icnml_box_top">Please enter your login information</div>
<div id="icnml_homepage_form" class="ui-widget-content ui-corner-bottom icnml_box_content">
<div id="icnml_box_fields" class="icnml_box_fields">
<div style="text-align: right;">
<label for="username">Username</label>
<div>
<input id="username" name="username" type="text" />
</div>
<div style="text-align: right;">
<label for="password">Password</label>
</div>
<div>
<input id="password" name="password" type="password" />
<div id="icnml_login_error" class="icnml_box_error"></div>
<div id="icnml_login_warning" class="icnml_box_warning"></div>
<div id="icnml_login_warning2" class="icnml_box_warning"></div>
<div class="icnml_button">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="login_button" role="button" aria-disabled="false">
<span class="ui-button-text">Login</span>
</a>
</div>
<div class="icnml_shybox" id="password_totp_reset">
<a href="{{ url_for( 'login.password_reset' ) }}">Password reset</a>
</div>
<div class="icnml_shybox" id="new_account">
<a href="{{ url_for( 'newuser.new_user' ) }}">Request an account</a>
<div class="icnml_shybox" style="margin-top: 10px;">
<span id="icnml_version"></span>
</div>
<div class="icnml_shybox">
<a id="icnml_tree_files" href="#"></a>
</div>
<div class="icnml_shybox">
<a id="icnml_documents" href="https://esc-md-git.unil.ch/ICNML/documents" style="text-decoration: underline">See the documents related to the ICNML project</a>
<script type="text/javascript">
envtype = "{{ envtype }}";
if( envtype !== "" )
{
document.write( '<div class="ribbon-wrapper"><div class="ribbon">' + envtype + '</div></div>' );
}
</script>
var login_action_password = async function()
{
$( "#icnml_login_error" ).html( "" );
$( "#login_button > span" ).text( "Please wait..." );
var username = $( "#username" ).val();
var password = $( "#password" ).val();
var password_local = $( "#password" ).val();
password = await generateKey( password, "icnml_" + username, 20000 );
password = password.substring( 0, 128 );
password = "pbkdf2$sha512$icnml_" + username + "$20000$" + password;
password_local = await generateKey( password_local, "icnml_" + username + "_localpassword", 50000 );
password_local = password_local.substring( 0, 128 );
var e = encrypt( password_local, "{{ session_security_key }}" );
sessionStorage.setItem( "session_key", e );
url: "{{ url_for( 'login.do_login' ) }}",
dataType: "json",
method: "POST",
data: {
username: username,
password: password
},
success: function( data )
{
if( ! data.error )
{
if( data.logged )
{
location.href = "{{ url_for( 'base.home' ) }}";
} else if( data.next_step === "totp" ) {
build_totp_form();
} else if( data.next_step === "securitykey" ) {
build_securitykey_form();
} else {
if( typeof data.message !== "undefined" )
var message = data.message;
else
var message = "Invalid username/password";
$( "#icnml_login_error" ).text( message );
}
$( "#login_button > span" ).text( "Login" );
}
},
error: function( data )
{
$( "#icnml_login_error" ).text( "Network error" );
}
} );
}
var login_action_totp = function()
{
$( "#icnml_login_error" ).html( "" );
$( "#login_button > span" ).text( "Please wait..." );
var totp = $( "#totp" ).val();
totp = totp.replace( /\D/g, '' );
url: "{{ url_for( 'login.do_login' ) }}",
dataType: "json",
method: "POST",
data: {
},
success: function( data )
{
if( ! data.error )
{
if( data.logged )
{
location.href = "{{ url_for( 'base.home' ) }}";
} else if( data.next_step === "securitykey" ) {
build_securitykey_form();
} else {
if( typeof data.message !== "undefined" )
if( typeof data.time !== "undefined" )
{
$( "#login_button > span" )
.text( "Login" );
var server_time = moment.utc( data.time * 1000 )
.local()
.format( "MMMM Do YYYY, HH:mm:ss" );
$( "#icnml_login_warning" )
.text( "Current server time: " + server_time );
if( typeof data.time_diff == "number" )
{
var time_diff = data.time_diff;
time_diff = Math.abs( time_diff );
time_diff_h = Math.floor( time_diff / 3600 );
time_diff_m = Math.floor( ( time_diff - time_diff_h * 3600 ) / 60 );
var time_diff_msg = "Your phone time is probably off by "
if( time_diff_h > 0 )
time_diff_msg += time_diff_h + " hour(s) and "
time_diff_msg += time_diff_m + " minute(s).";
$( "#icnml_login_warning" )
.css( "margin-bottom", "0px" );
$( "#icnml_login_warning2" )
$( "#icnml_login_error" ).text( "Network error" );
$( "#login_button > span" ).text( "Login" );
}
} );
}
var build_totp_form = function()
{
$( "#icnml_login_error" ).html( "" );
$( "#icnml_box_fields" ).html( "" );
$( "#new_account" ).remove();
$( "#icnml_box_fields" )
.append(
$( "<div />" )
.text( "TOTP" )
.css( "text-align", "right" )
)
.append(
$( "<div />" ).append(
$( "<input />" )
.attr( "id", "totp" )
.attr( "name", "totp" )
.on( "keyup", function( event )
{
if( event.keyCode == 13 )
{
event.preventDefault();
login_action_totp();
}
} )
)
);
$( "#password_totp_reset" )
.append(
$( "<a />" )
.attr( "href", "{{ url_for( 'login.totp_reset' ) }}" )
$( "#login_button" ).on( "click", function( event )
{
event.preventDefault();
login_action_totp();
} );
$( "#totp" ).focus();
}
var build_securitykey_form = function()
{
$( "#icnml_login_error" ).html( "" );
$( "#icnml_box_fields" ).html( "" );
$( "#icnml_box_fields" )
.removeClass( "icnml_box_fields" )
.addClass( "icnml_auto" );
$( "#login_button" ).remove();
$( "#new_account" ).remove();
$( "#icnml_homepage_form" )
.prepend(
$( "<div />" )
.css( "margin-bottom", "10px" )
.text( "Logging with your security key..." )
);
login_key();
}
/* Events binding */
$( "#username" ).on( "keyup", function( event )
{
if( event.keyCode == 13 )
{
event.preventDefault();
login_action_password();
}
} );
$( "#password" ).on( "keyup", function( event )
{
if( event.keyCode == 13 )
{
event.preventDefault();
login_action_password();
}
} );
$( "#login_button" ).on( "click", login_action_password );
$( document ).ready( function()
{
if( !window.crypto || !window.crypto.subtle || !window.TextEncoder || !window.TextDecoder)
{
.text( "Your browser does not support client-side cryptography. Please use compatible browser (Firefox, Chrome, Opera, Safari, ...) to protect your password before sending it to the ICNML server." );
$( "#username" ).prop( "disabled", true );
$( "#password" ).prop( "disabled", true );
$( "#login_button" ).remove();
setTimeout( function()
{
location.reload();
}, {{ session_timeout }} * 1000 );
/* Get the version of ICNML */
$.ajax( {
url: "{{ url_for( 'adm.version' ) }}",
dataType: "json",
method: "GET",
success: function( data ){
$( "#icnml_version" )
.text( "ICNML version: " + data.version );
$( "#icnml_tree_files" )
.attr( "href", data.treeurl )
.attr( "target", "_blank" )
.css( "text-decoration", "underline" )

Marco De Donno
committed
.text( "See and download the source code here" );