mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 07:01:16 +00:00
156 lines
5.9 KiB
JavaScript
156 lines
5.9 KiB
JavaScript
/*
|
|
* Credentials - the password, copy and 2FA buttons functions/credential.php
|
|
* draws wherever a credential is listed (the credentials list, assets,
|
|
* contacts, vendors, licenses, the quick looks). Bound once on document, so
|
|
* they work in ajax modals too.
|
|
*
|
|
* Nothing secret is in the page until it is asked for: the eye and the copy
|
|
* button fetch the password from agent/ajax.php (get_credential_via_id), the
|
|
* 2FA button fetches the current code (get_totp_token_via_id) - both check the
|
|
* module and the client and log the view.
|
|
*/
|
|
|
|
(function () {
|
|
|
|
// How long a shown password stays on screen, and how many codes in a row the 2FA button follows on to
|
|
var SHOW_FOR = 30000;
|
|
var OTP_ROUNDS = 6;
|
|
|
|
function fetchCredential(id, done) {
|
|
itflowGet('/agent/ajax.php', { get_credential_via_id: 'true', credential_id: id }, function (data) {
|
|
var credential = null;
|
|
try {
|
|
credential = JSON.parse(data);
|
|
} catch (e) {}
|
|
if (credential) {
|
|
done(credential);
|
|
}
|
|
});
|
|
}
|
|
|
|
function hidePassword(box) {
|
|
var value = box.querySelector('.itflow-credential-pw-value');
|
|
var mask = box.querySelector('.itflow-credential-pw-mask');
|
|
var button = box.querySelector('.itflow-credential-reveal');
|
|
clearTimeout(box.itflowHide);
|
|
value.textContent = '';
|
|
value.hidden = true;
|
|
mask.hidden = false;
|
|
if (button) {
|
|
button.querySelector('i').className = 'fas fa-eye';
|
|
button.setAttribute('aria-pressed', 'false');
|
|
button.title = 'Show password';
|
|
}
|
|
}
|
|
|
|
// The eye: show the password for a while, or hide it again
|
|
itflowBindOnce('itflowCredentialReveal', 'click', '.itflow-credential-reveal', function (e) {
|
|
e.preventDefault();
|
|
var button = this;
|
|
var box = button.closest('.itflow-credential-pw');
|
|
if (!box) {
|
|
return;
|
|
}
|
|
var value = box.querySelector('.itflow-credential-pw-value');
|
|
if (!value.hidden) {
|
|
hidePassword(box);
|
|
return;
|
|
}
|
|
fetchCredential(box.dataset.credentialId, function (credential) {
|
|
value.textContent = credential.password || '';
|
|
value.hidden = false;
|
|
box.querySelector('.itflow-credential-pw-mask').hidden = true;
|
|
button.querySelector('i').className = 'fas fa-eye-slash';
|
|
button.setAttribute('aria-pressed', 'true');
|
|
button.title = 'Hide password';
|
|
clearTimeout(box.itflowHide);
|
|
box.itflowHide = setTimeout(function () {
|
|
hidePassword(box);
|
|
}, SHOW_FOR);
|
|
});
|
|
});
|
|
|
|
// Copy: fetch the password and copy it, without showing it
|
|
itflowBindOnce('itflowCredentialCopy', 'click', '.itflow-credential-copy', function (e) {
|
|
e.preventDefault();
|
|
var button = this;
|
|
var box = button.closest('[data-credential-id]');
|
|
if (!box) {
|
|
return;
|
|
}
|
|
fetchCredential(box.dataset.credentialId, function (credential) {
|
|
itflowCopyText(credential.password || '', button).then(
|
|
function () { flashTooltip(button, 'Copied!'); },
|
|
function () { flashTooltip(button, 'Failed!'); }
|
|
);
|
|
});
|
|
});
|
|
|
|
function resetOtp(button) {
|
|
clearInterval(button.itflowTimer);
|
|
delete button.dataset.code;
|
|
button.classList.remove('is-live', 'is-ending');
|
|
button.querySelector('.itflow-credential-otp-code').textContent = '2FA';
|
|
button.querySelector('.itflow-credential-otp-time').textContent = '';
|
|
button.title = 'Show the 2FA code';
|
|
}
|
|
|
|
// 2FA: the first click fetches the code and counts it down, following on to
|
|
// the next one for a few minutes; clicking it while it shows copies it
|
|
itflowBindOnce('itflowCredentialOtp', 'click', '.itflow-credential-otp', function (e) {
|
|
e.preventDefault();
|
|
var button = this;
|
|
if (button.dataset.code) {
|
|
itflowCopyText(button.dataset.code, button).then(
|
|
function () { flashTooltip(button, 'Copied!'); },
|
|
function () { flashTooltip(button, 'Failed!'); }
|
|
);
|
|
return;
|
|
}
|
|
var codeEl = button.querySelector('.itflow-credential-otp-code');
|
|
var timeEl = button.querySelector('.itflow-credential-otp-time');
|
|
var rounds = 0;
|
|
var last = 31;
|
|
|
|
function load() {
|
|
itflowGet('/agent/ajax.php', { get_totp_token_via_id: 'true', credential_id: button.dataset.credentialOtp }, function (data) {
|
|
var code = '';
|
|
try {
|
|
code = String(JSON.parse(data) || '');
|
|
} catch (err) {}
|
|
if (!/^[0-9]{6,8}$/.test(code)) {
|
|
resetOtp(button);
|
|
codeEl.textContent = 'No code';
|
|
return;
|
|
}
|
|
button.dataset.code = code;
|
|
codeEl.textContent = code.length === 6 ? code.slice(0, 3) + ' ' + code.slice(3) : code;
|
|
button.classList.add('is-live');
|
|
button.title = 'Click to copy the code';
|
|
});
|
|
}
|
|
|
|
function tick() {
|
|
var left = 30 - Math.floor(Date.now() / 1000) % 30;
|
|
timeEl.textContent = left + 's';
|
|
button.classList.toggle('is-ending', left <= 5);
|
|
// A new period - the next code, for a few rounds, then back to the button
|
|
if (left > last) {
|
|
rounds++;
|
|
if (rounds >= OTP_ROUNDS || !document.body.contains(button)) {
|
|
resetOtp(button);
|
|
return;
|
|
}
|
|
load();
|
|
}
|
|
last = left;
|
|
}
|
|
|
|
clearInterval(button.itflowTimer);
|
|
load();
|
|
tick();
|
|
button.itflowTimer = setInterval(tick, 1000);
|
|
});
|
|
|
|
})();
|