mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 06:43:35 +00:00
* 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>
191 lines
6.2 KiB
TypeScript
191 lines
6.2 KiB
TypeScript
import { Component, OnInit } from "@angular/core";
|
|
import { ActivatedRoute } from "@angular/router";
|
|
|
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
|
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
|
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
|
import { SendApiService } from "@bitwarden/common/abstractions/send/send-api.service.abstraction";
|
|
import { SEND_KDF_ITERATIONS } from "@bitwarden/common/enums/kdfType";
|
|
import { SendType } from "@bitwarden/common/enums/sendType";
|
|
import { Utils } from "@bitwarden/common/misc/utils";
|
|
import { EncArrayBuffer } from "@bitwarden/common/models/domain/enc-array-buffer";
|
|
import { SendAccess } from "@bitwarden/common/models/domain/send-access";
|
|
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
|
|
import { SendAccessRequest } from "@bitwarden/common/models/request/send-access.request";
|
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
|
import { SendAccessResponse } from "@bitwarden/common/models/response/send-access.response";
|
|
import { SendAccessView } from "@bitwarden/common/models/view/send-access.view";
|
|
|
|
@Component({
|
|
selector: "app-send-access",
|
|
templateUrl: "access.component.html",
|
|
})
|
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
export class AccessComponent implements OnInit {
|
|
send: SendAccessView;
|
|
sendType = SendType;
|
|
downloading = false;
|
|
loading = true;
|
|
passwordRequired = false;
|
|
formPromise: Promise<SendAccessResponse>;
|
|
password: string;
|
|
showText = false;
|
|
unavailable = false;
|
|
error = false;
|
|
hideEmail = false;
|
|
|
|
private id: string;
|
|
private key: string;
|
|
private decKey: SymmetricCryptoKey;
|
|
private accessRequest: SendAccessRequest;
|
|
|
|
constructor(
|
|
private i18nService: I18nService,
|
|
private cryptoFunctionService: CryptoFunctionService,
|
|
private apiService: ApiService,
|
|
private platformUtilsService: PlatformUtilsService,
|
|
private route: ActivatedRoute,
|
|
private cryptoService: CryptoService,
|
|
private fileDownloadService: FileDownloadService,
|
|
private sendApiService: SendApiService
|
|
) {}
|
|
|
|
get sendText() {
|
|
if (this.send == null || this.send.text == null) {
|
|
return null;
|
|
}
|
|
return this.showText ? this.send.text.text : this.send.text.maskedText;
|
|
}
|
|
|
|
get expirationDate() {
|
|
if (this.send == null || this.send.expirationDate == null) {
|
|
return null;
|
|
}
|
|
return this.send.expirationDate;
|
|
}
|
|
|
|
get creatorIdentifier() {
|
|
if (this.send == null || this.send.creatorIdentifier == null) {
|
|
return null;
|
|
}
|
|
return this.send.creatorIdentifier;
|
|
}
|
|
|
|
ngOnInit() {
|
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
|
this.route.params.subscribe(async (params) => {
|
|
this.id = params.sendId;
|
|
this.key = params.key;
|
|
if (this.key == null || this.id == null) {
|
|
return;
|
|
}
|
|
await this.load();
|
|
});
|
|
}
|
|
|
|
async download() {
|
|
if (this.send == null || this.decKey == null) {
|
|
return;
|
|
}
|
|
|
|
if (this.downloading) {
|
|
return;
|
|
}
|
|
|
|
const downloadData = await this.sendApiService.getSendFileDownloadData(
|
|
this.send,
|
|
this.accessRequest
|
|
);
|
|
|
|
if (Utils.isNullOrWhitespace(downloadData.url)) {
|
|
this.platformUtilsService.showToast("error", null, this.i18nService.t("missingSendFile"));
|
|
return;
|
|
}
|
|
|
|
this.downloading = true;
|
|
const response = await fetch(new Request(downloadData.url, { cache: "no-store" }));
|
|
if (response.status !== 200) {
|
|
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
|
|
this.downloading = false;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const encBuf = await EncArrayBuffer.fromResponse(response);
|
|
const decBuf = await this.cryptoService.decryptFromBytes(encBuf, this.decKey);
|
|
this.fileDownloadService.download({
|
|
fileName: this.send.file.fileName,
|
|
blobData: decBuf,
|
|
downloadMethod: "save",
|
|
});
|
|
} catch (e) {
|
|
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
|
|
}
|
|
|
|
this.downloading = false;
|
|
}
|
|
|
|
copyText() {
|
|
this.platformUtilsService.copyToClipboard(this.send.text.text);
|
|
this.platformUtilsService.showToast(
|
|
"success",
|
|
null,
|
|
this.i18nService.t("valueCopied", this.i18nService.t("sendTypeText"))
|
|
);
|
|
}
|
|
|
|
toggleText() {
|
|
this.showText = !this.showText;
|
|
}
|
|
|
|
async load() {
|
|
this.unavailable = false;
|
|
this.error = false;
|
|
this.hideEmail = false;
|
|
const keyArray = Utils.fromUrlB64ToArray(this.key);
|
|
this.accessRequest = new SendAccessRequest();
|
|
if (this.password != null) {
|
|
const passwordHash = await this.cryptoFunctionService.pbkdf2(
|
|
this.password,
|
|
keyArray,
|
|
"sha256",
|
|
SEND_KDF_ITERATIONS
|
|
);
|
|
this.accessRequest.password = Utils.fromBufferToB64(passwordHash);
|
|
}
|
|
try {
|
|
let sendResponse: SendAccessResponse = null;
|
|
if (this.loading) {
|
|
sendResponse = await this.sendApiService.postSendAccess(this.id, this.accessRequest);
|
|
} else {
|
|
this.formPromise = this.sendApiService.postSendAccess(this.id, this.accessRequest);
|
|
sendResponse = await this.formPromise;
|
|
}
|
|
this.passwordRequired = false;
|
|
const sendAccess = new SendAccess(sendResponse);
|
|
this.decKey = await this.cryptoService.makeSendKey(keyArray);
|
|
this.send = await sendAccess.decrypt(this.decKey);
|
|
this.showText = this.send.text != null ? !this.send.text.hidden : true;
|
|
} catch (e) {
|
|
if (e instanceof ErrorResponse) {
|
|
if (e.statusCode === 401) {
|
|
this.passwordRequired = true;
|
|
} else if (e.statusCode === 404) {
|
|
this.unavailable = true;
|
|
} else {
|
|
this.error = true;
|
|
}
|
|
}
|
|
}
|
|
this.loading = false;
|
|
this.hideEmail =
|
|
this.creatorIdentifier == null &&
|
|
!this.passwordRequired &&
|
|
!this.loading &&
|
|
!this.unavailable;
|
|
}
|
|
}
|