imaging: LAPS-password-to-QR generator per bay card
Per-bay <details> section with: - Input field for LAPS password (paste from Intune portal manually, since deep-link to LAPS blade needs AAD objectId we can't obtain) - Make QR button generates a client-side QR from the input - QR displayed below at 280px with 4-cell quiet zone - Auto-clears input + QR after 60s with live countdown - Manual Clear button - Enter key on the input also triggers QR generation Password never POSTs to server, never logged, never persists past the 60s window. Generated using the same qrcode-generator lib already loaded for the device-id QR. Scan with a USB barcode scanner plugged into the bay (HID keyboard mode) -> password types into bay login field. Faster than reading off the Intune portal letter-by-letter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -96,6 +96,23 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if s.intune_device_id %}
|
||||||
|
<details class="mt-2 laps-card">
|
||||||
|
<summary class="text-muted small">LAPS password QR (paste -> scan on bay)</summary>
|
||||||
|
<div class="d-flex align-items-center gap-2 mt-2">
|
||||||
|
<input type="text"
|
||||||
|
class="form-control form-control-sm laps-input"
|
||||||
|
style="font-family: monospace; max-width: 22rem;"
|
||||||
|
placeholder="paste LAPS password from Intune portal here"
|
||||||
|
autocomplete="off">
|
||||||
|
<button type="button" class="btn btn-sm btn-primary laps-make-btn">Make QR</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary laps-clear-btn" style="display:none;">Clear</button>
|
||||||
|
<span class="laps-timer text-muted small"></span>
|
||||||
|
</div>
|
||||||
|
<div class="laps-qr-container mt-2"></div>
|
||||||
|
</details>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if s.log_tail %}
|
{% if s.log_tail %}
|
||||||
<details>
|
<details>
|
||||||
<summary class="text-muted small">Log tail ({{ s.log_tail | length }} line{{ 's' if s.log_tail | length != 1 }})</summary>
|
<summary class="text-muted small">Log tail ({{ s.log_tail | length }} line{{ 's' if s.log_tail | length != 1 }})</summary>
|
||||||
@@ -202,5 +219,67 @@ document.addEventListener('click', function(e) {
|
|||||||
copyText(text).then(function() { flashCopied(btn, true); })
|
copyText(text).then(function() { flashCopied(btn, true); })
|
||||||
.catch(function(err) { console.error('copy failed:', err); flashCopied(btn, false); });
|
.catch(function(err) { console.error('copy failed:', err); flashCopied(btn, false); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// LAPS password -> client-side QR. Password never leaves browser tab.
|
||||||
|
// Auto-clears input + QR after 60s so it doesn't linger on shared screens.
|
||||||
|
function renderLapsQR(card) {
|
||||||
|
var input = card.querySelector('.laps-input');
|
||||||
|
var container = card.querySelector('.laps-qr-container');
|
||||||
|
var makeBtn = card.querySelector('.laps-make-btn');
|
||||||
|
var clearBtn = card.querySelector('.laps-clear-btn');
|
||||||
|
var timerEl = card.querySelector('.laps-timer');
|
||||||
|
var text = input.value;
|
||||||
|
if (!text) { input.focus(); return; }
|
||||||
|
try {
|
||||||
|
var qr = qrcode(0, 'M');
|
||||||
|
qr.addData(text);
|
||||||
|
qr.make();
|
||||||
|
var modules = qr.getModuleCount();
|
||||||
|
var size = 280;
|
||||||
|
var cellSize = Math.max(1, Math.floor(size / (modules + 8)));
|
||||||
|
container.innerHTML = qr.createImgTag(cellSize, 4);
|
||||||
|
makeBtn.style.display = 'none';
|
||||||
|
clearBtn.style.display = '';
|
||||||
|
var remaining = 60;
|
||||||
|
timerEl.textContent = '(auto-clears in ' + remaining + 's)';
|
||||||
|
if (card._lapsTimer) clearInterval(card._lapsTimer);
|
||||||
|
card._lapsTimer = setInterval(function() {
|
||||||
|
remaining--;
|
||||||
|
if (remaining <= 0) { clearLapsQR(card); return; }
|
||||||
|
timerEl.textContent = '(auto-clears in ' + remaining + 's)';
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
container.textContent = 'QR error: ' + err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function clearLapsQR(card) {
|
||||||
|
var input = card.querySelector('.laps-input');
|
||||||
|
var container = card.querySelector('.laps-qr-container');
|
||||||
|
var makeBtn = card.querySelector('.laps-make-btn');
|
||||||
|
var clearBtn = card.querySelector('.laps-clear-btn');
|
||||||
|
var timerEl = card.querySelector('.laps-timer');
|
||||||
|
if (card._lapsTimer) { clearInterval(card._lapsTimer); card._lapsTimer = null; }
|
||||||
|
input.value = '';
|
||||||
|
container.innerHTML = '';
|
||||||
|
timerEl.textContent = '';
|
||||||
|
makeBtn.style.display = '';
|
||||||
|
clearBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
var card;
|
||||||
|
if (e.target.classList.contains('laps-make-btn')) {
|
||||||
|
card = e.target.closest('.laps-card');
|
||||||
|
if (card) renderLapsQR(card);
|
||||||
|
} else if (e.target.classList.contains('laps-clear-btn')) {
|
||||||
|
card = e.target.closest('.laps-card');
|
||||||
|
if (card) clearLapsQR(card);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Enter' && e.target.classList.contains('laps-input')) {
|
||||||
|
var card = e.target.closest('.laps-card');
|
||||||
|
if (card) { e.preventDefault(); renderLapsQR(card); }
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user