mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-08-05 06:50:10 +00:00
feat: add validFolderIds support for cipher responses and update folder handling in storage
This commit is contained in:
+10
-2
@@ -32,6 +32,7 @@ import { auditRequestMetadata, writeAuditEvent } from '../services/audit-events'
|
|||||||
// attachments, import/export, and current official clients.
|
// attachments, import/export, and current official clients.
|
||||||
export interface CipherResponseOptions {
|
export interface CipherResponseOptions {
|
||||||
preserveRepairableUris?: boolean;
|
preserveRepairableUris?: boolean;
|
||||||
|
validFolderIds?: ReadonlySet<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldPreserveRepairableCipherUris(request: Request): boolean {
|
export function shouldPreserveRepairableCipherUris(request: Request): boolean {
|
||||||
@@ -48,6 +49,12 @@ function normalizeOptionalId(value: unknown): string | null {
|
|||||||
return normalized ? normalized : null;
|
return normalized ? normalized : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeResponseFolderId(folderId: unknown, validFolderIds?: ReadonlySet<string>): string | null {
|
||||||
|
const normalized = normalizeOptionalId(folderId);
|
||||||
|
if (!normalized) return null;
|
||||||
|
return validFolderIds && !validFolderIds.has(normalized) ? null : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
function readBooleanOrFallback(value: unknown, fallback: boolean): boolean {
|
function readBooleanOrFallback(value: unknown, fallback: boolean): boolean {
|
||||||
return typeof value === 'boolean' ? value : fallback;
|
return typeof value === 'boolean' ? value : fallback;
|
||||||
}
|
}
|
||||||
@@ -727,7 +734,7 @@ export function cipherToResponse(
|
|||||||
// Pass through ALL stored cipher fields (known + unknown)
|
// Pass through ALL stored cipher fields (known + unknown)
|
||||||
...passthrough,
|
...passthrough,
|
||||||
// Server-computed / enforced fields (always override)
|
// Server-computed / enforced fields (always override)
|
||||||
folderId: normalizeOptionalId(cipher.folderId),
|
folderId: normalizeResponseFolderId(cipher.folderId, options.validFolderIds),
|
||||||
type: Number(cipher.type) || 1,
|
type: Number(cipher.type) || 1,
|
||||||
organizationId: normalizeOptionalId((passthrough as any).organizationId ?? null),
|
organizationId: normalizeOptionalId((passthrough as any).organizationId ?? null),
|
||||||
organizationUseTotp: !!((passthrough as any).organizationUseTotp ?? false),
|
organizationUseTotp: !!((passthrough as any).organizationUseTotp ?? false),
|
||||||
@@ -785,9 +792,10 @@ export async function handleGetCiphers(request: Request, env: Env, userId: strin
|
|||||||
const attachmentsByCipher = await storage.getAttachmentsByCipherIds(
|
const attachmentsByCipher = await storage.getAttachmentsByCipherIds(
|
||||||
filteredCiphers.map((cipher) => cipher.id)
|
filteredCiphers.map((cipher) => cipher.id)
|
||||||
);
|
);
|
||||||
|
const validFolderIds = new Set((await storage.getAllFolders(userId)).map((folder) => folder.id));
|
||||||
|
|
||||||
// Build responses only for the current page to keep pagination cheap.
|
// Build responses only for the current page to keep pagination cheap.
|
||||||
const responseOptions = cipherResponseOptionsForRequest(request);
|
const responseOptions = { ...cipherResponseOptionsForRequest(request), validFolderIds };
|
||||||
const cipherResponses: CipherResponse[] = [];
|
const cipherResponses: CipherResponse[] = [];
|
||||||
for (const cipher of filteredCiphers) {
|
for (const cipher of filteredCiphers) {
|
||||||
const attachments = attachmentsByCipher.get(cipher.id) || [];
|
const attachments = attachmentsByCipher.get(cipher.id) || [];
|
||||||
|
|||||||
@@ -88,12 +88,13 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
|
|||||||
.map(buildWebAuthnPrfOption)
|
.map(buildWebAuthnPrfOption)
|
||||||
.filter((option): option is NonNullable<typeof option> => !!option);
|
.filter((option): option is NonNullable<typeof option> => !!option);
|
||||||
const userDecryptionOptions = buildUserDecryptionOptions(user, webAuthnPrfOptions[0] || null);
|
const userDecryptionOptions = buildUserDecryptionOptions(user, webAuthnPrfOptions[0] || null);
|
||||||
|
const validFolderIds = new Set(folders.map((folder) => folder.id));
|
||||||
|
|
||||||
const profile: ProfileResponse = buildProfileResponse(user, env);
|
const profile: ProfileResponse = buildProfileResponse(user, env);
|
||||||
|
|
||||||
const cipherResponses: CipherResponse[] = [];
|
const cipherResponses: CipherResponse[] = [];
|
||||||
for (const cipher of ciphers) {
|
for (const cipher of ciphers) {
|
||||||
const response = cipherToResponse(cipher, attachmentsByCipher.get(cipher.id) || [], { preserveRepairableUris });
|
const response = cipherToResponse(cipher, attachmentsByCipher.get(cipher.id) || [], { preserveRepairableUris, validFolderIds });
|
||||||
if (isCipherResponseSyncCompatible(response)) {
|
if (isCipherResponseSyncCompatible(response)) {
|
||||||
cipherResponses.push(response);
|
cipherResponses.push(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,9 +44,14 @@ export async function clearFolderFromCiphers(
|
|||||||
`UPDATE ciphers
|
`UPDATE ciphers
|
||||||
SET folder_id = NULL, updated_at = ?,
|
SET folder_id = NULL, updated_at = ?,
|
||||||
data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate')
|
data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate')
|
||||||
WHERE user_id = ? AND folder_id = ?`
|
WHERE user_id = ?
|
||||||
|
AND (
|
||||||
|
folder_id = ?
|
||||||
|
OR json_extract(data, '$.folderId') = ?
|
||||||
|
OR json_extract(data, '$.folder_id') = ?
|
||||||
|
)`
|
||||||
)
|
)
|
||||||
.bind(now, userId, folderId)
|
.bind(now, userId, folderId, folderId, folderId)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +76,14 @@ export async function bulkDeleteFolders(
|
|||||||
`UPDATE ciphers
|
`UPDATE ciphers
|
||||||
SET folder_id = NULL, updated_at = ?,
|
SET folder_id = NULL, updated_at = ?,
|
||||||
data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate')
|
data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate')
|
||||||
WHERE user_id = ? AND folder_id IN (${placeholders})`
|
WHERE user_id = ?
|
||||||
|
AND (
|
||||||
|
folder_id IN (${placeholders})
|
||||||
|
OR json_extract(data, '$.folderId') IN (${placeholders})
|
||||||
|
OR json_extract(data, '$.folder_id') IN (${placeholders})
|
||||||
|
)`
|
||||||
)
|
)
|
||||||
.bind(now, userId, ...chunk)
|
.bind(now, userId, ...chunk, ...chunk, ...chunk)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
await db
|
await db
|
||||||
|
|||||||
Reference in New Issue
Block a user