From 813f986344aa9e88c97fe16371b68a951c556f79 Mon Sep 17 00:00:00 2001 From: Tiago Date: Wed, 17 Dec 2025 16:47:15 -0300 Subject: [PATCH] Remover bloqueio do cofre de senhas --- assets/app.js | 199 ++++++----------------------------------------- assets/style.css | 2 - index.html | 43 ++-------- 3 files changed, 32 insertions(+), 212 deletions(-) diff --git a/assets/app.js b/assets/app.js index a50bd74..30650fb 100644 --- a/assets/app.js +++ b/assets/app.js @@ -4,7 +4,6 @@ const STORAGE = { UNITS: "telseg_units", CREDS: "telseg_passwords", - VAULT: "telseg_vault", THEME: "telseg_theme", NOTES: "telseg_notes", AUTH: "telseg_auth", @@ -14,7 +13,7 @@ const $ = (sel, el = document) => el.querySelector(sel); const $$ = (sel, el = document) => Array.from(el.querySelectorAll(sel)); // Detecção de API -const state = { useApi: false, units: [], creds: [], vault: null, notes: [], auth: null }; +const state = { useApi: false, units: [], creds: [], notes: [], auth: null }; async function probeApi() { try { @@ -264,17 +263,6 @@ function escapeHTML(s) { return (s ?? "").replace(/[&<>"]/g, c => ({"&":"&", function escapeAttr(s) { return escapeHTML(s).replace(/'/g, "'"); } function formatDate(s) { try { const [y,m,d] = (s||"").split("-"); return d && m && y ? `${d}/${m}/${y}` : s; } catch { return s; } } -// WebCrypto guard (requer HTTPS/localhost) -const CRYPTO_REQUIRE_MSG = "O cofre precisa de HTTPS (ou localhost) porque o navegador bloqueia a criptografia em origens inseguras."; -let cryptoWarned = false; -function hasCrypto() { return !!(globalThis.crypto && globalThis.crypto.subtle); } -function ensureCrypto(msgTarget) { - if (hasCrypto()) return true; - if (msgTarget) msgTarget.textContent = CRYPTO_REQUIRE_MSG; - if (!cryptoWarned) { alert(CRYPTO_REQUIRE_MSG); cryptoWarned = true; } - return false; -} -// ------- Cofre de Senhas (WebCrypto) ------- async function refreshCreds() { if (state.useApi) { const r = await apiFetch('/api/creds'); @@ -286,18 +274,7 @@ async function refreshCreds() { } function loadCreds() { return state.creds || []; } function saveCreds(list) { saveJSON(STORAGE.CREDS, list); state.creds = list; } - -async function refreshVault() { - if (state.useApi) { - const r = await apiFetch('/api/vault'); - state.vault = r.ok ? await r.json() : null; - } else { - state.vault = loadJSON(STORAGE.VAULT, null); - } - return state.vault; -} -function getVault() { return state.vault; } -function setVault(v) { saveJSON(STORAGE.VAULT, v); state.vault = v; } +function getCredPassword(cred) { return cred?.passwordEnc ?? ""; } // ------- Notas ------- async function refreshNotes() { @@ -417,97 +394,6 @@ function openNoteView(n) { showModal('noteViewModal'); } -const textEnc = new TextEncoder(); -const textDec = new TextDecoder(); - -function ab2b64(ab) { - const bytes = new Uint8Array(ab); - let bin = ""; for (let i=0; i { if (c.url) window.open(c.url, '_blank', 'noopener'); }; btnCopyUser.onclick = () => { if (c.usuario) copyText(c.usuario); }; - btnCopyPass.onclick = async () => { - try { const pass = await decryptString(vaultKey, c.passwordEnc); copyText(pass); } catch { alert('Falha ao descriptografar.'); } + btnCopyPass.onclick = () => { + const pass = getCredPassword(c); + if (!pass) { alert('Senha indisponível.'); return; } + copyText(pass); }; - btnReveal.onclick = async () => { - try { const pass = await decryptString(vaultKey, c.passwordEnc); alert(`Senha: ${pass}`); } catch { alert('Falha ao descriptografar.'); } + btnReveal.onclick = () => { + const pass = getCredPassword(c); + if (!pass) { alert('Senha indisponível.'); return; } + alert(`Senha: ${pass}`); }; btnEdit.onclick = () => { hideModal('credViewModal'); openCredModal(c); }; btnDelete.onclick = async () => { @@ -647,7 +537,6 @@ function exportAll() { exportedAt: new Date().toISOString(), theme: document.documentElement.getAttribute("data-theme"), units: loadUnits(), - vault: getVault(), creds: loadCreds(), notes: loadNotes(), }; @@ -672,12 +561,10 @@ function importAll(file) { if (data.theme) { localStorage.setItem(STORAGE.THEME, data.theme); document.documentElement.setAttribute("data-theme", data.theme); } if (Array.isArray(data.units)) saveUnits(data.units); if (Array.isArray(data.creds)) saveCreds(data.creds); - if (data.vault && data.vault.salt && data.vault.verification) setVault(data.vault); if (Array.isArray(data.notes)) saveNotes(data.notes); alert("Importação concluída."); renderUnits($("#searchUnidades").value); renderNotes($("#searchNotes").value); - lockVault(); // força rechecagem do cofre } catch (e) { alert("Arquivo inválido."); } @@ -706,7 +593,7 @@ document.addEventListener("DOMContentLoaded", async () => { } } if (!state.useApi || state.auth?.user) { - await Promise.all([refreshUnits(), refreshCreds(), refreshVault(), refreshNotes()]); + await Promise.all([refreshUnits(), refreshCreds(), refreshNotes()]); } updateStorageInfo(); applyPermissionsUI(); @@ -757,14 +644,9 @@ document.addEventListener("DOMContentLoaded", async () => { if (btn) hideModal(btn.getAttribute("data-close-modal")); }); - // Senhas/Cofre - renderVaultGate(); - $("#lockVaultBtn").addEventListener("click", lockVault); + // Senhas $("#searchCreds").addEventListener("input", ev => renderCreds(ev.target.value)); - $("#addCredBtn").addEventListener("click", () => { - if (!vaultKey) { alert("Desbloqueie o cofre primeiro."); return; } - openCredModal(); - }); + $("#addCredBtn").addEventListener("click", () => openCredModal()); // Notas $("#addNoteBtn").addEventListener("click", () => openNoteModal()); @@ -821,50 +703,19 @@ document.addEventListener("DOMContentLoaded", async () => { renderNotes($("#searchNotes").value); }); - $("#setVaultForm").addEventListener("submit", async (ev) => { - ev.preventDefault(); - if (!ensureCrypto($("#vaultStatus"))) return; - const p1 = $("#vaultPass1").value; - const p2 = $("#vaultPass2").value; - if (!p1 || p1 !== p2) { alert("Senhas não conferem."); return; } - const salt = ab2b64(crypto.getRandomValues(new Uint8Array(16)).buffer); - const key = await deriveKey(p1, salt, VAULT_ITERS); - const verification = await encryptString(key, "ok"); - const meta = { salt, verification, iterations: VAULT_ITERS, v: 1 }; - if (state.useApi) { - await apiFetch('/api/vault', { method: 'PUT', body: JSON.stringify(meta) }); - await refreshVault(); - } else { - setVault(meta); - } - alert("Cofre configurado. Guarde sua senha mestra!"); - $("#vaultPass1").value = ""; $("#vaultPass2").value = ""; - unlockVault(p1); - }); - - $("#unlockVaultForm").addEventListener("submit", async (ev) => { - ev.preventDefault(); - const p = $("#vaultUnlockPass").value; - await unlockVault(p); - }); - $("#credForm").addEventListener("submit", async (ev) => { ev.preventDefault(); - if (!vaultKey) { alert("Cofre bloqueado."); return; } const id = $("#credId").value || uid(); const list = loadCreds(); const idx = list.findIndex(x => x.id === id); - const existing = idx >= 0 ? list[idx] : null; - const passwordPlain = $("#credSenha").value; // pode estar vazio ao editar - let passwordEnc = existing?.passwordEnc; - if (passwordPlain) passwordEnc = await encryptString(vaultKey, passwordPlain); + const passwordPlain = $("#credSenha").value.trim(); const item = { id, nome: $("#credNome").value.trim(), usuario: $("#credUsuario").value.trim(), url: $("#credUrl").value.trim(), notas: $("#credNotas").value.trim(), - passwordEnc, + passwordEnc: passwordPlain, updatedAt: Date.now(), }; if (!item.nome) { alert("Informe o nome."); return; } @@ -901,8 +752,7 @@ document.addEventListener("DOMContentLoaded", async () => { const data = JSON.parse(text); if (!confirm("Importar backup e substituir dados atuais no servidor?")) return; await apiFetch('/api/import', { method: 'POST', body: JSON.stringify(data) }); - await Promise.all([refreshUnits(), refreshCreds(), refreshVault(), refreshNotes()]); - lockVault(); + await Promise.all([refreshUnits(), refreshCreds(), refreshNotes()]); renderUnits($("#searchUnidades").value); // re-render renderNotes($("#searchNotes").value); } else { @@ -926,7 +776,7 @@ document.addEventListener("DOMContentLoaded", async () => { state.auth = { user: data.user }; hideLogin(); applyPermissionsUI(); - await Promise.all([refreshUnits(), refreshCreds(), refreshVault(), refreshNotes()]); + await Promise.all([refreshUnits(), refreshCreds(), refreshNotes()]); if (canMaster()) await renderUsers(); renderUnits(document.getElementById('searchUnidades').value); renderNotes(document.getElementById('searchNotes').value); @@ -940,6 +790,7 @@ document.addEventListener("DOMContentLoaded", async () => { if (!state.useApi || state.auth?.user) { renderUnits(""); renderNotes(""); + renderCreds(""); } }); diff --git a/assets/style.css b/assets/style.css index 496249a..158d467 100644 --- a/assets/style.css +++ b/assets/style.css @@ -135,8 +135,6 @@ label span { display: block; font-size: 13px; color: var(--muted); margin-bottom .modal-body { padding: 14px; display: grid; gap: 12px; } .modal-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; } -.vault-lock { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 16px; display: grid; gap: 10px; max-width: 720px; margin: 18px auto; } -.vault-form { display: grid; gap: 12px; } .hidden { display: none !important; } .app-footer { padding: 18px; text-align: center; color: var(--muted); } diff --git a/index.html b/index.html index 125d365..a0d5292 100644 --- a/index.html +++ b/index.html @@ -46,44 +46,15 @@
-
-

Cofre de Senhas

-

Proteja suas senhas com uma senha mestra.

- - - -
- -