mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 09:13:33 +00:00
[SG 623] Send Service Refactor (#4327)
* Split out api methods into sendApiService * Move SendService and abstraction * Libs updates * Web updates * CLI updates * Desktop updates * libs send service fixes * browser factory additions * Browser updates * Fix service injection for CLI SendReceiveCommand * Deprecate directly calling send state service methods * SendService observables updates * Update components to use new observables * Modify CLI to use state service instead of observables * Remove unnecessary await on get() * Move delete() to InternalSendService * SendService unit tests * Split fileUploadService by send and cipher * send and cipher service factory updates * Add file upload methods to get around circular dependency issues * Move api methods from sendService to sendApiService * Update cipherService to use fileApi methods * libs service injection and component changes * browser service injection and component changes * Desktop component changes * Web component changes * cipher service test fix * Fix file capitalization * CLI service import and command updates * Remove extra abstract fileUploadService * WIP: Condense callbacks for file upload Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> * Send callbacks for file upload * Fix circular service dependencies * Fix response return on upload * Fix function definitions * Service injection fixes and bug fixes * Fix folder casing * Service injection cleanup * Remove deleted file from capital letters whitelist * Create new SendApiService for popup * Move cipherFileUploadService to vault * Move SendFileUploadService methods into SendApiService * Rename methods to remove 'WithServer' * Properly subscribe to sendViews * Fix Send serialization * Implement fromJSON on sendFile and sendText * [PM-1347] Fix send key serialization (#4989) * Properly serialize key on send fromJSON * Remove call that nulled out decrypted sends * Fix null checks in fromJSON methods for models * lint fixes --------- Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import { ApiService } from "../../../abstractions/api.service";
|
||||
import {
|
||||
FileUploadApiMethods,
|
||||
FileUploadService,
|
||||
} from "../../../abstractions/file-upload/file-upload.service";
|
||||
import { Utils } from "../../../misc/utils";
|
||||
import { EncArrayBuffer } from "../../../models/domain/enc-array-buffer";
|
||||
import { EncString } from "../../../models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../models/domain/symmetric-crypto-key";
|
||||
import { ErrorResponse } from "../../../models/response/error.response";
|
||||
import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "../../abstractions/file-upload/cipher-file-upload.service";
|
||||
import { Cipher } from "../../models/domain/cipher";
|
||||
import { AttachmentRequest } from "../../models/request/attachment.request";
|
||||
import { AttachmentUploadDataResponse } from "../../models/response/attachment-upload-data.response";
|
||||
import { CipherResponse } from "../../models/response/cipher.response";
|
||||
|
||||
export class CipherFileUploadService implements CipherFileUploadServiceAbstraction {
|
||||
constructor(private apiService: ApiService, private fileUploadService: FileUploadService) {}
|
||||
|
||||
async upload(
|
||||
cipher: Cipher,
|
||||
encFileName: EncString,
|
||||
encData: EncArrayBuffer,
|
||||
admin: boolean,
|
||||
dataEncKey: [SymmetricCryptoKey, EncString]
|
||||
): Promise<CipherResponse> {
|
||||
const request: AttachmentRequest = {
|
||||
key: dataEncKey[1].encryptedString,
|
||||
fileName: encFileName.encryptedString,
|
||||
fileSize: encData.buffer.byteLength,
|
||||
adminRequest: admin,
|
||||
};
|
||||
|
||||
let response: CipherResponse;
|
||||
try {
|
||||
const uploadDataResponse = await this.apiService.postCipherAttachment(cipher.id, request);
|
||||
response = admin ? uploadDataResponse.cipherMiniResponse : uploadDataResponse.cipherResponse;
|
||||
await this.fileUploadService.upload(
|
||||
uploadDataResponse,
|
||||
encFileName,
|
||||
encData,
|
||||
this.generateMethods(uploadDataResponse, response, request.adminRequest)
|
||||
);
|
||||
} catch (e) {
|
||||
if (
|
||||
(e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) ||
|
||||
(e as ErrorResponse).statusCode === 405
|
||||
) {
|
||||
response = await this.legacyServerAttachmentFileUpload(
|
||||
request.adminRequest,
|
||||
cipher.id,
|
||||
encFileName,
|
||||
encData,
|
||||
dataEncKey[1]
|
||||
);
|
||||
} else if (e instanceof ErrorResponse) {
|
||||
throw new Error((e as ErrorResponse).getSingleMessage());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private generateMethods(
|
||||
uploadData: AttachmentUploadDataResponse,
|
||||
response: CipherResponse,
|
||||
isAdmin: boolean
|
||||
): FileUploadApiMethods {
|
||||
return {
|
||||
postDirect: this.generatePostDirectCallback(uploadData, isAdmin),
|
||||
renewFileUploadUrl: this.generateRenewFileUploadUrlCallback(uploadData, response, isAdmin),
|
||||
rollback: this.generateRollbackCallback(response, uploadData, isAdmin),
|
||||
};
|
||||
}
|
||||
|
||||
private generatePostDirectCallback(uploadData: AttachmentUploadDataResponse, isAdmin: boolean) {
|
||||
return (data: FormData) => {
|
||||
const response = isAdmin ? uploadData.cipherMiniResponse : uploadData.cipherResponse;
|
||||
return this.apiService.postAttachmentFile(response.id, uploadData.attachmentId, data);
|
||||
};
|
||||
}
|
||||
|
||||
private generateRenewFileUploadUrlCallback(
|
||||
uploadData: AttachmentUploadDataResponse,
|
||||
response: CipherResponse,
|
||||
isAdmin: boolean
|
||||
) {
|
||||
return async () => {
|
||||
const renewResponse = await this.apiService.renewAttachmentUploadUrl(
|
||||
response.id,
|
||||
uploadData.attachmentId
|
||||
);
|
||||
return renewResponse?.url;
|
||||
};
|
||||
}
|
||||
|
||||
private generateRollbackCallback(
|
||||
response: CipherResponse,
|
||||
uploadData: AttachmentUploadDataResponse,
|
||||
isAdmin: boolean
|
||||
) {
|
||||
return () => {
|
||||
if (isAdmin) {
|
||||
return this.apiService.deleteCipherAttachmentAdmin(response.id, uploadData.attachmentId);
|
||||
} else {
|
||||
return this.apiService.deleteCipherAttachment(response.id, uploadData.attachmentId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads.
|
||||
* This method still exists for backward compatibility with old server versions.
|
||||
*/
|
||||
async legacyServerAttachmentFileUpload(
|
||||
admin: boolean,
|
||||
cipherId: string,
|
||||
encFileName: EncString,
|
||||
encData: EncArrayBuffer,
|
||||
key: EncString
|
||||
) {
|
||||
const fd = new FormData();
|
||||
try {
|
||||
const blob = new Blob([encData.buffer], { type: "application/octet-stream" });
|
||||
fd.append("key", key.encryptedString);
|
||||
fd.append("data", blob, encFileName.encryptedString);
|
||||
} catch (e) {
|
||||
if (Utils.isNode && !Utils.isBrowser) {
|
||||
fd.append("key", key.encryptedString);
|
||||
fd.append(
|
||||
"data",
|
||||
Buffer.from(encData.buffer) as any,
|
||||
{
|
||||
filepath: encFileName.encryptedString,
|
||||
contentType: "application/octet-stream",
|
||||
} as any
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
let response: CipherResponse;
|
||||
try {
|
||||
if (admin) {
|
||||
response = await this.apiService.postCipherAttachmentAdminLegacy(cipherId, fd);
|
||||
} else {
|
||||
response = await this.apiService.postCipherAttachmentLegacy(cipherId, fd);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error((e as ErrorResponse).getSingleMessage());
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user