(function (exports) {
var DISABLE_RELOGIN = false;
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var update_form = function () {
if ($('#send_raw_disabled').val() == 'True') {
console.log('server said raw is not available; disabling send raw');
$('#send_raw').removeAttr('checked');
$('#send_raw').attr("disabled", true);
$('#send_raw_note').html("(Not available based on policy)");
} else if ($('#send_raw_disabled_if_missing_key').val() == 'True') {
// Look for key in the hash (by transforming #key=XYZ to ?key=XYZ)
var key = getParameterByName('key', window.location.hash.replace('#','?'));
if (!auth_allowed && key && key == '1') {
console.log('no auth so ignore key=1');
key = undefined;
}
if (key) {
$('#key').val(key);
// If necessary, update view-details-link used to view suppressed results
var details_link = $('#view-details-link');
if (details_link) {
details_link.attr('href', details_link.attr('href') + '#key=' + key);
}
var details_link2 = $('#view-details-link-suppressed');
if (details_link2) {
details_link2.attr('href', details_link2.attr('href') + '#key=' + key);
}
//if (key == '1') {
// $('#send_raw_note').html("(Requires authentication by tenant admin)");
//}
} else {
console.log('did not find key in hash; disabling send raw');
$('#send_raw').removeAttr('checked');
$('#send_raw').attr("disabled", true);
$('#send_raw_note').html("(Missing key in URL)");
}
} else {
console.log('server said key was not needed for send raw');
}
// If required, check box and hide container.
if ($('#send_raw_required').val() == 'True') {
$('#send_raw').attr('checked', true);
$('#send-raw-container').hide();
}
};
var labels = ['safe', 'spam', 'phish'];
var resetSpamPhishControls = function (enable, label) {
if (enable) {
$('#spam-phish-action-controls').show();
} else {
$('#spam-phish-action-controls').hide();
}
if (enable) {
$('#remove_message').prop("checked", true); // auto-check remove message if it's available
if ($('#remove_message').attr("disabled")) {
$('#remove_message').removeAttr("disabled");
}
if ($('#block_address').attr("disabled")) {
$('#block_address').removeAttr("disabled");
}
if ($('#block_domain').attr("disabled")) {
$('#block_domain').removeAttr("disabled");
}
if ($('#block_registered_domain').attr("disabled")) {
$('#block_registered_domain').removeAttr("disabled");
}
if ($('#block_address_mail_from').attr("disabled")) {
$('#block_address_mail_from').removeAttr("disabled");
}
if ($('#block_domain_mail_from').attr("disabled")) {
$('#block_domain_mail_from').removeAttr("disabled");
}
if ($('#block_registered_domain_mail_from').attr("disabled")) {
$('#block_registered_domain_mail_from').removeAttr("disabled");
}
if ($('#graymail_address_spam').attr("disabled")) {
$('#graymail_address_spam').removeAttr("disabled");
}
if (label == 'spam') {
$('#graymail_address_row_spam').show();
} else {
$('#graymail_address_row_spam').hide();
}
} else {
$('#remove_message').attr("disabled", true).prop('checked', false);
$('#block_address').attr("disabled", true).prop('checked', false);
$('#block_domain').attr("disabled", true).prop('checked', false);
$('#block_registered_domain').attr("disabled", true).prop('checked', false);
$('#block_address_mail_from').attr("disabled", true).prop('checked', false);
$('#block_domain_mail_from').attr("disabled", true).prop('checked', false);
$('#block_registered_domain_mail_from').attr("disabled", true).prop('checked', false);
$('#graymail_address_spam').attr("disabled", true).prop('checked', false);
$('#graymail_address_row_spam').hide();
}
};
// Note: For some messages, none of these elements exist, so the safe controls are completely hidden.
var resetSafeControls = function (enable) {
if (enable) {
$('#safe-action-controls').show();
} else {
$('#safe-action-controls').hide();
}
if (enable) {
$('.allow-action').removeAttr("disabled");
$('#graymail_address_safe').removeAttr("disabled");
} else {
$('.allow-action').attr("disabled", true).prop('checked', false);
$('#graymail_address_safe').attr("disabled", true).prop('checked', false);
}
};
var classify = function (label, force) {
var submit = $('#submit-button');
var current = $('#label').val();
// Normally, we'll clear a label if it's selected a second time (so the buttons toggle);
// but if the force param is true, leave it alone and just update everything again,
// since the first time we were not logged in and may have more to show now.
if (current === label && !force) {
label = "";
}
//console.log('running classify (force = ' + force + ')');
for (var i = 0; i < labels.length; i++) {
var el = $('#'+labels[i]);
// If we're resetting or this is the new selection, clear the not-selected state.
// Note: The not-selected classes were previously setting/unsetting the disabled attribute,
// but that makes it hard for a user to change their mind and just choose a different label without first
// (unintuitively) clicking the current label to unselect it.
if (label === "" || labels[i] === label) {
if (el.hasClass("not-selected")) {
el.removeClass("not-selected");
}
} else {
// If there is a label selected, and the current element is not that label,
// mark it as not-selected so it renders grayed out. We're still leaving it clickable,
// though, so a user can quickly change their label.
el.addClass("not-selected");
}
}
$('#label').val(label);
// If none selected, or we're not using auth or actually authenticated, hide and disable actions
if (label == '' || !auth_allowed || !keycloak || !keycloak.profile) {
// Hide, disable, and uncheck everything safe/spam/phish
resetSafeControls(false);
resetSpamPhishControls(false);
} else if (label == 'safe') {
resetSpamPhishControls(false);
resetSafeControls(true);
} else if (label == 'spam' || label == 'phish') {
resetSpamPhishControls(true, label);
resetSafeControls(false);
}
// If there's a selected label, enable the submit button; otherwise disable.
if (label) {
if (submit.attr("disabled")) {
submit.removeAttr("disabled");
}
$('#alt-submit-button').css({'z-index': -200});
if (show_selected_label_tip && label_tips[label]) {
$('#selected-label-tip-alert').html(label_tips[label]);
$('#selected-label-tip-alert').removeClass('alert-success');
$('#selected-label-tip-alert').removeClass('alert-warning');
$('#selected-label-tip-alert').removeClass('alert-danger');
$('#selected-label-tip-alert').addClass('alert-' + (label == 'safe' ? 'success' : (label == 'spam' ? 'warning' : 'danger')));
$('#selected-label-tip-wrapper').show();
}
} else {
submit.attr("disabled", true);
$('#alt-submit-button').css({'z-index': 200});
if (show_selected_label_tip) {
$('#selected-label-tip-wrapper').hide();
}
}
console.log('label is now saved as ' + label);
};
var get_classify_fn = function (label) {
return function () {
return classify(label);
};
};
function showGenericAlert(text, button_text, options) {
if (!options) {
options = {};
}
if (options.title) {
$('#generic-alert-dialog-title').html(options.title);
} else {
$('#generic-alert-dialog-title').html("Alert");
}
if (options.large) {
$('#generic-alert-dialog-modal .modal-dialog').addClass('modal-lg');
$('#generic-alert-dialog-modal .modal-dialog').addClass('modal-tall');
} else {
$('#generic-alert-dialog-modal .modal-dialog').removeClass('modal-lg');
$('#generic-alert-dialog-modal .modal-dialog').removeClass('modal-tall');
}
$('#generic-alert-dialog-text').html(text);
$('#generic-alert-button').html(button_text || default_ok_button_text);
if (options.hide_button) {
$('#generic-alert-button').hide(); // no button will be visible
} else {
$('#generic-alert-button').show();
}
if (options.hide_footer) {
$('#generic-alert-footer').hide(); // no footer or button will be visible; user will use 'x' to close
} else {
$('#generic-alert-footer').show();
}
$('#generic-alert-dialog-modal').modal();
}
// Note: The variables here are globals initialized and set in the script at the bottom of the
// corresponding html.
var showDefinitionsModal = function () {
showGenericAlert(definitions_html, definitions_button_text, { title: definitions_alert_title });
};
var setup_buttons = function () {
for (var i = 0; i < labels.length; i++) {
var el = $('#'+labels[i]);
el.click(get_classify_fn(labels[i]));
}
$('#alt-submit-button').click(validate);
$('.show-definitions').click(showDefinitionsModal);
if (auth_allowed) {
$('#login').click(function () { return login('oidc'); });
$('#login-google').click(function () { return login('google'); });
$('#login-sso').click(function () { return login(sso_provider); });
$('#logout').click(function () { return logout(); });
}
if (auth_required) {
$('#no-authentication').hide();
$('#authentication-required').show();
// Hide entire form until authenticated
if (require_authentication_to_show_form) {
$('#report-form-body').hide();
}
}
$('#generic-confirm-button').click(handleGenericConfirm);
};
var genericConfirmHandler = undefined;
function handleGenericConfirm() {
if (genericConfirmHandler) {
genericConfirmHandler();
genericConfirmHandler = undefined;
}
}
function showGenericConfirm(text, handler, options) {
if (!options) {
options = {};
}
genericConfirmHandler = handler;
$('#generic-confirm-dialog-text').html(text);
$('#generic-confirm-dialog-modal').modal({'backdrop': 'static'});
}
function login(provider) {
if (keycloak) {
if (provider) {
setLoginBreadcrumbProvider(provider); // record which provider we're trying to login, clear last timestamp to avoid loop
} else {
clearLoginBreadcrumbs();
}
keycloak.login({'idpHint': provider});
} else {
alert("The authentication system could not be loaded due to a temporary issue. Please proceed without signing in, or try again later.");
}
return false;
};
function logout() {
var options = {};
if (sso_auto_login) {
// Do something to prevent auto-login when the page reloads
options.redirectUri = window.location.origin + window.location.pathname + window.location.search +
(window.location.search.substr(0,1) == '?' ? '&' : '?') + 'sso_post_logout=true' + window.location.hash;
}
// Make sure we don't try to re-login again after page reloads.
clearLoginBreadcrumbs();
keycloak.logout(options);
return false;
};
function showPage() {
$('#body').show();
}
function hidePage() {
$('#body').hide();
}
var keycloak;
var KEYCLOAK_MAX_RELOADS = 5;
var resetKeycloakCounter = function () {
try {
localStorage.removeItem('inky-keycloak-counter');
} catch (e) {
}
};
var handleKeycloakLoadIssue = function () {
try {
// Check counter to avoid infinite reloading in case of keycloak outage.
var counter = parseInt(localStorage.getItem('inky-keycloak-counter') || 0);
if (counter < KEYCLOAK_MAX_RELOADS) {
console.log('keycloak counter ' + counter + ', so reloading page');
localStorage.setItem('inky-keycloak-counter', '' + (counter += 1));
location.reload();
} else {
console.log('keycloak counter ' + counter + ', so just showing login and resetting');
localStorage.removeItem('inky-keycloak-counter'); // reset
showLogin();
}
} catch (e) {
location.reload();
}
};
var shouldReLoginProvider = function (verbose) {
if (DISABLE_RELOGIN) {
return false;
}
if (!auth_allowed) {
return false;
}
var last_login_data = getLoginBreadcrumbs();
if (last_login_data) {
var now = Math.ceil(new Date().getTime() / 1000);
var last_login_seconds_ago = now - last_login_data.timestamp_seconds;
if (last_login_seconds_ago < 60*60*24*7) {
if (verbose) {
console.log('last logged in ' + last_login_seconds_ago +
' seconds ago so will attempt to re-login with same provider = ' + last_login_data.provider);
}
// Last login within a week, so try to login again as the same provider as last time.
return last_login_data.provider;
}
}
return null;
};
var getLoginBreadcrumbs = function () {
try {
var last_login_provider = JSON.parse(localStorage.getItem('inky.auth.last_login_provider') || null);
var last_login_timestamp = JSON.parse(localStorage.getItem('inky.auth.last_login_timestamp') || null);
if (last_login_provider && last_login_timestamp) {
return {
'timestamp_seconds': last_login_timestamp,
'provider': last_login_provider
};
} else {
return null;
}
} catch (e) {
return null;
}
};
var setLoginBreadcrumbTimestamp = function () {
try {
var timestamp_seconds = Math.ceil(new Date().getTime() / 1000);
//console.log('storing login breadcrumb: timestamp = ' + timestamp_seconds);
localStorage.setItem('inky.auth.last_login_timestamp', JSON.stringify(timestamp_seconds))
} catch (e) {
}
};
var setLoginBreadcrumbProvider = function (provider) {
try {
//console.log('storing login breadcrumb: provider = ' + provider);
localStorage.setItem('inky.auth.last_login_provider', JSON.stringify(provider));
clearLoginBreadcrumbs(/* timestamp only */ true); // clear last timestamp to avoid loop
} catch (e) {
}
};
var clearLoginBreadcrumbs = function (timestamp_only) {
try {
//console.log('clearing login breadcrumbs (timestamp only? ' + timestamp_only + ')');
localStorage.removeItem('inky.auth.last_login_timestamp');
if (!timestamp_only) {
localStorage.removeItem('inky.auth.last_login_provider');
}
} catch (e) {
}
};
var setup = function () {
// If we got here after a logout, then ignore anything about sso_auto_login;
// Note: in cases of custom sso, we shouldn't be seeing logouts, so this would most likely be the case
// of a config that tries to auto-login with a standard provider.
if (sso_auto_login) {
if (getParameterByName('sso_post_logout')) {
console.log('page is loaded after an explicit logout so will turn off auto login to avoid looping');
sso_auto_login = false;
}
}
// This should not return a value immediately after a logout.
var will_re_login = shouldReLoginProvider();
if (sso_auto_login || will_re_login) {
hidePage();
} else {
showPage();
}
setup_buttons();
update_form();
if (localStorage.getItem('inky-admin-previously-logged-in') && $('#dashboard-link').attr('href')) {
$('#dashboard-link-container').show();
} else {
$('#dashboard-link-container').hide();
}
if (auth_allowed) {
try {
keycloak = Keycloak({
url: 'https://auth.dashboard.inky.com:443/auth',
realm: 'inky',
clientId: 'inky-dashboard'
});
} catch (e) {
console.log('exception trying to load keycloak: ' + e);
}
$('#checking-auth').show();
try {
keycloak.init({
onLoad: 'check-sso',
checkLoginIframe: false,
redirectUri: window.location.href,
}).then(function(authenticated) {
resetKeycloakCounter();
if (authenticated) {
var expiresIn = keycloak.idTokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + keycloak.timeSkew;
console.log('user is authenticated via keycloak (token expires in ' + expiresIn + ' seconds)');
keycloak.loadUserProfile().then(function (r) {
setLoginBreadcrumbTimestamp(); // record we logged in
showLoggedIn();
}).catch(function (e) {
alert("Failed to load user profile. Please try signing in again.");
showLogin();
});
} else {
console.log('user is not authenticated via keycloak');
showLogin();
}
}).catch(function(e) {
console.log('keycloak error details: ', e);
//alert('Failed to initialize identity provider Keycloak!');
handleKeycloakLoadIssue();
});
} catch (e) {
console.log('caught exception loading keycloak: ', e);
//alert("A required component failed to load. The page will now reload. If the problem persists, please contact INKY support.");
handleKeycloakLoadIssue();
}
}
};
var showLoggedIn = function () {
// Set hidden field with token so it gets posted when the form is submitted
$('#keycloak_token').val(keycloak.idToken);
$('#checking-auth').hide();
$('#not-logged-in-container').hide();
$('#logged-in-user').html(keycloak.profile.email);
$('#logged-in-container').show();
$('#user-dashboard-footer-link').show();
// Make sure to show the report form which may have been hidden until now
// (due to auth_required and require_authentication_to_show_form).
$('#report-form-body').show();
// Make sure the page is visible now (might be hidden due to sso auto login)
showPage();
// If a label was already selected, ensure all the authenticated actions are shown;
var selectedLabel = $('#label').val();
if (selectedLabel) {
classify(selectedLabel, /* force */ true);
}
};
var showLogin = function () {
var re_login_provider = shouldReLoginProvider(/*verbose*/ true);
if (re_login_provider) {
// Note: This stuff is not going to be visible anymore, since the whole page should be hidden at this point.
// But just in case it's not, or we fine-tune that behavior later, keeping this here anyway.
$('#checking-auth div.alert').html("Connecting to Single Sign-On provider...");
$('#logged-in-container').hide();
$('#not-logged-in-container').hide();
login(re_login_provider); // this will be whatever was last logged in
} else if (sso_auto_login) {
// Note: This stuff is not going to be visible anymore, since the whole page should be hidden at this point.
// But just in case it's not, or we fine-tune that behavior later, keeping this here anyway.
$('#checking-auth div.alert').html("Connecting to Single Sign-On provider...");
$('#logged-in-container').hide();
$('#not-logged-in-container').hide();
login(sso_auto_login_provider); // this might be a custom provider or one of the standard ones
} else {
$('#checking-auth').hide();
$('#logged-in-container').hide();
$('#not-logged-in-container').show();
// Make sure the page is visible now. Should be already, but just in case.
showPage();
}
}
// Record if user confirmed action already.
// Note: We're just checking once but in almost all cases, there will only be 1 type of block domain action to really confirm.
var alreadyConfirmed = false;
var requiresConfirmation = function() {
if (alreadyConfirmed) {
return false;
}
//
// Find all the checked block or allow actions to create a confirmation prompt.
//
var checked_blocks = [];
$('.block-domain-action').each(function () {
if ($(this).is(':checked')) {
checked_blocks.push($(this).parent().find('label').html().trim());
}
});
var checked_allows = [];
$('.allow-action').each(function() {
if ($(this).is(':checked')) {
checked_allows.push($(this).parent().find('label').html().trim());
}
})
var text;
if (checked_blocks.length > 0) {
text = block_confirmation_text + "