fix(security): scope storage reads by user

This commit is contained in:
shuaiplus
2026-07-02 16:03:49 +08:00
parent ce3674669e
commit baf569983d
14 changed files with 182 additions and 199 deletions
+15 -15
View File
@@ -167,7 +167,7 @@ export async function handleCreateAttachment(
const storage = new StorageService(env.DB);
// Verify cipher exists and belongs to user
const cipher = await storage.getCipher(cipherId);
const cipher = await storage.getCipherForUser(cipherId, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
}
@@ -205,7 +205,7 @@ export async function handleCreateAttachment(
await storage.saveAttachment(attachment);
// Add attachment to cipher
await storage.addAttachmentToCipher(cipherId, attachmentId);
await storage.addAttachmentToCipherForUser(cipherId, attachmentId, userId);
// Update cipher revision date
const revisionInfo = await storage.updateCipherRevisionDate(cipherId);
@@ -215,7 +215,7 @@ export async function handleCreateAttachment(
}
// Get updated cipher for response
const updatedCipher = await storage.getCipher(cipherId);
const updatedCipher = await storage.getCipherForUser(cipherId, userId);
const attachments = await storage.getAttachmentsByCipher(cipherId);
const jwtSecret = getSafeJwtSecret(env);
if (!jwtSecret) {
@@ -244,13 +244,13 @@ export async function handleUploadAttachment(
const storage = new StorageService(env.DB);
// Verify cipher exists and belongs to user
const cipher = await storage.getCipher(cipherId);
const cipher = await storage.getCipherForUser(cipherId, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
}
// Verify attachment exists
const attachment = await storage.getAttachment(attachmentId);
const attachment = await storage.getAttachmentForUser(attachmentId, userId);
if (!attachment || attachment.cipherId !== cipherId) {
return errorResponse('Attachment not found', 404);
}
@@ -283,12 +283,12 @@ export async function handlePublicUploadAttachment(
}
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(cipherId);
const cipher = await storage.getCipherForUser(cipherId, claims.userId);
if (!cipher || cipher.userId !== claims.userId) {
return errorResponse('Cipher not found', 404);
}
const attachment = await storage.getAttachment(attachmentId);
const attachment = await storage.getAttachmentForUser(attachmentId, claims.userId);
if (!attachment || attachment.cipherId !== cipherId) {
return errorResponse('Attachment not found', 404);
}
@@ -308,13 +308,13 @@ export async function handleGetAttachment(
const storage = new StorageService(env.DB);
// Verify cipher exists and belongs to user
const cipher = await storage.getCipher(cipherId);
const cipher = await storage.getCipherForUser(cipherId, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
}
// Verify attachment exists
const attachment = await storage.getAttachment(attachmentId);
const attachment = await storage.getAttachmentForUser(attachmentId, userId);
if (!attachment || attachment.cipherId !== cipherId) {
return errorResponse('Attachment not found', 404);
}
@@ -349,12 +349,12 @@ export async function handleUpdateAttachmentMetadata(
): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(cipherId);
const cipher = await storage.getCipherForUser(cipherId, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
}
const attachment = await storage.getAttachment(attachmentId);
const attachment = await storage.getAttachmentForUser(attachmentId, userId);
if (!attachment || attachment.cipherId !== cipherId) {
return errorResponse('Attachment not found', 404);
}
@@ -471,13 +471,13 @@ export async function handleDeleteAttachment(
const storage = new StorageService(env.DB);
// Verify cipher exists and belongs to user
const cipher = await storage.getCipher(cipherId);
const cipher = await storage.getCipherForUser(cipherId, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
}
// Verify attachment exists
const attachment = await storage.getAttachment(attachmentId);
const attachment = await storage.getAttachmentForUser(attachmentId, userId);
if (!attachment || attachment.cipherId !== cipherId) {
return errorResponse('Attachment not found', 404);
}
@@ -486,7 +486,7 @@ export async function handleDeleteAttachment(
await deleteBlobObject(env, path);
// Delete attachment metadata
await storage.deleteAttachment(attachmentId);
await storage.deleteAttachmentForUser(attachmentId, userId);
// Update cipher revision date
const revisionInfo = await storage.updateCipherRevisionDate(cipherId);
@@ -501,7 +501,7 @@ export async function handleDeleteAttachment(
}
// Get updated cipher for response
const updatedCipher = await storage.getCipher(cipherId);
const updatedCipher = await storage.getCipherForUser(cipherId, userId);
const attachments = await storage.getAttachmentsByCipher(cipherId);
const cipherResponse = cipherToResponse(updatedCipher!, attachments);
+3 -3
View File
@@ -201,7 +201,7 @@ export async function handleCreateAuthRequest(request: Request, env: Env): Promi
export async function handleGetAuthRequest(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const authRequest = await storage.getAuthRequestById(id);
const authRequest = await storage.getAuthRequestByIdForUser(id, userId);
if (!authRequest || authRequest.userId !== userId) return errorResponse('Not found', 404);
return jsonResponse(toAuthRequestResponse(request, authRequest));
}
@@ -239,7 +239,7 @@ export async function handleUpdateAuthRequest(request: Request, env: Env, userId
const body = await readJsonBody(request);
if (!body) return errorResponse('Invalid request payload', 400);
const authRequest = await storage.getAuthRequestById(id);
const authRequest = await storage.getAuthRequestByIdForUser(id, userId);
if (!authRequest || authRequest.userId !== userId || isAuthRequestExpired(authRequest)) {
return errorResponse('Not found', 404);
}
@@ -275,7 +275,7 @@ export async function handleUpdateAuthRequest(request: Request, env: Env, userId
masterPasswordHash,
});
if (!updated) return errorResponse('Auth request has already been answered.', 409);
const updatedRequest = await storage.getAuthRequestById(id);
const updatedRequest = await storage.getAuthRequestByIdForUser(id, userId);
// Match Bitwarden upstream behavior: only approval wakes the originating anonymous
// client. Denials are not pushed to avoid leaking that a login attempt was rejected.
if (approved) {
+11 -11
View File
@@ -812,7 +812,7 @@ export async function handleGetCiphers(request: Request, env: Env, userId: strin
// GET /api/ciphers/:id
export async function handleGetCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -827,8 +827,8 @@ export async function handleGetCipher(request: Request, env: Env, userId: string
async function verifyFolderOwnership(storage: StorageService, folderId: string | null | undefined, userId: string): Promise<boolean> {
if (!folderId) return true;
const folder = await storage.getFolder(folderId);
return !!(folder && folder.userId === userId);
const folder = await storage.getFolderForUser(folderId, userId);
return !!folder;
}
// POST /api/ciphers
@@ -909,7 +909,7 @@ export async function handleCreateCipher(request: Request, env: Env, userId: str
// PUT /api/ciphers/:id
export async function handleUpdateCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const existingCipher = await storage.getCipher(id);
const existingCipher = await storage.getCipherForUser(id, userId);
if (!existingCipher || existingCipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1020,7 +1020,7 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
// DELETE /api/ciphers/:id
export async function handleDeleteCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1052,7 +1052,7 @@ export async function handleDeleteCipher(request: Request, env: Env, userId: str
// - If item is already soft-deleted -> hard delete.
export async function handleDeleteCipherCompat(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1079,7 +1079,7 @@ export async function handleDeleteCipherCompat(request: Request, env: Env, userI
// DELETE /api/ciphers/:id (permanent)
export async function handlePermanentDeleteCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1104,7 +1104,7 @@ export async function handlePermanentDeleteCipher(request: Request, env: Env, us
// PUT /api/ciphers/:id/restore
export async function handleRestoreCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1126,7 +1126,7 @@ export async function handleRestoreCipher(request: Request, env: Env, userId: st
// PUT /api/ciphers/:id/partial - Update only favorite/folderId
export async function handlePartialUpdateCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1218,7 +1218,7 @@ function parseCipherIdList(body: { ids?: unknown }): string[] | null {
// PUT/POST /api/ciphers/:id/archive
export async function handleArchiveCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
@@ -1244,7 +1244,7 @@ export async function handleArchiveCipher(request: Request, env: Env, userId: st
// PUT/POST /api/ciphers/:id/unarchive
export async function handleUnarchiveCipher(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const cipher = await storage.getCipher(id);
const cipher = await storage.getCipherForUser(id, userId);
if (!cipher || cipher.userId !== userId) {
return errorResponse('Cipher not found', 404);
+5 -5
View File
@@ -80,7 +80,7 @@ export async function handleGetFolders(request: Request, env: Env, userId: strin
// GET /api/folders/:id
export async function handleGetFolder(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const folder = await storage.getFolder(id);
const folder = await storage.getFolderForUser(id, userId);
if (!folder || folder.userId !== userId) {
return errorResponse('Folder not found', 404);
@@ -129,7 +129,7 @@ export async function handleCreateFolder(request: Request, env: Env, userId: str
// PUT /api/folders/:id
export async function handleUpdateFolder(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const folder = await storage.getFolder(id);
const folder = await storage.getFolderForUser(id, userId);
if (!folder || folder.userId !== userId) {
return errorResponse('Folder not found', 404);
@@ -163,7 +163,7 @@ export async function handleUpdateFolder(request: Request, env: Env, userId: str
// DELETE /api/folders/:id
export async function handleDeleteFolder(request: Request, env: Env, userId: string, id: string): Promise<Response> {
const storage = new StorageService(env.DB);
const folder = await storage.getFolder(id);
const folder = await storage.getFolderForUser(id, userId);
if (!folder || folder.userId !== userId) {
return errorResponse('Folder not found', 404);
@@ -204,8 +204,8 @@ export async function handleBulkDeleteFolders(request: Request, env: Env, userId
const folders = (
await Promise.all(ids.map(async (id) => {
const folder = await storage.getFolder(id);
return folder && folder.userId === userId ? folder : null;
const folder = await storage.getFolderForUser(id, userId);
return folder;
}))
).filter((folder): folder is Folder => !!folder);
const revisionDate = await storage.bulkDeleteFolders(ids, userId);
+1 -1
View File
@@ -341,7 +341,7 @@ export async function handleToken(request: Request, env: Env): Promise<Response>
let valid = false;
const normalizedAuthRequestId = String(authRequestId || '').trim();
if (normalizedAuthRequestId) {
const authRequest = await storage.getAuthRequestById(normalizedAuthRequestId);
const authRequest = await storage.getAuthRequestByIdForUser(normalizedAuthRequestId, user.id);
valid = !!(
authRequest &&
authRequest.userId === user.id &&
+8 -8
View File
@@ -134,7 +134,7 @@ export async function handleGetSends(request: Request, env: Env, userId: string)
export async function handleGetSend(request: Request, env: Env, userId: string, sendId: string): Promise<Response> {
void request;
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found', 404);
@@ -401,7 +401,7 @@ export async function handleGetSendFileUpload(
): Promise<Response> {
void request;
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found', 404);
}
@@ -436,7 +436,7 @@ export async function handleUploadSendFile(
fileId: string
): Promise<Response> {
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found. Unable to save the file.', 404);
}
@@ -472,7 +472,7 @@ export async function handlePublicUploadSendFile(
}
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, claims.userId);
if (!send || send.userId !== claims.userId) {
return errorResponse('Send not found. Unable to save the file.', 404);
}
@@ -485,7 +485,7 @@ export async function handlePublicUploadSendFile(
export async function handleUpdateSend(request: Request, env: Env, userId: string, sendId: string): Promise<Response> {
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found', 404);
}
@@ -632,7 +632,7 @@ export async function handleUpdateSend(request: Request, env: Env, userId: strin
export async function handleDeleteSend(request: Request, env: Env, userId: string, sendId: string): Promise<Response> {
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found', 404);
}
@@ -698,7 +698,7 @@ export async function handleBulkDeleteSends(request: Request, env: Env, userId:
export async function handleRemoveSendPassword(request: Request, env: Env, userId: string, sendId: string): Promise<Response> {
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found', 404);
}
@@ -719,7 +719,7 @@ export async function handleRemoveSendPassword(request: Request, env: Env, userI
export async function handleRemoveSendAuth(request: Request, env: Env, userId: string, sendId: string): Promise<Response> {
const storage = new StorageService(env.DB);
const send = await storage.getSend(sendId);
const send = await storage.getSendForUser(sendId, userId);
if (!send || send.userId !== userId) {
return errorResponse('Send not found', 404);
}
+64 -1
View File
@@ -22,10 +22,35 @@ export async function getAttachment(db: D1Database, id: string): Promise<Attachm
};
}
export async function getAttachmentForUser(db: D1Database, id: string, userId: string): Promise<Attachment | null> {
const row = await db
.prepare(
`SELECT a.id, a.cipher_id, a.file_name, a.size, a.size_name, a.key
FROM attachments a
INNER JOIN ciphers c ON c.id = a.cipher_id
WHERE a.id = ? AND c.user_id = ?`
)
.bind(id, userId)
.first<any>();
if (!row) return null;
return {
id: row.id,
cipherId: row.cipher_id,
fileName: row.file_name,
size: row.size,
sizeName: row.size_name,
key: row.key,
};
}
export async function saveAttachment(db: D1Database, safeBind: SafeBind, attachment: Attachment): Promise<void> {
const stmt = db.prepare(
'INSERT INTO attachments(id, cipher_id, file_name, size, size_name, key) VALUES(?, ?, ?, ?, ?, ?) ' +
'ON CONFLICT(id) DO UPDATE SET cipher_id=excluded.cipher_id, file_name=excluded.file_name, size=excluded.size, size_name=excluded.size_name, key=excluded.key'
'ON CONFLICT(id) DO UPDATE SET cipher_id=excluded.cipher_id, file_name=excluded.file_name, size=excluded.size, size_name=excluded.size_name, key=excluded.key ' +
'WHERE EXISTS (' +
'SELECT 1 FROM ciphers current_cipher INNER JOIN ciphers next_cipher ON next_cipher.id = excluded.cipher_id ' +
'WHERE current_cipher.id = attachments.cipher_id AND current_cipher.user_id = next_cipher.user_id' +
')'
);
await safeBind(stmt, attachment.id, attachment.cipherId, attachment.fileName, attachment.size, attachment.sizeName, attachment.key).run();
}
@@ -34,6 +59,20 @@ export async function deleteAttachment(db: D1Database, id: string): Promise<void
await db.prepare('DELETE FROM attachments WHERE id = ?').bind(id).run();
}
export async function deleteAttachmentForUser(db: D1Database, id: string, userId: string): Promise<void> {
await db
.prepare(
`DELETE FROM attachments
WHERE id = ?
AND EXISTS (
SELECT 1 FROM ciphers c
WHERE c.id = attachments.cipher_id AND c.user_id = ?
)`
)
.bind(id, userId)
.run();
}
export async function bulkDeleteAttachmentsByIds(
db: D1Database,
sqlChunkSize: SqlChunkSize,
@@ -135,6 +174,30 @@ export async function addAttachmentToCipher(db: D1Database, cipherId: string, at
await db.prepare('UPDATE attachments SET cipher_id = ? WHERE id = ?').bind(cipherId, attachmentId).run();
}
export async function addAttachmentToCipherForUser(
db: D1Database,
cipherId: string,
attachmentId: string,
userId: string
): Promise<void> {
await db
.prepare(
`UPDATE attachments
SET cipher_id = ?
WHERE id = ?
AND EXISTS (
SELECT 1 FROM ciphers target_cipher
WHERE target_cipher.id = ? AND target_cipher.user_id = ?
)
AND EXISTS (
SELECT 1 FROM ciphers current_cipher
WHERE current_cipher.id = attachments.cipher_id AND current_cipher.user_id = ?
)`
)
.bind(cipherId, attachmentId, cipherId, userId, userId)
.run();
}
export async function deleteAllAttachmentsByCipher(db: D1Database, cipherId: string): Promise<void> {
await db.prepare('DELETE FROM attachments WHERE cipher_id = ?').bind(cipherId).run();
}
@@ -68,6 +68,11 @@ export async function getAuthRequestById(db: D1Database, id: string): Promise<Au
return row ? mapAuthRequestRow(row) : null;
}
export async function getAuthRequestByIdForUser(db: D1Database, id: string, userId: string): Promise<AuthRequestRecord | null> {
const row = await db.prepare(`${AUTH_REQUEST_SELECT} WHERE id = ? AND user_id = ? LIMIT 1`).bind(id, userId).first<any>();
return row ? mapAuthRequestRow(row) : null;
}
export async function listAuthRequestsByUserId(db: D1Database, userId: string): Promise<AuthRequestRecord[]> {
const res = await db.prepare(`${AUTH_REQUEST_SELECT} WHERE user_id = ? ORDER BY creation_date DESC`).bind(userId).all<any>();
return (res.results || []).map(mapAuthRequestRow);
+10 -1
View File
@@ -107,6 +107,14 @@ export async function getCipher(db: D1Database, id: string): Promise<Cipher | nu
return parseCipherRow(row);
}
export async function getCipherForUser(db: D1Database, id: string, userId: string): Promise<Cipher | null> {
const row = await db
.prepare(`SELECT ${selectCipherColumns()} FROM ciphers WHERE id = ? AND user_id = ?`)
.bind(id, userId)
.first<CipherRow>();
return parseCipherRow(row);
}
export async function saveCipher(db: D1Database, safeBind: SafeBind, cipher: Cipher): Promise<void> {
const folderId = normalizeOptionalId(cipher.folderId);
const data = buildCipherData(cipher, folderId);
@@ -114,7 +122,8 @@ export async function saveCipher(db: D1Database, safeBind: SafeBind, cipher: Cip
'INSERT INTO ciphers(id, user_id, type, folder_id, name, notes, favorite, data, reprompt, key, created_at, updated_at, archived_at, deleted_at) ' +
'VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ' +
'ON CONFLICT(id) DO UPDATE SET ' +
'user_id=excluded.user_id, type=excluded.type, folder_id=excluded.folder_id, name=excluded.name, notes=excluded.notes, favorite=excluded.favorite, data=excluded.data, reprompt=excluded.reprompt, key=excluded.key, updated_at=excluded.updated_at, archived_at=excluded.archived_at, deleted_at=excluded.deleted_at'
'type=excluded.type, folder_id=excluded.folder_id, name=excluded.name, notes=excluded.notes, favorite=excluded.favorite, data=excluded.data, reprompt=excluded.reprompt, key=excluded.key, updated_at=excluded.updated_at, archived_at=excluded.archived_at, deleted_at=excluded.deleted_at ' +
'WHERE user_id=excluded.user_id'
);
await safeBind(
stmt,
+10 -1
View File
@@ -19,11 +19,20 @@ export async function getFolder(db: D1Database, id: string): Promise<Folder | nu
return mapFolderRow(row);
}
export async function getFolderForUser(db: D1Database, id: string, userId: string): Promise<Folder | null> {
const row = await db
.prepare('SELECT id, user_id, name, created_at, updated_at FROM folders WHERE id = ? AND user_id = ?')
.bind(id, userId)
.first<any>();
if (!row) return null;
return mapFolderRow(row);
}
export async function saveFolder(db: D1Database, folder: Folder): Promise<void> {
await db
.prepare(
'INSERT INTO folders(id, user_id, name, created_at, updated_at) VALUES(?, ?, ?, ?, ?) ' +
'ON CONFLICT(id) DO UPDATE SET user_id=excluded.user_id, name=excluded.name, updated_at=excluded.updated_at'
'ON CONFLICT(id) DO UPDATE SET name=excluded.name, updated_at=excluded.updated_at WHERE user_id=excluded.user_id'
)
.bind(folder.id, folder.userId, folder.name, folder.createdAt, folder.updatedAt)
.run();
+14 -2
View File
@@ -40,15 +40,27 @@ export async function getSend(db: D1Database, id: string): Promise<Send | null>
return mapSendRow(row);
}
export async function getSendForUser(db: D1Database, id: string, userId: string): Promise<Send | null> {
const row = await db
.prepare(
'SELECT id, user_id, type, name, notes, data, key, password_hash, password_salt, password_iterations, auth_type, emails, max_access_count, access_count, disabled, hide_email, created_at, updated_at, expiration_date, deletion_date FROM sends WHERE id = ? AND user_id = ?'
)
.bind(id, userId)
.first<any>();
if (!row) return null;
return mapSendRow(row);
}
export async function saveSend(db: D1Database, safeBind: SafeBind, send: Send): Promise<void> {
const stmt = db.prepare(
'INSERT INTO sends(id, user_id, type, name, notes, data, key, password_hash, password_salt, password_iterations, auth_type, emails, max_access_count, access_count, disabled, hide_email, created_at, updated_at, expiration_date, deletion_date) ' +
'VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ' +
'ON CONFLICT(id) DO UPDATE SET ' +
'user_id=excluded.user_id, type=excluded.type, name=excluded.name, notes=excluded.notes, data=excluded.data, key=excluded.key, ' +
'type=excluded.type, name=excluded.name, notes=excluded.notes, data=excluded.data, key=excluded.key, ' +
'password_hash=excluded.password_hash, password_salt=excluded.password_salt, password_iterations=excluded.password_iterations, auth_type=excluded.auth_type, emails=excluded.emails, ' +
'max_access_count=excluded.max_access_count, access_count=excluded.access_count, disabled=excluded.disabled, hide_email=excluded.hide_email, ' +
'updated_at=excluded.updated_at, expiration_date=excluded.expiration_date, deletion_date=excluded.deletion_date'
'updated_at=excluded.updated_at, expiration_date=excluded.expiration_date, deletion_date=excluded.deletion_date ' +
'WHERE user_id=excluded.user_id'
);
await safeBind(
+35
View File
@@ -41,6 +41,7 @@ import {
deleteFolder as deleteStoredFolder,
getAllFolders as listStoredFolders,
getFolder as findStoredFolder,
getFolderForUser as findStoredFolderForUser,
getFoldersPage as listStoredFoldersPage,
saveFolder as saveStoredFolder,
} from './storage-folder-repo';
@@ -53,6 +54,7 @@ import {
bulkUnarchiveCiphers as unarchiveStoredCiphers,
getAllCiphers as listStoredCiphers,
getCipher as findStoredCipher,
getCipherForUser as findStoredCipherForUser,
getCiphersByIds as listStoredCiphersByIds,
getCiphersPage as listStoredCiphersPage,
saveCipher as saveStoredCipher,
@@ -60,10 +62,13 @@ import {
} from './storage-cipher-repo';
import {
addAttachmentToCipher as attachStoredAttachmentToCipher,
addAttachmentToCipherForUser as attachStoredAttachmentToCipherForUser,
bulkDeleteAttachmentsByIds as deleteStoredAttachmentsByIds,
deleteAllAttachmentsByCipher as deleteStoredAttachmentsByCipher,
deleteAttachment as deleteStoredAttachment,
deleteAttachmentForUser as deleteStoredAttachmentForUser,
getAttachment as findStoredAttachment,
getAttachmentForUser as findStoredAttachmentForUser,
getAttachmentsByCipher as listStoredAttachmentsByCipher,
getAttachmentsByCipherIds as listStoredAttachmentsByCipherIds,
getAttachmentsByUserId as listStoredAttachmentsByUserId,
@@ -75,6 +80,7 @@ import {
deleteSend as deleteStoredSend,
getAllSends as listStoredSends,
getSend as findStoredSend,
getSendForUser as findStoredSendForUser,
getSendsByIds as listStoredSendsByIds,
getSendsPage as listStoredSendsPage,
incrementSendAccessCount as incrementStoredSendAccessCount,
@@ -114,6 +120,7 @@ import {
import {
createAuthRequest as createStoredAuthRequest,
getAuthRequestById as findStoredAuthRequestById,
getAuthRequestByIdForUser as findStoredAuthRequestByIdForUser,
listAuthRequestsByUserId as listStoredAuthRequestsByUserId,
listPendingAuthRequestsByUserId as listStoredPendingAuthRequestsByUserId,
markAuthRequestAuthenticated as markStoredAuthRequestAuthenticated,
@@ -458,6 +465,10 @@ export class StorageService {
return findStoredCipher(this.db, id);
}
async getCipherForUser(id: string, userId: string): Promise<Cipher | null> {
return findStoredCipherForUser(this.db, id, userId);
}
async saveCipher(cipher: Cipher): Promise<void> {
await saveStoredCipher(this.db, this.safeBind.bind(this), cipher);
}
@@ -508,6 +519,10 @@ export class StorageService {
return findStoredFolder(this.db, id);
}
async getFolderForUser(id: string, userId: string): Promise<Folder | null> {
return findStoredFolderForUser(this.db, id, userId);
}
async saveFolder(folder: Folder): Promise<void> {
await saveStoredFolder(this.db, folder);
}
@@ -546,6 +561,10 @@ export class StorageService {
return findStoredAttachment(this.db, id);
}
async getAttachmentForUser(id: string, userId: string): Promise<Attachment | null> {
return findStoredAttachmentForUser(this.db, id, userId);
}
async saveAttachment(attachment: Attachment): Promise<void> {
await saveStoredAttachment(this.db, this.safeBind.bind(this), attachment);
}
@@ -554,6 +573,10 @@ export class StorageService {
await deleteStoredAttachment(this.db, id);
}
async deleteAttachmentForUser(id: string, userId: string): Promise<void> {
await deleteStoredAttachmentForUser(this.db, id, userId);
}
async bulkDeleteAttachmentsByIds(ids: string[]): Promise<void> {
await deleteStoredAttachmentsByIds(this.db, this.sqlChunkSize.bind(this), ids);
}
@@ -574,6 +597,10 @@ export class StorageService {
await attachStoredAttachmentToCipher(this.db, cipherId, attachmentId);
}
async addAttachmentToCipherForUser(cipherId: string, attachmentId: string, userId: string): Promise<void> {
await attachStoredAttachmentToCipherForUser(this.db, cipherId, attachmentId, userId);
}
async deleteAllAttachmentsByCipher(cipherId: string): Promise<void> {
await deleteStoredAttachmentsByCipher(this.db, cipherId);
}
@@ -634,6 +661,10 @@ export class StorageService {
return findStoredSend(this.db, id);
}
async getSendForUser(id: string, userId: string): Promise<Send | null> {
return findStoredSendForUser(this.db, id, userId);
}
async saveSend(send: Send): Promise<void> {
await saveStoredSend(this.db, this.safeBind.bind(this), send);
}
@@ -783,6 +814,10 @@ export class StorageService {
return findStoredAuthRequestById(this.db, id);
}
async getAuthRequestByIdForUser(id: string, userId: string): Promise<AuthRequestRecord | null> {
return findStoredAuthRequestByIdForUser(this.db, id, userId);
}
async listAuthRequestsByUserId(userId: string): Promise<AuthRequestRecord[]> {
return listStoredAuthRequestsByUserId(this.db, userId);
}