1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00
Files
browser/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts
Jason Ng 7243d62b19 [PM-9627] item details v2 defects (#10082)
* Pull proper item name in item details. add Typography Module to reusable view sections
2024-07-11 18:48:47 -04:00

156 lines
4.9 KiB
TypeScript

import { CommonModule } from "@angular/common";
import { Component, Input } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { NEVER, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { StateProvider } from "@bitwarden/common/platform/state";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
ToastService,
ItemModule,
IconButtonModule,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
} from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
@Component({
selector: "app-attachments-v2",
templateUrl: "attachments-v2.component.html",
standalone: true,
imports: [
CommonModule,
JslibModule,
ItemModule,
IconButtonModule,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
],
})
export class AttachmentsV2Component {
@Input() cipher: CipherView;
canAccessPremium: boolean;
orgKey: OrgKey;
private passwordReprompted = false;
constructor(
private passwordRepromptService: PasswordRepromptService,
private i18nService: I18nService,
private apiService: ApiService,
private fileDownloadService: FileDownloadService,
private cryptoService: CryptoService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
private toastService: ToastService,
private stateProvider: StateProvider,
private encryptService: EncryptService,
) {
this.subscribeToHasPremiumCheck();
this.subscribeToOrgKey();
}
subscribeToHasPremiumCheck() {
this.billingAccountProfileStateService.hasPremiumFromAnySource$
.pipe(takeUntilDestroyed())
.subscribe((data) => {
this.canAccessPremium = data;
});
}
subscribeToOrgKey() {
this.stateProvider.activeUserId$
.pipe(
switchMap((userId) => (userId != null ? this.cryptoService.orgKeys$(userId) : NEVER)),
takeUntilDestroyed(),
)
.subscribe((data: Record<OrganizationId, OrgKey> | null) => {
if (data) {
this.orgKey = data[this.cipher.organizationId as OrganizationId];
}
});
}
async downloadAttachment(attachment: any) {
this.passwordReprompted =
this.passwordReprompted ||
(await this.passwordRepromptService.passwordRepromptCheck(this.cipher));
if (!this.passwordReprompted) {
return;
}
const file = attachment as any;
if (file.downloading) {
return;
}
if (this.cipher.organizationId == null && !this.canAccessPremium) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("premiumRequired"),
message: this.i18nService.t("premiumRequiredDesc"),
});
return;
}
let url: string;
try {
const attachmentDownloadResponse = await this.apiService.getAttachmentData(
this.cipher.id,
attachment.id,
);
url = attachmentDownloadResponse.url;
} catch (e) {
if (e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) {
url = attachment.url;
} else if (e instanceof ErrorResponse) {
throw new Error((e as ErrorResponse).getSingleMessage());
} else {
throw e;
}
}
file.downloading = true;
const response = await fetch(new Request(url, { cache: "no-store" }));
if (response.status !== 200) {
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
file.downloading = false;
return;
}
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
const key = attachment.key != null ? attachment.key : this.orgKey;
const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
this.fileDownloadService.download({
fileName: attachment.fileName,
blobData: decBuf,
});
} catch (e) {
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
file.downloading = false;
}
}