05/29 Update shared items displayed on Grantee items 4
This commit is contained in:
+56
-6
@@ -142,6 +142,29 @@ const Vault = (() => {
|
|||||||
"user_handle",
|
"user_handle",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Fields that are internal metadata and should never be shown in the detail
|
||||||
|
// view or shared with recipients.
|
||||||
|
const INTERNAL_PLAIN_FIELDS = new Set([
|
||||||
|
"password_history",
|
||||||
|
"password_changed_at",
|
||||||
|
"autofill",
|
||||||
|
"autologin",
|
||||||
|
"reprompt",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip internal/owner-only fields from a plain object before encrypting it
|
||||||
|
* for a recipient. Recipients should only see credential data, not owner
|
||||||
|
* preferences or history.
|
||||||
|
*/
|
||||||
|
function prepareSharePlain(plain) {
|
||||||
|
const out = {};
|
||||||
|
for (const [k, v] of Object.entries(plain)) {
|
||||||
|
if (!INTERNAL_PLAIN_FIELDS.has(k)) out[k] = v;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// ── API helpers ───────────────────────────────────────────────────────────
|
// ── API helpers ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function apiFetch(path, options = {}) {
|
async function apiFetch(path, options = {}) {
|
||||||
@@ -414,7 +437,7 @@ const Vault = (() => {
|
|||||||
SharingSession.getKey(),
|
SharingSession.getKey(),
|
||||||
recipPub,
|
recipPub,
|
||||||
);
|
);
|
||||||
const { enc_data, iv } = await SharingCrypto.encryptForShare(sharedKey, plainData);
|
const { enc_data, iv } = await SharingCrypto.encryptForShare(sharedKey, prepareSharePlain(plainData));
|
||||||
const { enc_name, iv_name } = await SharingCrypto.encryptName(sharedKey, itemName);
|
const { enc_name, iv_name } = await SharingCrypto.encryptName(sharedKey, itemName);
|
||||||
return { share_id: s.id, enc_data, iv, enc_name, iv_name };
|
return { share_id: s.id, enc_data, iv, enc_name, iv_name };
|
||||||
}),
|
}),
|
||||||
@@ -720,11 +743,19 @@ const Vault = (() => {
|
|||||||
copyToClipboard(secret || "", "Copied to clipboard");
|
copyToClipboard(secret || "", "Copied to clipboard");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
async function openFreshSharedDetail() {
|
||||||
|
// Re-fetch shared items so the grantee always sees the owner's latest
|
||||||
|
// version, not a stale in-memory snapshot.
|
||||||
|
await loadSharedItemsForVault();
|
||||||
|
const fresh = _sharedItems.find((si) => si._shareId === item._shareId);
|
||||||
|
const target = fresh || item;
|
||||||
|
showSharedItemDetails(target.name, target.item_type, target.plain);
|
||||||
|
}
|
||||||
li.querySelector('[data-action="view"]').addEventListener("click", (e) => {
|
li.querySelector('[data-action="view"]').addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
showSharedItemDetails(item.name, item.item_type, item.plain);
|
openFreshSharedDetail();
|
||||||
});
|
});
|
||||||
li.addEventListener("click", () => showSharedItemDetails(item.name, item.item_type, item.plain));
|
li.addEventListener("click", () => openFreshSharedDetail());
|
||||||
return li;
|
return li;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2220,12 +2251,31 @@ const Vault = (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderDetailFields(container, plain) {
|
function renderDetailFields(container, plain) {
|
||||||
|
const DATE_FIELDS = new Set(["password_changed_at"]);
|
||||||
container.innerHTML =
|
container.innerHTML =
|
||||||
Object.entries(plain)
|
Object.entries(plain)
|
||||||
.filter(([, v]) => v)
|
.filter(([k, v]) => {
|
||||||
|
if (INTERNAL_PLAIN_FIELDS.has(k)) return false; // strip internal metadata
|
||||||
|
if (k === "tags") return Array.isArray(v) && v.length > 0;
|
||||||
|
return !!v;
|
||||||
|
})
|
||||||
.map(([k, v]) => {
|
.map(([k, v]) => {
|
||||||
const label = FIELD_LABELS[k] || k.replace(/_/g, " ");
|
const label = FIELD_LABELS[k] || k.replace(/_/g, " ");
|
||||||
const val = String(v);
|
// Tags → badge chips, no copy button
|
||||||
|
if (k === "tags") {
|
||||||
|
const badges = (v || [])
|
||||||
|
.map((t) => `<span class="item-tag">${escHtml(t)}</span>`)
|
||||||
|
.join(" ");
|
||||||
|
return `<div class="detail-field">
|
||||||
|
<span class="detail-label">${escHtml(label)}</span>
|
||||||
|
<div class="detail-val-row"><span class="detail-val">${badges}</span></div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
// Dates → human-readable
|
||||||
|
let val = String(v);
|
||||||
|
if (DATE_FIELDS.has(k)) {
|
||||||
|
try { val = new Date(v).toLocaleString(); } catch { /* keep raw */ }
|
||||||
|
}
|
||||||
const sensitive = SENSITIVE_FIELDS.has(k);
|
const sensitive = SENSITIVE_FIELDS.has(k);
|
||||||
const valHtml = sensitive
|
const valHtml = sensitive
|
||||||
? `<span class="detail-sensitive">
|
? `<span class="detail-sensitive">
|
||||||
@@ -2313,7 +2363,7 @@ const Vault = (() => {
|
|||||||
|
|
||||||
const { enc_data, iv } = await SharingCrypto.encryptForShare(
|
const { enc_data, iv } = await SharingCrypto.encryptForShare(
|
||||||
sharedKey,
|
sharedKey,
|
||||||
item.plain,
|
prepareSharePlain(item.plain),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Encrypt the display name with the same ECDH shared key so the server
|
// Encrypt the display name with the same ECDH shared key so the server
|
||||||
|
|||||||
Reference in New Issue
Block a user