diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts
index 0a60478c94d..db3e69f7d6f 100644
--- a/apps/desktop/src/app/app-routing.module.ts
+++ b/apps/desktop/src/app/app-routing.module.ts
@@ -17,7 +17,6 @@ import {
import { ChangePasswordComponent } from "@bitwarden/angular/auth/password-management/change-password";
import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component";
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
-import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route";
import {
LoginComponent,
LoginSecondaryContentComponent,
@@ -50,7 +49,6 @@ import { SetPasswordComponent } from "../auth/set-password.component";
import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
-import { VaultComponent } from "../vault/app/vault/vault.component";
import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component";
import { SendComponent } from "./tools/send/send.component";
@@ -102,15 +100,11 @@ const routes: Routes = [
},
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
- ...featureFlaggedRoute({
- defaultComponent: VaultComponent,
- flaggedComponent: VaultV2Component,
- featureFlag: FeatureFlag.PM18520_UpdateDesktopCipherForm,
- routeOptions: {
- path: "vault",
- canActivate: [authGuard],
- },
- }),
+ {
+ path: "vault",
+ component: VaultV2Component,
+ canActivate: [authGuard],
+ },
{ path: "set-password", component: SetPasswordComponent },
{
path: "send",
diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts
index 58c3e10e334..112732d8f2c 100644
--- a/apps/desktop/src/app/app.module.ts
+++ b/apps/desktop/src/app/app.module.ts
@@ -18,19 +18,8 @@ import { UpdateTempPasswordComponent } from "../auth/update-temp-password.compon
import { SshAgentService } from "../autofill/services/ssh-agent.service";
import { PremiumComponent } from "../billing/app/accounts/premium.component";
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
-import { AddEditCustomFieldsComponent } from "../vault/app/vault/add-edit-custom-fields.component";
-import { AddEditComponent } from "../vault/app/vault/add-edit.component";
-import { AttachmentsComponent } from "../vault/app/vault/attachments.component";
-import { CollectionsComponent } from "../vault/app/vault/collections.component";
-import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.component";
-import { PasswordHistoryComponent } from "../vault/app/vault/password-history.component";
-import { ShareComponent } from "../vault/app/vault/share.component";
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
-import { VaultItemsComponent } from "../vault/app/vault/vault-items.component";
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
-import { VaultComponent } from "../vault/app/vault/vault.component";
-import { ViewCustomFieldsComponent } from "../vault/app/vault/view-custom-fields.component";
-import { ViewComponent } from "../vault/app/vault/view.component";
import { SettingsComponent } from "./accounts/settings.component";
import { VaultTimeoutInputComponent } from "./accounts/vault-timeout-input.component";
@@ -61,28 +50,17 @@ import { SharedModule } from "./shared/shared.module";
],
declarations: [
AccountSwitcherComponent,
- AddEditComponent,
- AddEditCustomFieldsComponent,
AppComponent,
- AttachmentsComponent,
- CollectionsComponent,
ColorPasswordPipe,
ColorPasswordCountPipe,
- FolderAddEditComponent,
HeaderComponent,
- PasswordHistoryComponent,
PremiumComponent,
RemovePasswordComponent,
SearchComponent,
SetPasswordComponent,
SettingsComponent,
- ShareComponent,
UpdateTempPasswordComponent,
- VaultComponent,
- VaultItemsComponent,
VaultTimeoutInputComponent,
- ViewCustomFieldsComponent,
- ViewComponent,
],
providers: [SshAgentService],
bootstrap: [AppComponent],
diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss
index ce03406dd24..3c3d4ff508c 100644
--- a/apps/desktop/src/scss/misc.scss
+++ b/apps/desktop/src/scss/misc.scss
@@ -441,10 +441,6 @@ img,
user-select: none;
}
-app-vault-view .box-footer {
- user-select: auto;
-}
-
/* override for vault icon in desktop */
app-vault-icon > div {
display: flex;
diff --git a/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html b/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html
deleted file mode 100644
index bc0c3876b71..00000000000
--- a/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.ts b/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.ts
deleted file mode 100644
index b4be2406c4b..00000000000
--- a/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component } from "@angular/core";
-
-import { AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent } from "@bitwarden/angular/vault/components/add-edit-custom-fields.component";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-
-@Component({
- selector: "app-vault-add-edit-custom-fields",
- templateUrl: "add-edit-custom-fields.component.html",
- standalone: false,
-})
-export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsComponent {
- constructor(i18nService: I18nService, eventCollectionService: EventCollectionService) {
- super(i18nService, eventCollectionService);
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.html b/apps/desktop/src/vault/app/vault/add-edit.component.html
deleted file mode 100644
index 2cd384885ce..00000000000
--- a/apps/desktop/src/vault/app/vault/add-edit.component.html
+++ /dev/null
@@ -1,826 +0,0 @@
-
diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts
deleted file mode 100644
index e9b18270f2d..00000000000
--- a/apps/desktop/src/vault/app/vault/add-edit.component.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { DatePipe } from "@angular/common";
-import { Component, NgZone, OnChanges, OnDestroy, OnInit, ViewChild } from "@angular/core";
-import { NgForm } from "@angular/forms";
-import { map, shareReplay } from "rxjs";
-
-import { CollectionService } from "@bitwarden/admin-console/common";
-import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/vault/components/add-edit.component";
-import { AuditService } from "@bitwarden/common/abstractions/audit.service";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
-import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
-import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { PasswordRepromptService, SshImportPromptService } from "@bitwarden/vault";
-
-const BroadcasterSubscriptionId = "AddEditComponent";
-
-@Component({
- selector: "app-vault-add-edit",
- templateUrl: "add-edit.component.html",
- standalone: false,
-})
-export class AddEditComponent extends BaseAddEditComponent implements OnInit, OnChanges, OnDestroy {
- @ViewChild("form")
- private form: NgForm;
- menuItems$ = this.restrictedItemTypesService.restricted$.pipe(
- map((restrictedItemTypes) =>
- // Filter out restricted item types from the default CIPHER_MENU_ITEMS array
- CIPHER_MENU_ITEMS.filter(
- (typeOption) =>
- !restrictedItemTypes.some(
- (restrictedType) => restrictedType.cipherType === typeOption.type,
- ),
- ),
- ),
- shareReplay({ bufferSize: 1, refCount: true }),
- );
-
- constructor(
- cipherService: CipherService,
- folderService: FolderService,
- i18nService: I18nService,
- platformUtilsService: PlatformUtilsService,
- auditService: AuditService,
- accountService: AccountService,
- collectionService: CollectionService,
- messagingService: MessagingService,
- eventCollectionService: EventCollectionService,
- policyService: PolicyService,
- passwordRepromptService: PasswordRepromptService,
- private broadcasterService: BroadcasterService,
- private ngZone: NgZone,
- logService: LogService,
- organizationService: OrganizationService,
- dialogService: DialogService,
- datePipe: DatePipe,
- configService: ConfigService,
- toastService: ToastService,
- cipherAuthorizationService: CipherAuthorizationService,
- sdkService: SdkService,
- sshImportPromptService: SshImportPromptService,
- protected restrictedItemTypesService: RestrictedItemTypesService,
- ) {
- super(
- cipherService,
- folderService,
- i18nService,
- platformUtilsService,
- auditService,
- accountService,
- collectionService,
- messagingService,
- eventCollectionService,
- policyService,
- logService,
- passwordRepromptService,
- organizationService,
- dialogService,
- window,
- datePipe,
- configService,
- cipherAuthorizationService,
- toastService,
- sdkService,
- sshImportPromptService,
- );
- }
-
- async ngOnInit() {
- await super.ngOnInit();
- await this.load();
- this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
- this.ngZone.run(() => {
- switch (message.command) {
- case "windowHidden":
- this.onWindowHidden();
- break;
- default:
- }
- });
- });
- // We use ngOnChanges for everything else instead.
- }
-
- async ngOnChanges() {
- await this.load();
- }
-
- ngOnDestroy() {
- this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
- }
-
- async load() {
- if (
- document.querySelectorAll("app-vault-add-edit .ng-dirty").length === 0 ||
- (this.cipher != null && this.cipherId !== this.cipher.id)
- ) {
- this.cipher = null;
- }
-
- await super.load();
- }
-
- onWindowHidden() {
- this.showPassword = false;
- this.showCardNumber = false;
- this.showCardCode = false;
- if (this.cipher !== null && this.cipher.hasFields) {
- this.cipher.fields.forEach((field) => {
- field.showValue = false;
- });
- }
- }
-
- allowOwnershipOptions(): boolean {
- return (
- (!this.editMode || this.cloneMode) &&
- this.ownershipOptions &&
- (this.ownershipOptions.length > 1 || !this.allowPersonal)
- );
- }
-
- markPasswordAsDirty() {
- this.form.controls["Login.Password"].markAsDirty();
- }
-
- openHelpReprompt() {
- this.platformUtilsService.launchUri(
- "https://bitwarden.com/help/managing-items/#protect-individual-items",
- );
- }
-
- /**
- * Updates the cipher when an attachment is altered.
- * Note: This only updates the `attachments` and `revisionDate`
- * properties to ensure any in-progress edits are not lost.
- */
- patchCipherAttachments(cipher: CipherView) {
- this.cipher.attachments = cipher.attachments;
- this.cipher.revisionDate = cipher.revisionDate;
- }
-
- truncateString(value: string, length: number) {
- return value.length > length ? value.substring(0, length) + "..." : value;
- }
-
- togglePrivateKey() {
- this.showPrivateKey = !this.showPrivateKey;
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/attachments.component.html b/apps/desktop/src/vault/app/vault/attachments.component.html
deleted file mode 100644
index addd068f1a4..00000000000
--- a/apps/desktop/src/vault/app/vault/attachments.component.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/attachments.component.ts b/apps/desktop/src/vault/app/vault/attachments.component.ts
deleted file mode 100644
index a116a4d2acb..00000000000
--- a/apps/desktop/src/vault/app/vault/attachments.component.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Component } from "@angular/core";
-
-import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
-import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { KeyService } from "@bitwarden/key-management";
-
-@Component({
- selector: "app-vault-attachments",
- templateUrl: "attachments.component.html",
- standalone: false,
-})
-export class AttachmentsComponent extends BaseAttachmentsComponent {
- constructor(
- cipherService: CipherService,
- i18nService: I18nService,
- keyService: KeyService,
- encryptService: EncryptService,
- platformUtilsService: PlatformUtilsService,
- apiService: ApiService,
- logService: LogService,
- stateService: StateService,
- fileDownloadService: FileDownloadService,
- dialogService: DialogService,
- billingAccountProfileStateService: BillingAccountProfileStateService,
- accountService: AccountService,
- toastService: ToastService,
- configService: ConfigService,
- ) {
- super(
- cipherService,
- i18nService,
- keyService,
- encryptService,
- platformUtilsService,
- apiService,
- window,
- logService,
- stateService,
- fileDownloadService,
- dialogService,
- billingAccountProfileStateService,
- accountService,
- toastService,
- configService,
- );
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/collections.component.html b/apps/desktop/src/vault/app/vault/collections.component.html
deleted file mode 100644
index a87cbc6b180..00000000000
--- a/apps/desktop/src/vault/app/vault/collections.component.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/collections.component.ts b/apps/desktop/src/vault/app/vault/collections.component.ts
deleted file mode 100644
index 46455d04cd2..00000000000
--- a/apps/desktop/src/vault/app/vault/collections.component.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Component } from "@angular/core";
-
-import { CollectionService } from "@bitwarden/admin-console/common";
-import { CollectionsComponent as BaseCollectionsComponent } from "@bitwarden/angular/admin-console/components/collections.component";
-import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { ToastService } from "@bitwarden/components";
-
-@Component({
- selector: "app-vault-collections",
- templateUrl: "collections.component.html",
- standalone: false,
-})
-export class CollectionsComponent extends BaseCollectionsComponent {
- constructor(
- cipherService: CipherService,
- i18nService: I18nService,
- collectionService: CollectionService,
- platformUtilsService: PlatformUtilsService,
- organizationService: OrganizationService,
- logService: LogService,
- accountService: AccountService,
- toastService: ToastService,
- ) {
- super(
- collectionService,
- platformUtilsService,
- i18nService,
- cipherService,
- organizationService,
- logService,
- accountService,
- toastService,
- );
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/folder-add-edit.component.html b/apps/desktop/src/vault/app/vault/folder-add-edit.component.html
deleted file mode 100644
index d22628df046..00000000000
--- a/apps/desktop/src/vault/app/vault/folder-add-edit.component.html
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts b/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts
deleted file mode 100644
index cecd5cd671c..00000000000
--- a/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Component } from "@angular/core";
-import { FormBuilder } from "@angular/forms";
-
-import { FolderAddEditComponent as BaseFolderAddEditComponent } from "@bitwarden/angular/vault/components/folder-add-edit.component";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
-import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { KeyService } from "@bitwarden/key-management";
-
-@Component({
- selector: "app-folder-add-edit",
- templateUrl: "folder-add-edit.component.html",
- standalone: false,
-})
-export class FolderAddEditComponent extends BaseFolderAddEditComponent {
- constructor(
- folderService: FolderService,
- folderApiService: FolderApiServiceAbstraction,
- accountService: AccountService,
- keyService: KeyService,
- i18nService: I18nService,
- platformUtilsService: PlatformUtilsService,
- logService: LogService,
- dialogService: DialogService,
- formBuilder: FormBuilder,
- toastService: ToastService,
- ) {
- super(
- folderService,
- folderApiService,
- accountService,
- keyService,
- i18nService,
- platformUtilsService,
- logService,
- dialogService,
- formBuilder,
- toastService,
- );
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/password-history.component.html b/apps/desktop/src/vault/app/vault/password-history.component.html
deleted file mode 100644
index 362061b250d..00000000000
--- a/apps/desktop/src/vault/app/vault/password-history.component.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- {{ h.lastUsedDate | date: "medium" }}
-
-
-
-
-
-
- {{ "noPasswordsInList" | i18n }}
-
-
-
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/password-history.component.ts b/apps/desktop/src/vault/app/vault/password-history.component.ts
deleted file mode 100644
index e83ce0d56ea..00000000000
--- a/apps/desktop/src/vault/app/vault/password-history.component.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Component } from "@angular/core";
-
-import { PasswordHistoryComponent as BasePasswordHistoryComponent } from "@bitwarden/angular/vault/components/password-history.component";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { ToastService } from "@bitwarden/components";
-
-@Component({
- selector: "app-password-history",
- templateUrl: "password-history.component.html",
- standalone: false,
-})
-export class PasswordHistoryComponent extends BasePasswordHistoryComponent {
- constructor(
- cipherService: CipherService,
- platformUtilsService: PlatformUtilsService,
- i18nService: I18nService,
- accountService: AccountService,
- toastService: ToastService,
- ) {
- super(cipherService, platformUtilsService, i18nService, accountService, window, toastService);
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/share.component.html b/apps/desktop/src/vault/app/vault/share.component.html
deleted file mode 100644
index 8f85ecf891e..00000000000
--- a/apps/desktop/src/vault/app/vault/share.component.html
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/share.component.ts b/apps/desktop/src/vault/app/vault/share.component.ts
deleted file mode 100644
index 50842439323..00000000000
--- a/apps/desktop/src/vault/app/vault/share.component.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Component } from "@angular/core";
-
-import { CollectionService } from "@bitwarden/admin-console/common";
-import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
-import { ShareComponent as BaseShareComponent } from "@bitwarden/angular/components/share.component";
-import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-
-@Component({
- selector: "app-vault-share",
- templateUrl: "share.component.html",
- standalone: false,
-})
-export class ShareComponent extends BaseShareComponent {
- constructor(
- cipherService: CipherService,
- i18nService: I18nService,
- collectionService: CollectionService,
- platformUtilsService: PlatformUtilsService,
- logService: LogService,
- organizationService: OrganizationService,
- accountService: AccountService,
- private modalRef: ModalRef,
- ) {
- super(
- collectionService,
- platformUtilsService,
- i18nService,
- cipherService,
- logService,
- organizationService,
- accountService,
- );
- }
-
- protected close() {
- this.modalRef.close();
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/vault-items.component.html b/apps/desktop/src/vault/app/vault/vault-items.component.html
deleted file mode 100644
index 8a869cd2a32..00000000000
--- a/apps/desktop/src/vault/app/vault/vault-items.component.html
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
![]()
-
{{ "noItemsInList" | i18n }}
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/vault-items.component.ts b/apps/desktop/src/vault/app/vault/vault-items.component.ts
deleted file mode 100644
index c37a29833d9..00000000000
--- a/apps/desktop/src/vault/app/vault/vault-items.component.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Component } from "@angular/core";
-import { distinctUntilChanged } from "rxjs";
-
-import { VaultItemsComponent as BaseVaultItemsComponent } from "@bitwarden/angular/vault/components/vault-items.component";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
-
-import { SearchBarService } from "../../../app/layout/search/search-bar.service";
-
-@Component({
- selector: "app-vault-items",
- templateUrl: "vault-items.component.html",
- standalone: false,
-})
-export class VaultItemsComponent extends BaseVaultItemsComponent {
- constructor(
- searchService: SearchService,
- searchBarService: SearchBarService,
- cipherService: CipherService,
- accountService: AccountService,
- protected restrictedItemTypesService: RestrictedItemTypesService,
- ) {
- super(searchService, cipherService, accountService, restrictedItemTypesService);
-
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil
- searchBarService.searchText$.pipe(distinctUntilChanged()).subscribe((searchText) => {
- this.searchText = searchText;
- });
- }
-
- trackByFn(index: number, c: CipherView) {
- return c.id;
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/vault.component.html b/apps/desktop/src/vault/app/vault/vault.component.html
deleted file mode 100644
index 9a25619b1a8..00000000000
--- a/apps/desktop/src/vault/app/vault/vault.component.html
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
![Bitwarden]()
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts
deleted file mode 100644
index d8a54f1ec35..00000000000
--- a/apps/desktop/src/vault/app/vault/vault.component.ts
+++ /dev/null
@@ -1,870 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import {
- ChangeDetectorRef,
- Component,
- NgZone,
- OnDestroy,
- OnInit,
- ViewChild,
- ViewContainerRef,
-} from "@angular/core";
-import { ActivatedRoute, Router } from "@angular/router";
-import { firstValueFrom, Subject, takeUntil, switchMap, lastValueFrom } from "rxjs";
-import { filter, first, map, take } from "rxjs/operators";
-
-import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
-import { ModalService } from "@bitwarden/angular/services/modal.service";
-import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
-import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
-import { EventType } from "@bitwarden/common/enums";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
-import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { SyncService } from "@bitwarden/common/platform/sync";
-import { CipherId, UserId } from "@bitwarden/common/types/guid";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
-import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
-import { CipherType, toCipherType } from "@bitwarden/common/vault/enums";
-import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { DialogService, ToastService } from "@bitwarden/components";
-import {
- AddEditFolderDialogComponent,
- AddEditFolderDialogResult,
- DecryptionFailureDialogComponent,
- PasswordRepromptService,
-} from "@bitwarden/vault";
-
-import { SearchBarService } from "../../../app/layout/search/search-bar.service";
-import { invokeMenu, RendererMenuItem } from "../../../utils";
-
-import { AddEditComponent } from "./add-edit.component";
-import { AttachmentsComponent } from "./attachments.component";
-import { CollectionsComponent } from "./collections.component";
-import { CredentialGeneratorDialogComponent } from "./credential-generator-dialog.component";
-import { PasswordHistoryComponent } from "./password-history.component";
-import { ShareComponent } from "./share.component";
-import { VaultFilterComponent } from "./vault-filter/vault-filter.component";
-import { VaultItemsComponent } from "./vault-items.component";
-import { ViewComponent } from "./view.component";
-
-const BroadcasterSubscriptionId = "VaultComponent";
-
-@Component({
- selector: "app-vault",
- templateUrl: "vault.component.html",
- standalone: false,
-})
-export class VaultComponent implements OnInit, OnDestroy {
- @ViewChild(ViewComponent) viewComponent: ViewComponent;
- @ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
- @ViewChild(VaultItemsComponent, { static: true }) vaultItemsComponent: VaultItemsComponent;
- @ViewChild("generator", { read: ViewContainerRef, static: true })
- generatorModalRef: ViewContainerRef;
- @ViewChild(VaultFilterComponent, { static: true }) vaultFilterComponent: VaultFilterComponent;
- @ViewChild("attachments", { read: ViewContainerRef, static: true })
- attachmentsModalRef: ViewContainerRef;
- @ViewChild("passwordHistory", { read: ViewContainerRef, static: true })
- passwordHistoryModalRef: ViewContainerRef;
- @ViewChild("share", { read: ViewContainerRef, static: true }) shareModalRef: ViewContainerRef;
- @ViewChild("collections", { read: ViewContainerRef, static: true })
- collectionsModalRef: ViewContainerRef;
-
- action: string;
- cipherId: string = null;
- favorites = false;
- type: CipherType = null;
- folderId: string = null;
- collectionId: string = null;
- organizationId: string = null;
- myVaultOnly = false;
- addType: CipherType = null;
- addOrganizationId: string = null;
- addCollectionIds: string[] = null;
- showingModal = false;
- deleted = false;
- userHasPremiumAccess = false;
- activeFilter: VaultFilter = new VaultFilter();
- activeUserId: UserId;
- cipherRepromptId: string | null = null;
-
- private modal: ModalRef = null;
- private componentIsDestroyed$ = new Subject();
-
- constructor(
- private route: ActivatedRoute,
- private router: Router,
- private i18nService: I18nService,
- private modalService: ModalService,
- private broadcasterService: BroadcasterService,
- private changeDetectorRef: ChangeDetectorRef,
- private ngZone: NgZone,
- private syncService: SyncService,
- private messagingService: MessagingService,
- private platformUtilsService: PlatformUtilsService,
- private eventCollectionService: EventCollectionService,
- private totpService: TotpService,
- private passwordRepromptService: PasswordRepromptService,
- private searchBarService: SearchBarService,
- private apiService: ApiService,
- private dialogService: DialogService,
- private billingAccountProfileStateService: BillingAccountProfileStateService,
- private toastService: ToastService,
- private accountService: AccountService,
- private cipherService: CipherService,
- private folderService: FolderService,
- private authRequestService: AuthRequestServiceAbstraction,
- private configService: ConfigService,
- ) {}
-
- async ngOnInit() {
- this.accountService.activeAccount$
- .pipe(
- switchMap((account) =>
- this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id),
- ),
- takeUntil(this.componentIsDestroyed$),
- )
- .subscribe((canAccessPremium: boolean) => {
- this.userHasPremiumAccess = canAccessPremium;
- });
-
- this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.ngZone.run(async () => {
- let detectChanges = true;
-
- switch (message.command) {
- case "newLogin":
- await this.addCipher(CipherType.Login);
- break;
- case "newCard":
- await this.addCipher(CipherType.Card);
- break;
- case "newIdentity":
- await this.addCipher(CipherType.Identity);
- break;
- case "newSecureNote":
- await this.addCipher(CipherType.SecureNote);
- break;
- case "newSshKey":
- await this.addCipher(CipherType.SshKey);
- break;
- case "focusSearch":
- (document.querySelector("#search") as HTMLInputElement).select();
- detectChanges = false;
- break;
- case "syncCompleted":
- await this.vaultItemsComponent.reload(this.activeFilter.buildFilter());
- await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter);
- await this.vaultFilterComponent.reloadOrganizations();
- break;
- case "modalShown":
- this.showingModal = true;
- break;
- case "modalClosed":
- this.showingModal = false;
- break;
- case "copyUsername": {
- const uComponent =
- this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
- const uCipher = uComponent != null ? uComponent.cipher : null;
- if (
- this.cipherId != null &&
- uCipher != null &&
- uCipher.id === this.cipherId &&
- uCipher.login != null &&
- uCipher.login.username != null
- ) {
- this.copyValue(uCipher, uCipher.login.username, "username", "Username");
- }
- break;
- }
- case "copyPassword": {
- const pComponent =
- this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
- const pCipher = pComponent != null ? pComponent.cipher : null;
- if (
- this.cipherId != null &&
- pCipher != null &&
- pCipher.id === this.cipherId &&
- pCipher.login != null &&
- pCipher.login.password != null &&
- pCipher.viewPassword
- ) {
- this.copyValue(pCipher, pCipher.login.password, "password", "Password");
- }
- break;
- }
- case "copyTotp": {
- const tComponent =
- this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
- const tCipher = tComponent != null ? tComponent.cipher : null;
- if (
- this.cipherId != null &&
- tCipher != null &&
- tCipher.id === this.cipherId &&
- tCipher.login != null &&
- tCipher.login.hasTotp &&
- this.userHasPremiumAccess
- ) {
- const value = await firstValueFrom(this.totpService.getCode$(tCipher.login.totp));
- this.copyValue(tCipher, value.code, "verificationCodeTotp", "TOTP");
- }
- break;
- }
- default:
- detectChanges = false;
- break;
- }
-
- if (detectChanges) {
- this.changeDetectorRef.detectChanges();
- }
- });
- });
-
- if (!this.syncService.syncInProgress) {
- await this.load();
- }
-
- this.searchBarService.setEnabled(true);
- this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
-
- const browserLoginApprovalFeatureFlag = await firstValueFrom(
- this.configService.getFeatureFlag$(FeatureFlag.PM14938_BrowserExtensionLoginApproval),
- );
- if (browserLoginApprovalFeatureFlag === true) {
- const authRequests = await firstValueFrom(this.authRequestService.getPendingAuthRequests$());
- // There is a chance that there is more than one auth request in the response we only show the most recent one
- if (authRequests.length > 0) {
- const mostRecentAuthRequest = authRequests.reduce((latest, current) => {
- const latestDate = new Date(latest.creationDate).getTime();
- const currentDate = new Date(current.creationDate).getTime();
- return currentDate > latestDate ? current : latest;
- });
-
- this.messagingService.send("openLoginApproval", {
- notificationId: mostRecentAuthRequest.id,
- });
- }
- } else {
- const authRequest = await this.apiService.getLastAuthRequest();
- if (authRequest != null) {
- this.messagingService.send("openLoginApproval", {
- notificationId: authRequest.id,
- });
- }
- }
-
- this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
-
- this.cipherService
- .failedToDecryptCiphers$(this.activeUserId)
- .pipe(
- map((ciphers) => ciphers?.filter((c) => !c.isDeleted) ?? []),
- filter((ciphers) => ciphers.length > 0),
- take(1),
- takeUntil(this.componentIsDestroyed$),
- )
- .subscribe((ciphers) => {
- DecryptionFailureDialogComponent.open(this.dialogService, {
- cipherIds: ciphers.map((c) => c.id as CipherId),
- });
- });
- }
-
- ngOnDestroy() {
- this.searchBarService.setEnabled(false);
- this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
- this.componentIsDestroyed$.next(true);
- this.componentIsDestroyed$.complete();
- }
-
- async load() {
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
- this.route.queryParams.pipe(first()).subscribe(async (params) => {
- if (params.cipherId) {
- const cipherView = new CipherView();
- cipherView.id = params.cipherId;
- if (params.action === "clone") {
- await this.cloneCipher(cipherView);
- } else if (params.action === "edit") {
- await this.editCipher(cipherView);
- } else {
- await this.viewCipher(cipherView);
- }
- } else if (params.action === "add") {
- this.addType = toCipherType(params.addType);
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.addCipher(this.addType);
- }
-
- const paramCipherType = toCipherType(params.type);
- this.activeFilter = new VaultFilter({
- status: params.deleted ? "trash" : params.favorites ? "favorites" : "all",
- cipherType: params.action === "add" || paramCipherType == null ? null : paramCipherType,
- selectedFolderId: params.folderId,
- selectedCollectionId: params.selectedCollectionId,
- selectedOrganizationId: params.selectedOrganizationId,
- myVaultOnly: params.myVaultOnly ?? false,
- });
- await this.vaultItemsComponent.reload(this.activeFilter.buildFilter());
- });
- }
-
- async viewCipher(cipher: CipherView) {
- if (!(await this.canNavigateAway("view", cipher))) {
- return;
- } else if (!(await this.passwordReprompt(cipher))) {
- return;
- }
-
- this.cipherId = cipher.id;
- this.action = "view";
- this.go();
- }
-
- viewCipherMenu(cipher: CipherView) {
- const menu: RendererMenuItem[] = [
- {
- label: this.i18nService.t("view"),
- click: () =>
- this.functionWithChangeDetection(() => {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.viewCipher(cipher);
- }),
- },
- ];
-
- if (cipher.decryptionFailure) {
- invokeMenu(menu);
- return;
- }
-
- if (!cipher.isDeleted) {
- menu.push({
- label: this.i18nService.t("edit"),
- click: () =>
- this.functionWithChangeDetection(() => {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.editCipher(cipher);
- }),
- });
- if (!cipher.organizationId) {
- menu.push({
- label: this.i18nService.t("clone"),
- click: () =>
- this.functionWithChangeDetection(() => {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.cloneCipher(cipher);
- }),
- });
- }
- }
-
- switch (cipher.type) {
- case CipherType.Login:
- if (
- cipher.login.canLaunch ||
- cipher.login.username != null ||
- cipher.login.password != null
- ) {
- menu.push({ type: "separator" });
- }
- if (cipher.login.canLaunch) {
- menu.push({
- label: this.i18nService.t("launch"),
- click: () => this.platformUtilsService.launchUri(cipher.login.launchUri),
- });
- }
- if (cipher.login.username != null) {
- menu.push({
- label: this.i18nService.t("copyUsername"),
- click: () => this.copyValue(cipher, cipher.login.username, "username", "Username"),
- });
- }
- if (cipher.login.password != null && cipher.viewPassword) {
- menu.push({
- label: this.i18nService.t("copyPassword"),
- click: () => {
- this.copyValue(cipher, cipher.login.password, "password", "Password");
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
- },
- });
- }
- if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) {
- menu.push({
- label: this.i18nService.t("copyVerificationCodeTotp"),
- click: async () => {
- const value = await firstValueFrom(this.totpService.getCode$(cipher.login.totp));
- this.copyValue(cipher, value.code, "verificationCodeTotp", "TOTP");
- },
- });
- }
- break;
- case CipherType.Card:
- if (cipher.card.number != null || cipher.card.code != null) {
- menu.push({ type: "separator" });
- }
- if (cipher.card.number != null) {
- menu.push({
- label: this.i18nService.t("copyNumber"),
- click: () => this.copyValue(cipher, cipher.card.number, "number", "Card Number"),
- });
- }
- if (cipher.card.code != null) {
- menu.push({
- label: this.i18nService.t("copySecurityCode"),
- click: () => {
- this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code");
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
- },
- });
- }
- break;
- default:
- break;
- }
-
- invokeMenu(menu);
- }
-
- async editCipher(cipher: CipherView) {
- if (!(await this.canNavigateAway("edit", cipher))) {
- return;
- } else if (!(await this.passwordReprompt(cipher))) {
- return;
- }
-
- await this.editCipherWithoutPasswordPrompt(cipher);
- }
-
- async editCipherWithoutPasswordPrompt(cipher: CipherView) {
- if (!(await this.canNavigateAway("edit", cipher))) {
- return;
- }
-
- this.cipherId = cipher.id;
- this.action = "edit";
- this.go();
- }
-
- async cloneCipher(cipher: CipherView) {
- if (!(await this.canNavigateAway("clone", cipher))) {
- return;
- } else if (!(await this.passwordReprompt(cipher))) {
- return;
- }
-
- await this.cloneCipherWithoutPasswordPrompt(cipher);
- }
-
- async cloneCipherWithoutPasswordPrompt(cipher: CipherView) {
- if (!(await this.canNavigateAway("edit", cipher))) {
- return;
- }
-
- this.cipherId = cipher.id;
- this.action = "clone";
- this.go();
- }
-
- async addCipher(type: CipherType = null) {
- if (!(await this.canNavigateAway("add", null))) {
- return;
- }
-
- this.addType = type || this.activeFilter.cipherType;
- this.action = "add";
- this.cipherId = null;
- this.prefillNewCipherFromFilter();
- this.go();
-
- if (type === CipherType.SshKey) {
- this.toastService.showToast({
- variant: "success",
- title: "",
- message: this.i18nService.t("sshKeyGenerated"),
- });
- }
- }
-
- addCipherOptions() {
- const menu: RendererMenuItem[] = [
- {
- label: this.i18nService.t("typeLogin"),
- click: () => this.addCipherWithChangeDetection(CipherType.Login),
- },
- {
- label: this.i18nService.t("typeCard"),
- click: () => this.addCipherWithChangeDetection(CipherType.Card),
- },
- {
- label: this.i18nService.t("typeIdentity"),
- click: () => this.addCipherWithChangeDetection(CipherType.Identity),
- },
- {
- label: this.i18nService.t("typeSecureNote"),
- click: () => this.addCipherWithChangeDetection(CipherType.SecureNote),
- },
- ];
-
- invokeMenu(menu);
- }
-
- async savedCipher(cipher: CipherView) {
- this.cipherId = null;
- this.action = "view";
- await this.vaultItemsComponent.refresh();
- this.cipherId = cipher.id;
- await this.cipherService.clearCache(this.activeUserId);
- await this.vaultItemsComponent.load(this.activeFilter.buildFilter());
- this.go();
- await this.vaultItemsComponent.refresh();
- }
-
- async deletedCipher(cipher: CipherView) {
- this.cipherId = null;
- this.action = null;
- this.go();
- await this.vaultItemsComponent.refresh();
- }
-
- async restoredCipher(cipher: CipherView) {
- this.cipherId = null;
- this.action = null;
- this.go();
- await this.vaultItemsComponent.refresh();
- }
-
- async editCipherAttachments(cipher: CipherView) {
- if (this.modal != null) {
- this.modal.close();
- }
-
- const [modal, childComponent] = await this.modalService.openViewRef(
- AttachmentsComponent,
- this.attachmentsModalRef,
- (comp) => (comp.cipherId = cipher.id),
- );
- this.modal = modal;
-
- let madeAttachmentChanges = false;
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil
- childComponent.onUploadedAttachment.subscribe((cipher) => {
- madeAttachmentChanges = true;
- // Update the edit component cipher with the updated cipher,
- // which is needed because the revision date is updated when an attachment is altered
- this.addEditComponent.patchCipherAttachments(cipher);
- });
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil
- childComponent.onDeletedAttachment.subscribe((cipher) => {
- madeAttachmentChanges = true;
- // Update the edit component cipher with the updated cipher,
- // which is needed because the revision date is updated when an attachment is altered
- this.addEditComponent.patchCipherAttachments(cipher);
- });
-
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
- this.modal.onClosed.subscribe(async () => {
- this.modal = null;
- if (madeAttachmentChanges) {
- await this.vaultItemsComponent.refresh();
- }
- madeAttachmentChanges = false;
- });
- }
-
- async shareCipher(cipher: CipherView) {
- if (this.modal != null) {
- this.modal.close();
- }
-
- const [modal, childComponent] = await this.modalService.openViewRef(
- ShareComponent,
- this.shareModalRef,
- (comp) => (comp.cipherId = cipher.id),
- );
- this.modal = modal;
-
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
- childComponent.onSharedCipher.subscribe(async () => {
- this.modal.close();
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.viewCipher(cipher);
- await this.vaultItemsComponent.refresh();
- await this.cipherService.clearCache(this.activeUserId);
- await this.vaultItemsComponent.load(this.activeFilter.buildFilter());
- });
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
- this.modal.onClosed.subscribe(async () => {
- this.modal = null;
- });
- }
-
- async cipherCollections(cipher: CipherView) {
- if (this.modal != null) {
- this.modal.close();
- }
-
- const [modal, childComponent] = await this.modalService.openViewRef(
- CollectionsComponent,
- this.collectionsModalRef,
- (comp) => (comp.cipherId = cipher.id),
- );
- this.modal = modal;
-
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil
- childComponent.onSavedCollections.subscribe(() => {
- this.modal.close();
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.viewCipher(cipher);
- });
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
- this.modal.onClosed.subscribe(async () => {
- this.modal = null;
- });
- }
-
- async viewCipherPasswordHistory(cipher: CipherView) {
- if (this.modal != null) {
- this.modal.close();
- }
-
- [this.modal] = await this.modalService.openViewRef(
- PasswordHistoryComponent,
- this.passwordHistoryModalRef,
- (comp) => (comp.cipherId = cipher.id),
- );
-
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
- this.modal.onClosed.subscribe(async () => {
- this.modal = null;
- });
- }
-
- cancelledAddEdit(cipher: CipherView) {
- this.cipherId = cipher.id;
- this.action = this.cipherId != null ? "view" : null;
- this.go();
- }
-
- async applyVaultFilter(vaultFilter: VaultFilter) {
- this.searchBarService.setPlaceholderText(
- this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter)),
- );
- this.activeFilter = vaultFilter;
- await this.vaultItemsComponent.reload(
- this.activeFilter.buildFilter(),
- vaultFilter.status === "trash",
- );
- this.go();
- }
-
- private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string {
- if (vaultFilter.status === "favorites") {
- return "searchFavorites";
- }
- if (vaultFilter.status === "trash") {
- return "searchTrash";
- }
- if (vaultFilter.cipherType != null) {
- return "searchType";
- }
- if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId != "none") {
- return "searchFolder";
- }
- if (vaultFilter.selectedCollectionId != null) {
- return "searchCollection";
- }
- if (vaultFilter.selectedOrganizationId != null) {
- return "searchOrganization";
- }
- if (vaultFilter.myVaultOnly) {
- return "searchMyVault";
- }
-
- return "searchVault";
- }
-
- async openGenerator(passwordType = true) {
- CredentialGeneratorDialogComponent.open(this.dialogService, {
- onCredentialGenerated: (value?: string) => {
- if (this.addEditComponent != null) {
- this.addEditComponent.markPasswordAsDirty();
- if (passwordType) {
- this.addEditComponent.cipher.login.password = value ?? "";
- } else {
- this.addEditComponent.cipher.login.username = value ?? "";
- }
- }
- },
- type: passwordType ? "password" : "username",
- });
- return;
- }
-
- async addFolder() {
- this.messagingService.send("newFolder");
- }
-
- async editFolder(folderId: string) {
- const folderView = await firstValueFrom(
- this.folderService.getDecrypted$(folderId, this.activeUserId),
- );
-
- const dialogRef = AddEditFolderDialogComponent.open(this.dialogService, {
- editFolderConfig: {
- folder: {
- ...folderView,
- },
- },
- });
-
- const result = await lastValueFrom(dialogRef.closed);
-
- if (
- result === AddEditFolderDialogResult.Deleted ||
- result === AddEditFolderDialogResult.Created
- ) {
- await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter);
- }
- }
-
- private dirtyInput(): boolean {
- return (
- (this.action === "add" || this.action === "edit" || this.action === "clone") &&
- document.querySelectorAll("app-vault-add-edit .ng-dirty").length > 0
- );
- }
-
- private async wantsToSaveChanges(): Promise {
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "unsavedChangesTitle" },
- content: { key: "unsavedChangesConfirmation" },
- type: "warning",
- });
- return !confirmed;
- }
-
- private go(queryParams: any = null) {
- if (queryParams == null) {
- queryParams = {
- action: this.action,
- cipherId: this.cipherId,
- favorites: this.favorites ? true : null,
- type: this.type,
- folderId: this.folderId,
- collectionId: this.collectionId,
- deleted: this.deleted ? true : null,
- organizationId: this.organizationId,
- myVaultOnly: this.myVaultOnly,
- };
- }
-
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.router.navigate([], {
- relativeTo: this.route,
- queryParams: queryParams,
- replaceUrl: true,
- });
- }
-
- private addCipherWithChangeDetection(type: CipherType = null) {
- this.functionWithChangeDetection(() => this.addCipher(type));
- }
-
- private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) {
- this.functionWithChangeDetection(async () => {
- if (
- this.passwordRepromptService.protectedFields().includes(aType) &&
- !(await this.passwordReprompt(cipher))
- ) {
- return;
- }
-
- this.platformUtilsService.copyToClipboard(value);
- this.toastService.showToast({
- variant: "info",
- title: null,
- message: this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey)),
- });
- if (this.action === "view") {
- this.messagingService.send("minimizeOnCopy");
- }
- });
- }
-
- private functionWithChangeDetection(func: () => void) {
- this.ngZone.run(() => {
- func();
- this.changeDetectorRef.detectChanges();
- });
- }
-
- private prefillNewCipherFromFilter() {
- if (this.activeFilter.selectedCollectionId != null) {
- const collection = this.vaultFilterComponent.collections.fullList.filter(
- (c) => c.id === this.activeFilter.selectedCollectionId,
- );
- if (collection.length > 0) {
- this.addOrganizationId = collection[0].organizationId;
- this.addCollectionIds = [this.activeFilter.selectedCollectionId];
- }
- } else if (this.activeFilter.selectedOrganizationId) {
- this.addOrganizationId = this.activeFilter.selectedOrganizationId;
- }
- if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) {
- this.folderId = this.activeFilter.selectedFolderId;
- }
- }
-
- private async canNavigateAway(action: string, cipher?: CipherView) {
- // Don't navigate to same route
- if (this.action === action && (cipher == null || this.cipherId === cipher.id)) {
- return false;
- } else if (this.dirtyInput() && (await this.wantsToSaveChanges())) {
- return false;
- }
-
- return true;
- }
-
- private async passwordReprompt(cipher: CipherView) {
- if (cipher.reprompt === CipherRepromptType.None) {
- this.cipherRepromptId = null;
- return true;
- }
- if (this.cipherRepromptId === cipher.id) {
- return true;
- }
- const repromptResult = await this.passwordRepromptService.showPasswordPrompt();
- if (repromptResult) {
- this.cipherRepromptId = cipher.id;
- }
- return repromptResult;
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/view-custom-fields.component.html b/apps/desktop/src/vault/app/vault/view-custom-fields.component.html
deleted file mode 100644
index 5e0389af25a..00000000000
--- a/apps/desktop/src/vault/app/vault/view-custom-fields.component.html
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
-
- {{ field.name }}
-
-
- {{ "cfTypeLinked" | i18n }}: {{ field.name }}
-
-
- {{ field.value || " " }}
-
-
- {{ field.maskedValue }}
-
-
-
-
-
-
-
- {{ cipher.linkedFieldI18nKey(field.linkedId) | i18n }}
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/view-custom-fields.component.ts b/apps/desktop/src/vault/app/vault/view-custom-fields.component.ts
deleted file mode 100644
index efe61ad1fa7..00000000000
--- a/apps/desktop/src/vault/app/vault/view-custom-fields.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component } from "@angular/core";
-
-import { ViewCustomFieldsComponent as BaseViewCustomFieldsComponent } from "@bitwarden/angular/vault/components/view-custom-fields.component";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-
-@Component({
- selector: "app-vault-view-custom-fields",
- templateUrl: "view-custom-fields.component.html",
- standalone: false,
-})
-export class ViewCustomFieldsComponent extends BaseViewCustomFieldsComponent {
- constructor(eventCollectionService: EventCollectionService) {
- super(eventCollectionService);
- }
-}
diff --git a/apps/desktop/src/vault/app/vault/view.component.html b/apps/desktop/src/vault/app/vault/view.component.html
deleted file mode 100644
index d3e3a751d9d..00000000000
--- a/apps/desktop/src/vault/app/vault/view.component.html
+++ /dev/null
@@ -1,683 +0,0 @@
-
-
-
-
-
-
- {{ "name" | i18n }}
- {{ cipher.name }}
-
-
-
-
-
- {{ "username" | i18n }}
- {{ cipher.login.username }}
-
-
-
-
-
-
-
-
{{ "password" | i18n }}
-
- {{ cipher.login.maskedPassword }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ "typePasskey" | i18n }}
- {{ fido2CredentialCreationDateValue }}
-
-
-
-
-
- {{ "verificationCodeTotp" | i18n }}
- {{ totpInfo.totpCodeFormatted }}
-
-
- {{ totpInfo.totpSec }}
-
-
-
-
-
-
-
-
-
-
-
{{ "verificationCodeTotp" | i18n }}
-
- {{ "premiumSubcriptionRequired" | i18n }}
-
-
-
-
-
-
-
-
- {{ "cardholderName" | i18n }}
- {{ cipher.card.cardholderName }}
-
-
-
- {{ "number" | i18n }}
- {{
- cipher.card.maskedNumber | creditCardNumber: cipher.card.brand
- }}
- {{
- cipher.card.number | creditCardNumber: cipher.card.brand
- }}
-
-
-
-
-
-
-
- {{ "brand" | i18n }}
- {{ cipher.card.brand }}
-
-
- {{ "expiration" | i18n }}
- {{ cipher.card.expiration }}
-
-
-
- {{ "securityCode" | i18n }}
- {{ cipher.card.maskedCode }}
- {{ cipher.card.code }}
-
-
-
-
-
-
-
-
-
-
- {{ "identityName" | i18n }}
- {{ cipher.identity.fullName }}
-
-
- {{ "username" | i18n }}
- {{ cipher.identity.username }}
-
-
- {{ "company" | i18n }}
- {{ cipher.identity.company }}
-
-
- {{ "ssn" | i18n }}
- {{ cipher.identity.ssn }}
-
-
- {{ "passportNumber" | i18n }}
- {{ cipher.identity.passportNumber }}
-
-
- {{ "licenseNumber" | i18n }}
- {{ cipher.identity.licenseNumber }}
-
-
- {{ "email" | i18n }}
- {{ cipher.identity.email }}
-
-
- {{ "phone" | i18n }}
- {{ cipher.identity.phone }}
-
-
-
{{ "address" | i18n }}
-
{{ cipher.identity.address1 }}
-
{{ cipher.identity.address2 }}
-
{{ cipher.identity.address3 }}
-
- {{ cipher.identity.fullAddressPart2 }}
-
-
{{ cipher.identity.country }}
-
-
-
-
-
-
-
{{ "sshPrivateKey" | i18n }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ "uri" | i18n }}
- {{ "website" | i18n }}
- {{ u.hostOrUri }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/desktop/src/vault/app/vault/view.component.ts b/apps/desktop/src/vault/app/vault/view.component.ts
deleted file mode 100644
index 7e7f7b57fc8..00000000000
--- a/apps/desktop/src/vault/app/vault/view.component.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import { DatePipe } from "@angular/common";
-import {
- ChangeDetectorRef,
- Component,
- EventEmitter,
- Input,
- NgZone,
- OnChanges,
- OnDestroy,
- OnInit,
- Output,
- SimpleChanges,
-} from "@angular/core";
-
-import { ViewComponent as BaseViewComponent } from "@bitwarden/angular/vault/components/view.component";
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { AuditService } from "@bitwarden/common/abstractions/audit.service";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
-import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
-import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
-import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
-import { CipherId } from "@bitwarden/common/types/guid";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
-import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { KeyService } from "@bitwarden/key-management";
-import { DecryptionFailureDialogComponent, PasswordRepromptService } from "@bitwarden/vault";
-
-const BroadcasterSubscriptionId = "ViewComponent";
-
-@Component({
- selector: "app-vault-view",
- templateUrl: "view.component.html",
- standalone: false,
-})
-export class ViewComponent extends BaseViewComponent implements OnInit, OnDestroy, OnChanges {
- @Output() onViewCipherPasswordHistory = new EventEmitter();
- @Input() masterPasswordAlreadyPrompted: boolean = false;
-
- constructor(
- cipherService: CipherService,
- folderService: FolderService,
- totpService: TotpService,
- tokenService: TokenService,
- i18nService: I18nService,
- keyService: KeyService,
- encryptService: EncryptService,
- platformUtilsService: PlatformUtilsService,
- auditService: AuditService,
- broadcasterService: BroadcasterService,
- ngZone: NgZone,
- changeDetectorRef: ChangeDetectorRef,
- eventCollectionService: EventCollectionService,
- apiService: ApiService,
- private messagingService: MessagingService,
- passwordRepromptService: PasswordRepromptService,
- logService: LogService,
- stateService: StateService,
- fileDownloadService: FileDownloadService,
- dialogService: DialogService,
- datePipe: DatePipe,
- billingAccountProfileStateService: BillingAccountProfileStateService,
- accountService: AccountService,
- toastService: ToastService,
- cipherAuthorizationService: CipherAuthorizationService,
- configService: ConfigService,
- ) {
- super(
- cipherService,
- folderService,
- totpService,
- tokenService,
- i18nService,
- keyService,
- encryptService,
- platformUtilsService,
- auditService,
- window,
- broadcasterService,
- ngZone,
- changeDetectorRef,
- eventCollectionService,
- apiService,
- passwordRepromptService,
- logService,
- stateService,
- fileDownloadService,
- dialogService,
- datePipe,
- accountService,
- billingAccountProfileStateService,
- toastService,
- cipherAuthorizationService,
- configService,
- );
- }
-
- ngOnInit() {
- super.ngOnInit();
-
- this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
- this.ngZone.run(() => {
- switch (message.command) {
- case "windowHidden":
- this.onWindowHidden();
- break;
- default:
- }
- });
- });
- this.passwordReprompted = this.masterPasswordAlreadyPrompted;
- }
-
- ngOnDestroy() {
- super.ngOnDestroy();
- this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
- }
-
- async ngOnChanges(changes: SimpleChanges) {
- if (this.cipher?.decryptionFailure) {
- DecryptionFailureDialogComponent.open(this.dialogService, {
- cipherIds: [this.cipherId as CipherId],
- });
- return;
- }
- this.passwordReprompted = this.masterPasswordAlreadyPrompted;
-
- if (changes["cipherId"]) {
- if (changes["cipherId"].currentValue !== changes["cipherId"].previousValue) {
- this.showPrivateKey = false;
- }
- }
- }
-
- viewHistory() {
- this.onViewCipherPasswordHistory.emit(this.cipher);
- }
-
- async copy(value: string, typeI18nKey: string, aType: string): Promise {
- const hasCopied = await super.copy(value, typeI18nKey, aType);
- if (hasCopied) {
- this.messagingService.send("minimizeOnCopy");
- }
-
- return hasCopied;
- }
-
- onWindowHidden() {
- this.showPassword = false;
- this.showCardNumber = false;
- this.showCardCode = false;
- if (this.cipher !== null && this.cipher.hasFields) {
- this.cipher.fields.forEach((field) => {
- field.showValue = false;
- });
- }
- }
-
- showGetPremium() {
- if (!this.canAccessPremium) {
- this.messagingService.send("premiumRequired");
- }
- }
-}
diff --git a/libs/angular/src/admin-console/components/collections.component.ts b/libs/angular/src/admin-console/components/collections.component.ts
deleted file mode 100644
index 9d36ab4619f..00000000000
--- a/libs/angular/src/admin-console/components/collections.component.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
-import { firstValueFrom, map } from "rxjs";
-
-// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
-// eslint-disable-next-line no-restricted-imports
-import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
-import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { UserId } from "@bitwarden/common/types/guid";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { ToastService } from "@bitwarden/components";
-
-@Directive()
-export class CollectionsComponent implements OnInit {
- @Input() cipherId: string;
- @Input() allowSelectNone = false;
- @Output() onSavedCollections = new EventEmitter();
-
- formPromise: Promise;
- cipher: CipherView;
- collectionIds: string[];
- collections: CollectionView[] = [];
- organization: Organization;
-
- protected cipherDomain: Cipher;
-
- constructor(
- protected collectionService: CollectionService,
- protected platformUtilsService: PlatformUtilsService,
- protected i18nService: I18nService,
- protected cipherService: CipherService,
- protected organizationService: OrganizationService,
- private logService: LogService,
- private accountService: AccountService,
- private toastService: ToastService,
- ) {}
-
- async ngOnInit() {
- await this.load();
- }
-
- async load() {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- this.cipherDomain = await this.loadCipher(activeUserId);
- this.collectionIds = this.loadCipherCollections();
- this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
- this.collections = await this.loadCollections();
-
- this.collections.forEach((c) => ((c as any).checked = false));
- if (this.collectionIds != null) {
- this.collections.forEach((c) => {
- (c as any).checked = this.collectionIds != null && this.collectionIds.indexOf(c.id) > -1;
- });
- }
-
- if (this.organization == null) {
- this.organization = await firstValueFrom(
- this.organizationService
- .organizations$(activeUserId)
- .pipe(
- map((organizations) =>
- organizations.find((org) => org.id === this.cipher.organizationId),
- ),
- ),
- );
- }
- }
-
- async submit(): Promise {
- const selectedCollectionIds = this.collections
- .filter((c) => {
- if (this.organization.canEditAllCiphers) {
- return !!(c as any).checked;
- } else {
- return !!(c as any).checked && !c.readOnly;
- }
- })
- .map((c) => c.id);
- if (!this.allowSelectNone && selectedCollectionIds.length === 0) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("selectOneCollection"),
- });
- return false;
- }
- this.cipherDomain.collectionIds = selectedCollectionIds;
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- this.formPromise = this.saveCollections(activeUserId);
- await this.formPromise;
- this.onSavedCollections.emit();
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("editedItem"),
- });
- return true;
- } catch (e) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: e.message,
- });
- return false;
- }
- }
-
- protected loadCipher(userId: UserId) {
- return this.cipherService.get(this.cipherId, userId);
- }
-
- protected loadCipherCollections() {
- return this.cipherDomain.collectionIds;
- }
-
- protected async loadCollections() {
- const allCollections = await this.collectionService.getAllDecrypted();
- return allCollections.filter(
- (c) => !c.readOnly && c.organizationId === this.cipher.organizationId,
- );
- }
-
- protected saveCollections(userId: UserId) {
- return this.cipherService.saveCollectionsWithServer(this.cipherDomain, userId);
- }
-}
diff --git a/libs/angular/src/components/share.component.ts b/libs/angular/src/components/share.component.ts
deleted file mode 100644
index 51827bfb9f2..00000000000
--- a/libs/angular/src/components/share.component.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
-import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
-
-// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
-// eslint-disable-next-line no-restricted-imports
-import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
-import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
-import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { Utils } from "@bitwarden/common/platform/misc/utils";
-import { Checkable, isChecked } from "@bitwarden/common/types/checkable";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-
-@Directive()
-export class ShareComponent implements OnInit, OnDestroy {
- @Input() cipherId: string;
- @Input() organizationId: string;
- @Output() onSharedCipher = new EventEmitter();
-
- formPromise: Promise;
- cipher: CipherView;
- collections: Checkable[] = [];
- organizations$: Observable;
-
- protected writeableCollections: Checkable[] = [];
-
- private _destroy = new Subject();
-
- constructor(
- protected collectionService: CollectionService,
- protected platformUtilsService: PlatformUtilsService,
- protected i18nService: I18nService,
- protected cipherService: CipherService,
- private logService: LogService,
- protected organizationService: OrganizationService,
- protected accountService: AccountService,
- ) {}
-
- async ngOnInit() {
- await this.load();
- }
-
- ngOnDestroy(): void {
- this._destroy.next();
- this._destroy.complete();
- }
-
- async load() {
- const allCollections = await this.collectionService.getAllDecrypted();
- this.writeableCollections = allCollections.map((c) => c).filter((c) => !c.readOnly);
-
- const userId = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((account) => account?.id)),
- );
-
- this.organizations$ = this.organizationService.memberOrganizations$(userId).pipe(
- map((orgs) => {
- return orgs
- .filter((o) => o.enabled && o.status === OrganizationUserStatusType.Confirmed)
- .sort(Utils.getSortFunction(this.i18nService, "name"));
- }),
- );
-
- this.organizations$.pipe(takeUntil(this._destroy)).subscribe((orgs) => {
- if (this.organizationId == null && orgs.length > 0) {
- this.organizationId = orgs[0].id;
- this.filterCollections();
- }
- });
-
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const cipherDomain = await this.cipherService.get(this.cipherId, activeUserId);
- this.cipher = await this.cipherService.decrypt(cipherDomain, activeUserId);
- }
-
- filterCollections() {
- this.writeableCollections.forEach((c) => (c.checked = false));
- if (this.organizationId == null || this.writeableCollections.length === 0) {
- this.collections = [];
- } else {
- this.collections = this.writeableCollections.filter(
- (c) => c.organizationId === this.organizationId,
- );
- }
- }
-
- async submit(): Promise {
- const selectedCollectionIds = this.collections.filter(isChecked).map((c) => c.id);
- if (selectedCollectionIds.length === 0) {
- this.platformUtilsService.showToast(
- "error",
- this.i18nService.t("errorOccurred"),
- this.i18nService.t("selectOneCollection"),
- );
- return;
- }
-
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const cipherDomain = await this.cipherService.get(this.cipherId, activeUserId);
- const cipherView = await this.cipherService.decrypt(cipherDomain, activeUserId);
- const orgs = await firstValueFrom(this.organizations$);
- const orgName =
- orgs.find((o) => o.id === this.organizationId)?.name ?? this.i18nService.t("organization");
-
- try {
- this.formPromise = this.cipherService
- .shareWithServer(cipherView, this.organizationId, selectedCollectionIds, activeUserId)
- .then(async () => {
- this.onSharedCipher.emit();
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("movedItemToOrg", cipherView.name, orgName),
- );
- });
- await this.formPromise;
- return true;
- } catch (e) {
- this.logService.error(e);
- }
- return false;
- }
-
- get canSave() {
- if (this.collections != null) {
- for (let i = 0; i < this.collections.length; i++) {
- if (this.collections[i].checked) {
- return true;
- }
- }
- }
- return false;
- }
-}
diff --git a/libs/angular/src/vault/components/add-edit-custom-fields.component.ts b/libs/angular/src/vault/components/add-edit-custom-fields.component.ts
deleted file mode 100644
index 9f774ca3c24..00000000000
--- a/libs/angular/src/vault/components/add-edit-custom-fields.component.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop";
-import { Directive, Input, OnChanges, SimpleChanges } from "@angular/core";
-
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { EventType } from "@bitwarden/common/enums";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { Utils } from "@bitwarden/common/platform/misc/utils";
-import { FieldType, CipherType } from "@bitwarden/common/vault/enums";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
-
-@Directive()
-export class AddEditCustomFieldsComponent implements OnChanges {
- @Input() cipher: CipherView;
- @Input() thisCipherType: CipherType;
- @Input() editMode: boolean;
-
- addFieldType: FieldType = FieldType.Text;
- addFieldTypeOptions: any[];
- addFieldLinkedTypeOption: any;
- linkedFieldOptions: any[] = [];
-
- cipherType = CipherType;
- fieldType = FieldType;
- eventType = EventType;
-
- constructor(
- private i18nService: I18nService,
- private eventCollectionService: EventCollectionService,
- ) {
- this.addFieldTypeOptions = [
- { name: i18nService.t("cfTypeText"), value: FieldType.Text },
- { name: i18nService.t("cfTypeHidden"), value: FieldType.Hidden },
- { name: i18nService.t("cfTypeBoolean"), value: FieldType.Boolean },
- ];
- this.addFieldLinkedTypeOption = {
- name: this.i18nService.t("cfTypeLinked"),
- value: FieldType.Linked,
- };
- }
-
- ngOnChanges(changes: SimpleChanges) {
- if (changes.thisCipherType != null) {
- this.setLinkedFieldOptions();
-
- if (!changes.thisCipherType.firstChange) {
- this.resetCipherLinkedFields();
- }
- }
- }
-
- addField() {
- if (this.cipher.fields == null) {
- this.cipher.fields = [];
- }
-
- const f = new FieldView();
- f.type = this.addFieldType;
- f.newField = true;
-
- if (f.type === FieldType.Linked) {
- f.linkedId = this.linkedFieldOptions[0].value;
- }
-
- this.cipher.fields.push(f);
- }
-
- removeField(field: FieldView) {
- const i = this.cipher.fields.indexOf(field);
- if (i > -1) {
- this.cipher.fields.splice(i, 1);
- }
- }
-
- toggleFieldValue(field: FieldView) {
- const f = field as any;
- f.showValue = !f.showValue;
- if (this.editMode && f.showValue) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(
- EventType.Cipher_ClientToggledHiddenFieldVisible,
- this.cipher.id,
- );
- }
- }
-
- trackByFunction(index: number, item: any) {
- return index;
- }
-
- drop(event: CdkDragDrop) {
- moveItemInArray(this.cipher.fields, event.previousIndex, event.currentIndex);
- }
-
- private setLinkedFieldOptions() {
- if (this.cipher.linkedFieldOptions == null) {
- return;
- }
-
- const options: any = [];
- this.cipher.linkedFieldOptions.forEach((linkedFieldOption, id) =>
- options.push({ name: this.i18nService.t(linkedFieldOption.i18nKey), value: id }),
- );
- this.linkedFieldOptions = options.sort(Utils.getSortFunction(this.i18nService, "name"));
- }
-
- private resetCipherLinkedFields() {
- if (this.cipher.fields == null || this.cipher.fields.length === 0) {
- return;
- }
-
- // Delete any Linked custom fields if the item type does not support them
- if (this.cipher.linkedFieldOptions == null) {
- this.cipher.fields = this.cipher.fields.filter((f) => f.type !== FieldType.Linked);
- return;
- }
-
- this.cipher.fields
- .filter((f) => f.type === FieldType.Linked)
- .forEach((f) => (f.linkedId = this.linkedFieldOptions[0].value));
- }
-}
diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts
deleted file mode 100644
index 3541fa0c8e8..00000000000
--- a/libs/angular/src/vault/components/add-edit.component.ts
+++ /dev/null
@@ -1,855 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { DatePipe } from "@angular/common";
-import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
-import { concatMap, firstValueFrom, map, Observable, Subject, switchMap, takeUntil } from "rxjs";
-
-// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
-// eslint-disable-next-line no-restricted-imports
-import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
-import { AuditService } from "@bitwarden/common/abstractions/audit.service";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
-import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums";
-import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
-import { EventType } from "@bitwarden/common/enums";
-import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
-import { Utils } from "@bitwarden/common/platform/misc/utils";
-import { UserId } from "@bitwarden/common/types/guid";
-import {
- CipherService,
- EncryptionContext,
-} from "@bitwarden/common/vault/abstractions/cipher.service";
-import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
-import { CipherType, SecureNoteType } from "@bitwarden/common/vault/enums";
-import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
-import { CardView } from "@bitwarden/common/vault/models/view/card.view";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
-import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
-import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
-import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
-import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
-import { SshKeyView } from "@bitwarden/common/vault/models/view/ssh-key.view";
-import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { generate_ssh_key } from "@bitwarden/sdk-internal";
-// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
-// eslint-disable-next-line no-restricted-imports
-import { PasswordRepromptService, SshImportPromptService } from "@bitwarden/vault";
-
-@Directive()
-export class AddEditComponent implements OnInit, OnDestroy {
- @Input() cloneMode = false;
- @Input() folderId: string = null;
- @Input() cipherId: string;
- @Input() type: CipherType;
- @Input() collectionIds: string[];
- @Input() organizationId: string = null;
- @Input() collectionId: string = null;
- @Output() onSavedCipher = new EventEmitter();
- @Output() onDeletedCipher = new EventEmitter();
- @Output() onRestoredCipher = new EventEmitter();
- @Output() onCancelled = new EventEmitter();
- @Output() onEditAttachments = new EventEmitter();
- @Output() onShareCipher = new EventEmitter();
- @Output() onEditCollections = new EventEmitter();
- @Output() onGeneratePassword = new EventEmitter();
- @Output() onGenerateUsername = new EventEmitter();
-
- canDeleteCipher$: Observable;
-
- editMode = false;
- cipher: CipherView;
- folders$: Observable;
- collections: CollectionView[] = [];
- title: string;
- formPromise: Promise;
- deletePromise: Promise;
- restorePromise: Promise;
- checkPasswordPromise: Promise;
- showPassword = false;
- showPrivateKey = false;
- showTotpSeed = false;
- showCardNumber = false;
- showCardCode = false;
- cipherType = CipherType;
- cardBrandOptions: any[];
- cardExpMonthOptions: any[];
- identityTitleOptions: any[];
- uriMatchOptions: any[];
- ownershipOptions: any[] = [];
- autofillOnPageLoadOptions: any[];
- currentDate = new Date();
- allowPersonal = true;
- reprompt = false;
- canUseReprompt = true;
- organization: Organization;
- /**
- * Flag to determine if the action is being performed from the admin console.
- */
- isAdminConsoleAction: boolean = false;
-
- protected componentName = "";
- protected destroy$ = new Subject();
- protected writeableCollections: CollectionView[];
- private organizationDataOwnershipAppliesToUser: boolean;
- private previousCipherId: string;
-
- get fido2CredentialCreationDateValue(): string {
- const dateCreated = this.i18nService.t("dateCreated");
- const creationDate = this.datePipe.transform(
- this.cipher?.login?.fido2Credentials?.[0]?.creationDate,
- "short",
- );
- return `${dateCreated} ${creationDate}`;
- }
-
- constructor(
- protected cipherService: CipherService,
- protected folderService: FolderService,
- protected i18nService: I18nService,
- protected platformUtilsService: PlatformUtilsService,
- protected auditService: AuditService,
- protected accountService: AccountService,
- protected collectionService: CollectionService,
- protected messagingService: MessagingService,
- protected eventCollectionService: EventCollectionService,
- protected policyService: PolicyService,
- protected logService: LogService,
- protected passwordRepromptService: PasswordRepromptService,
- private organizationService: OrganizationService,
- protected dialogService: DialogService,
- protected win: Window,
- protected datePipe: DatePipe,
- protected configService: ConfigService,
- protected cipherAuthorizationService: CipherAuthorizationService,
- protected toastService: ToastService,
- protected sdkService: SdkService,
- private sshImportPromptService: SshImportPromptService,
- ) {
- this.cardBrandOptions = [
- { name: "-- " + i18nService.t("select") + " --", value: null },
- { name: "Visa", value: "Visa" },
- { name: "Mastercard", value: "Mastercard" },
- { name: "American Express", value: "Amex" },
- { name: "Discover", value: "Discover" },
- { name: "Diners Club", value: "Diners Club" },
- { name: "JCB", value: "JCB" },
- { name: "Maestro", value: "Maestro" },
- { name: "UnionPay", value: "UnionPay" },
- { name: "RuPay", value: "RuPay" },
- { name: i18nService.t("other"), value: "Other" },
- ];
- this.cardExpMonthOptions = [
- { name: "-- " + i18nService.t("select") + " --", value: null },
- { name: "01 - " + i18nService.t("january"), value: "1" },
- { name: "02 - " + i18nService.t("february"), value: "2" },
- { name: "03 - " + i18nService.t("march"), value: "3" },
- { name: "04 - " + i18nService.t("april"), value: "4" },
- { name: "05 - " + i18nService.t("may"), value: "5" },
- { name: "06 - " + i18nService.t("june"), value: "6" },
- { name: "07 - " + i18nService.t("july"), value: "7" },
- { name: "08 - " + i18nService.t("august"), value: "8" },
- { name: "09 - " + i18nService.t("september"), value: "9" },
- { name: "10 - " + i18nService.t("october"), value: "10" },
- { name: "11 - " + i18nService.t("november"), value: "11" },
- { name: "12 - " + i18nService.t("december"), value: "12" },
- ];
- this.identityTitleOptions = [
- { name: "-- " + i18nService.t("select") + " --", value: null },
- { name: i18nService.t("mr"), value: i18nService.t("mr") },
- { name: i18nService.t("mrs"), value: i18nService.t("mrs") },
- { name: i18nService.t("ms"), value: i18nService.t("ms") },
- { name: i18nService.t("mx"), value: i18nService.t("mx") },
- { name: i18nService.t("dr"), value: i18nService.t("dr") },
- ];
- this.uriMatchOptions = [
- { name: i18nService.t("defaultMatchDetection"), value: null },
- { name: i18nService.t("baseDomain"), value: UriMatchStrategy.Domain },
- { name: i18nService.t("host"), value: UriMatchStrategy.Host },
- { name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
- { name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
- { name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
- { name: i18nService.t("never"), value: UriMatchStrategy.Never },
- ];
- this.autofillOnPageLoadOptions = [
- { name: i18nService.t("autoFillOnPageLoadUseDefault"), value: null },
- { name: i18nService.t("autoFillOnPageLoadYes"), value: true },
- { name: i18nService.t("autoFillOnPageLoadNo"), value: false },
- ];
- }
-
- async ngOnInit() {
- this.accountService.activeAccount$
- .pipe(
- getUserId,
- switchMap((userId) =>
- this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
- ),
- concatMap(async (policyAppliesToActiveUser) => {
- this.organizationDataOwnershipAppliesToUser = policyAppliesToActiveUser;
- await this.init();
- }),
- takeUntil(this.destroy$),
- )
- .subscribe();
-
- this.writeableCollections = await this.loadCollections();
- this.canUseReprompt = await this.passwordRepromptService.enabled();
- }
-
- ngOnDestroy() {
- this.destroy$.next();
- this.destroy$.complete();
- }
-
- async init() {
- if (this.ownershipOptions.length) {
- this.ownershipOptions = [];
- }
- if (this.organizationDataOwnershipAppliesToUser) {
- this.allowPersonal = false;
- } else {
- const myEmail = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((a) => a?.email)),
- );
- this.ownershipOptions.push({ name: myEmail, value: null });
- }
-
- const userId = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((account) => account?.id)),
- );
- const orgs = await firstValueFrom(this.organizationService.organizations$(userId));
- orgs
- .filter((org) => org.isMember)
- .sort(Utils.getSortFunction(this.i18nService, "name"))
- .forEach((o) => {
- if (o.enabled && o.status === OrganizationUserStatusType.Confirmed) {
- this.ownershipOptions.push({ name: o.name, value: o.id });
- }
- });
- if (!this.allowPersonal && this.organizationId == undefined) {
- this.organizationId = this.defaultOwnerId;
- }
- }
-
- async load() {
- this.editMode = this.cipherId != null;
- if (this.editMode) {
- this.editMode = true;
- if (this.cloneMode) {
- this.cloneMode = true;
- this.title = this.i18nService.t("addItem");
- } else {
- this.title = this.i18nService.t("editItem");
- }
- } else {
- this.title = this.i18nService.t("addItem");
- }
-
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
-
- const loadedAddEditCipherInfo = await this.loadAddEditCipherInfo(activeUserId);
-
- if (this.cipher == null) {
- if (this.editMode) {
- const cipher = await this.loadCipher(activeUserId);
- this.cipher = await this.cipherService.decrypt(cipher, activeUserId);
-
- // Adjust Cipher Name if Cloning
- if (this.cloneMode) {
- this.cipher.name += " - " + this.i18nService.t("clone");
- // If not allowing personal ownership, update cipher's org Id to prompt downstream changes
- if (this.cipher.organizationId == null && !this.allowPersonal) {
- this.cipher.organizationId = this.organizationId;
- }
- }
- } else {
- this.cipher = new CipherView();
- this.cipher.organizationId = this.organizationId == null ? null : this.organizationId;
- this.cipher.folderId = this.folderId;
- this.cipher.type = this.type == null ? CipherType.Login : this.type;
- this.cipher.login = new LoginView();
- this.cipher.login.uris = [new LoginUriView()];
- this.cipher.card = new CardView();
- this.cipher.identity = new IdentityView();
- this.cipher.secureNote = new SecureNoteView();
- this.cipher.secureNote.type = SecureNoteType.Generic;
- this.cipher.sshKey = new SshKeyView();
- this.cipher.reprompt = CipherRepromptType.None;
- }
- }
-
- if (this.cipher != null && (!this.editMode || loadedAddEditCipherInfo || this.cloneMode)) {
- await this.organizationChanged();
- if (
- this.collectionIds != null &&
- this.collectionIds.length > 0 &&
- this.collections.length > 0
- ) {
- this.collections.forEach((c) => {
- if (this.collectionIds.indexOf(c.id) > -1) {
- (c as any).checked = true;
- }
- });
- }
- }
- // Only Admins can clone a cipher to different owner
- if (this.cloneMode && this.cipher.organizationId != null) {
- const activeUserId = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((a) => a?.id)),
- );
-
- const cipherOrg = (
- await firstValueFrom(this.organizationService.memberOrganizations$(activeUserId))
- ).find((o) => o.id === this.cipher.organizationId);
-
- if (cipherOrg != null && !cipherOrg.isAdmin && !cipherOrg.permissions.editAnyCollection) {
- this.ownershipOptions = [{ name: cipherOrg.name, value: cipherOrg.id }];
- }
- }
-
- // We don't want to copy passkeys when we clone a cipher
- if (this.cloneMode && this.cipher?.login?.hasFido2Credentials) {
- this.cipher.login.fido2Credentials = null;
- }
-
- this.folders$ = this.folderService.folderViews$(activeUserId);
-
- if (this.editMode && this.previousCipherId !== this.cipherId) {
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientViewed, [this.cipher]);
- }
- this.previousCipherId = this.cipherId;
- this.reprompt = this.cipher.reprompt !== CipherRepromptType.None;
- if (this.reprompt) {
- this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[2].value;
- }
-
- this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(
- this.cipher,
- this.isAdminConsoleAction,
- );
-
- if (!this.editMode || this.cloneMode) {
- // Creating an ssh key directly while filtering to the ssh key category
- // must force a key to be set. SSH keys must never be created with an empty private key field
- if (
- this.cipher.type === CipherType.SshKey &&
- (this.cipher.sshKey.privateKey == null || this.cipher.sshKey.privateKey === "")
- ) {
- await this.generateSshKey(false);
- }
- }
- }
-
- async submit(): Promise {
- if (this.cipher.isDeleted) {
- return this.restore();
- }
-
- // normalize card expiry year on save
- if (this.cipher.type === this.cipherType.Card) {
- this.cipher.card.expYear = normalizeExpiryYearFormat(this.cipher.card.expYear);
- }
-
- // trim whitespace from the TOTP field
- if (this.cipher.type === this.cipherType.Login && this.cipher.login.totp) {
- this.cipher.login.totp = this.cipher.login.totp.trim();
- }
-
- if (this.cipher.name == null || this.cipher.name === "") {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("nameRequired"),
- });
- return false;
- }
-
- if (
- (!this.editMode || this.cloneMode) &&
- !this.allowPersonal &&
- this.cipher.organizationId == null
- ) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("personalOwnershipSubmitError"),
- });
- return false;
- }
-
- if (
- (!this.editMode || this.cloneMode) &&
- this.cipher.type === CipherType.Login &&
- this.cipher.login.uris != null &&
- this.cipher.login.uris.length === 1 &&
- (this.cipher.login.uris[0].uri == null || this.cipher.login.uris[0].uri === "")
- ) {
- this.cipher.login.uris = [];
- }
-
- // Allows saving of selected collections during "Add" and "Clone" flows
- if ((!this.editMode || this.cloneMode) && this.cipher.organizationId != null) {
- this.cipher.collectionIds =
- this.collections == null
- ? []
- : this.collections.filter((c) => (c as any).checked).map((c) => c.id);
- }
-
- // Clear current Cipher Id if exists to trigger "Add" cipher flow
- if (this.cloneMode) {
- this.cipher.id = null;
- }
-
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const cipher = await this.encryptCipher(activeUserId);
-
- try {
- this.formPromise = this.saveCipher(cipher);
- const savedCipher = await this.formPromise;
-
- // Reset local cipher from the saved cipher returned from the server
- this.cipher = await savedCipher.decrypt(
- await this.cipherService.getKeyForCipherKeyDecryption(savedCipher, activeUserId),
- );
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t(this.editMode && !this.cloneMode ? "editedItem" : "addedItem"),
- });
- this.onSavedCipher.emit(this.cipher);
- this.messagingService.send(this.editMode && !this.cloneMode ? "editedCipher" : "addedCipher");
- return true;
- } catch (e) {
- this.logService.error(e);
- }
-
- return false;
- }
-
- addUri() {
- if (this.cipher.type !== CipherType.Login) {
- return;
- }
-
- if (this.cipher.login.uris == null) {
- this.cipher.login.uris = [];
- }
-
- this.cipher.login.uris.push(new LoginUriView());
- }
-
- removeUri(uri: LoginUriView) {
- if (this.cipher.type !== CipherType.Login || this.cipher.login.uris == null) {
- return;
- }
-
- const i = this.cipher.login.uris.indexOf(uri);
- if (i > -1) {
- this.cipher.login.uris.splice(i, 1);
- }
- }
-
- removePasskey() {
- if (this.cipher.type !== CipherType.Login || this.cipher.login.fido2Credentials == null) {
- return;
- }
-
- this.cipher.login.fido2Credentials = null;
- }
-
- onCardNumberChange(): void {
- this.cipher.card.brand = CardView.getCardBrandByPatterns(this.cipher.card.number);
- }
-
- getCardExpMonthDisplay() {
- return this.cardExpMonthOptions.find((x) => x.value == this.cipher.card.expMonth)?.name;
- }
-
- trackByFunction(index: number, item: any) {
- return index;
- }
-
- cancel() {
- this.onCancelled.emit(this.cipher);
- }
-
- attachments() {
- this.onEditAttachments.emit(this.cipher);
- }
-
- share() {
- this.onShareCipher.emit(this.cipher);
- }
-
- editCollections() {
- this.onEditCollections.emit(this.cipher);
- }
-
- async delete(): Promise {
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "deleteItem" },
- content: {
- key: this.cipher.isDeleted ? "permanentlyDeleteItemConfirmation" : "deleteItemConfirmation",
- },
- type: "warning",
- });
-
- if (!confirmed) {
- return false;
- }
-
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- this.deletePromise = this.deleteCipher(activeUserId);
- await this.deletePromise;
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t(
- this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem",
- ),
- });
- this.onDeletedCipher.emit(this.cipher);
- this.messagingService.send(
- this.cipher.isDeleted ? "permanentlyDeletedCipher" : "deletedCipher",
- );
- } catch (e) {
- this.logService.error(e);
- }
-
- return true;
- }
-
- async restore(): Promise {
- if (!this.cipher.isDeleted) {
- return false;
- }
-
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- this.restorePromise = this.restoreCipher(activeUserId);
- await this.restorePromise;
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("restoredItem"),
- });
- this.onRestoredCipher.emit(this.cipher);
- this.messagingService.send("restoredCipher");
- } catch (e) {
- this.logService.error(e);
- }
-
- return true;
- }
-
- async generateUsername(): Promise {
- if (this.cipher.login?.username?.length) {
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "overwriteUsername" },
- content: { key: "overwriteUsernameConfirmation" },
- type: "warning",
- });
-
- if (!confirmed) {
- return false;
- }
- }
-
- this.onGenerateUsername.emit();
- return true;
- }
-
- async generatePassword(): Promise {
- if (this.cipher.login?.password?.length) {
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "overwritePassword" },
- content: { key: "overwritePasswordConfirmation" },
- type: "warning",
- });
-
- if (!confirmed) {
- return false;
- }
- }
-
- this.onGeneratePassword.emit();
- return true;
- }
-
- togglePassword() {
- this.showPassword = !this.showPassword;
-
- if (this.editMode && this.showPassword) {
- document.getElementById("loginPassword")?.focus();
-
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientToggledPasswordVisible, [
- this.cipher,
- ]);
- }
- }
-
- toggleTotpSeed() {
- this.showTotpSeed = !this.showTotpSeed;
-
- if (this.editMode && this.showTotpSeed) {
- document.getElementById("loginTotp")?.focus();
-
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientToggledTOTPSeedVisible, [
- this.cipher,
- ]);
- }
- }
-
- async toggleCardNumber() {
- this.showCardNumber = !this.showCardNumber;
- if (this.showCardNumber) {
- void this.eventCollectionService.collectMany(
- EventType.Cipher_ClientToggledCardNumberVisible,
- [this.cipher],
- );
- }
- }
-
- toggleCardCode() {
- this.showCardCode = !this.showCardCode;
- document.getElementById("cardCode").focus();
- if (this.editMode && this.showCardCode) {
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientToggledCardCodeVisible, [
- this.cipher,
- ]);
- }
- }
-
- togglePrivateKey() {
- this.showPrivateKey = !this.showPrivateKey;
- }
-
- toggleUriOptions(uri: LoginUriView) {
- const u = uri as any;
- u.showOptions = u.showOptions == null && uri.match != null ? false : !u.showOptions;
- }
-
- loginUriMatchChanged(uri: LoginUriView) {
- const u = uri as any;
- u.showOptions = u.showOptions == null ? true : u.showOptions;
- }
-
- async organizationChanged() {
- if (this.writeableCollections != null) {
- this.writeableCollections.forEach((c) => ((c as any).checked = false));
- }
- if (this.cipher.organizationId != null) {
- this.collections = this.writeableCollections?.filter(
- (c) => c.organizationId === this.cipher.organizationId,
- );
- // If there's only one collection, check it by default
- if (this.collections.length === 1) {
- (this.collections[0] as any).checked = true;
- }
- const activeUserId = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((a) => a?.id)),
- );
-
- const org = (
- await firstValueFrom(this.organizationService.organizations$(activeUserId))
- ).find((org) => org.id === this.cipher.organizationId);
- if (org != null) {
- this.cipher.organizationUseTotp = org.useTotp;
- }
- } else {
- this.collections = [];
- }
- }
-
- async checkPassword() {
- if (this.checkPasswordPromise != null) {
- return;
- }
-
- if (
- this.cipher.login == null ||
- this.cipher.login.password == null ||
- this.cipher.login.password === ""
- ) {
- return;
- }
-
- this.checkPasswordPromise = this.auditService.passwordLeaked(this.cipher.login.password);
- const matches = await this.checkPasswordPromise;
- this.checkPasswordPromise = null;
-
- if (matches > 0) {
- this.toastService.showToast({
- variant: "warning",
- title: null,
- message: this.i18nService.t("passwordExposed", matches.toString()),
- });
- } else {
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("passwordSafe"),
- });
- }
- }
-
- repromptChanged() {
- this.reprompt = !this.reprompt;
- if (this.reprompt) {
- this.cipher.reprompt = CipherRepromptType.Password;
- this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[2].value;
- } else {
- this.cipher.reprompt = CipherRepromptType.None;
- this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[0].value;
- }
- }
-
- protected async loadCollections() {
- const allCollections = await this.collectionService.getAllDecrypted();
- return allCollections.filter((c) => !c.readOnly);
- }
-
- protected loadCipher(userId: UserId) {
- return this.cipherService.get(this.cipherId, userId);
- }
-
- protected encryptCipher(userId: UserId) {
- return this.cipherService.encrypt(this.cipher, userId);
- }
-
- protected saveCipher(data: EncryptionContext) {
- let orgAdmin = this.organization?.canEditAllCiphers;
-
- // if a cipher is unassigned we want to check if they are an admin or have permission to edit any collection
- if (!data.cipher.collectionIds) {
- orgAdmin = this.organization?.canEditUnassignedCiphers;
- }
-
- return this.cipher.id == null
- ? this.cipherService.createWithServer(data, orgAdmin)
- : this.cipherService.updateWithServer(data, orgAdmin);
- }
-
- protected deleteCipher(userId: UserId) {
- return this.cipher.isDeleted
- ? this.cipherService.deleteWithServer(this.cipher.id, userId, this.asAdmin)
- : this.cipherService.softDeleteWithServer(this.cipher.id, userId, this.asAdmin);
- }
-
- protected restoreCipher(userId: UserId) {
- return this.cipherService.restoreWithServer(this.cipher.id, userId, this.asAdmin);
- }
-
- /**
- * Determines if a cipher must be deleted as an admin by belonging to an organization and being unassigned to a collection.
- */
- get asAdmin(): boolean {
- return (
- this.cipher.organizationId !== null &&
- this.cipher.organizationId.length > 0 &&
- (this.organization?.canEditAllCiphers ||
- !this.cipher.collectionIds ||
- this.cipher.collectionIds.length === 0)
- );
- }
-
- get defaultOwnerId(): string | null {
- return this.ownershipOptions[0].value;
- }
-
- async loadAddEditCipherInfo(userId: UserId): Promise {
- const addEditCipherInfo: any = await firstValueFrom(
- this.cipherService.addEditCipherInfo$(userId),
- );
- const loadedSavedInfo = addEditCipherInfo != null;
-
- if (loadedSavedInfo) {
- this.cipher = addEditCipherInfo.cipher;
- this.collectionIds = addEditCipherInfo.collectionIds;
-
- if (!this.editMode && !this.allowPersonal && this.cipher.organizationId == null) {
- // This is a new cipher and personal ownership isn't allowed, so we need to set the default owner
- this.cipher.organizationId = this.defaultOwnerId;
- }
- }
-
- await this.cipherService.setAddEditCipherInfo(null, userId);
-
- return loadedSavedInfo;
- }
-
- async copy(value: string, typeI18nKey: string, aType: string): Promise {
- if (value == null) {
- return false;
- }
-
- const copyOptions = this.win != null ? { window: this.win } : null;
- this.platformUtilsService.copyToClipboard(value, copyOptions);
- this.toastService.showToast({
- variant: "info",
- title: null,
- message: this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
- });
-
- if (typeI18nKey === "password") {
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientCopiedPassword, [
- this.cipher,
- ]);
- } else if (typeI18nKey === "securityCode") {
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientCopiedCardCode, [
- this.cipher,
- ]);
- } else if (aType === "H_Field") {
- void this.eventCollectionService.collectMany(EventType.Cipher_ClientCopiedHiddenField, [
- this.cipher,
- ]);
- }
-
- return true;
- }
-
- async importSshKeyFromClipboard() {
- const key = await this.sshImportPromptService.importSshKeyFromClipboard();
- if (key != null) {
- this.cipher.sshKey.privateKey = key.privateKey;
- this.cipher.sshKey.publicKey = key.publicKey;
- this.cipher.sshKey.keyFingerprint = key.keyFingerprint;
- }
- }
-
- private async generateSshKey(showNotification: boolean = true) {
- await firstValueFrom(this.sdkService.client$);
- const sshKey = generate_ssh_key("Ed25519");
- this.cipher.sshKey.privateKey = sshKey.privateKey;
- this.cipher.sshKey.publicKey = sshKey.publicKey;
- this.cipher.sshKey.keyFingerprint = sshKey.fingerprint;
-
- if (showNotification) {
- this.toastService.showToast({
- variant: "success",
- title: "",
- message: this.i18nService.t("sshKeyGenerated"),
- });
- }
- }
-
- async typeChange() {
- if (this.cipher.type === CipherType.SshKey) {
- await this.generateSshKey();
- }
- }
-}
diff --git a/libs/angular/src/vault/components/attachments.component.ts b/libs/angular/src/vault/components/attachments.component.ts
deleted file mode 100644
index e4b01d3aac1..00000000000
--- a/libs/angular/src/vault/components/attachments.component.ts
+++ /dev/null
@@ -1,354 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
-import { firstValueFrom } from "rxjs";
-
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
-import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
-import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
-import { CipherId, UserId } from "@bitwarden/common/types/guid";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
-import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
-import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { KeyService } from "@bitwarden/key-management";
-
-@Directive()
-export class AttachmentsComponent implements OnInit {
- @Input() cipherId: string;
- @Input() viewOnly: boolean;
- @Output() onUploadedAttachment = new EventEmitter();
- @Output() onDeletedAttachment = new EventEmitter();
- @Output() onReuploadedAttachment = new EventEmitter();
-
- cipher: CipherView;
- cipherDomain: Cipher;
- canAccessAttachments: boolean;
- formPromise: Promise;
- deletePromises: { [id: string]: Promise } = {};
- reuploadPromises: { [id: string]: Promise } = {};
- emergencyAccessId?: string = null;
- protected componentName = "";
-
- constructor(
- protected cipherService: CipherService,
- protected i18nService: I18nService,
- protected keyService: KeyService,
- protected encryptService: EncryptService,
- protected platformUtilsService: PlatformUtilsService,
- protected apiService: ApiService,
- protected win: Window,
- protected logService: LogService,
- protected stateService: StateService,
- protected fileDownloadService: FileDownloadService,
- protected dialogService: DialogService,
- protected billingAccountProfileStateService: BillingAccountProfileStateService,
- protected accountService: AccountService,
- protected toastService: ToastService,
- protected configService: ConfigService,
- ) {}
-
- async ngOnInit() {
- await this.init();
- }
-
- async submit() {
- const fileEl = document.getElementById("file") as HTMLInputElement;
- const files = fileEl.files;
- if (files == null || files.length === 0) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("selectFile"),
- });
- return;
- }
-
- if (files[0].size > 524288000) {
- // 500 MB
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("maxFileSize"),
- });
- return;
- }
-
- try {
- const activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
- this.formPromise = this.saveCipherAttachment(files[0], activeUserId);
- this.cipherDomain = await this.formPromise;
- this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("attachmentSaved"),
- });
- this.onUploadedAttachment.emit(this.cipher);
- } catch (e) {
- this.logService.error(e);
- }
-
- // reset file input
- // ref: https://stackoverflow.com/a/20552042
- fileEl.type = "";
- fileEl.type = "file";
- fileEl.value = "";
- }
-
- async delete(attachment: AttachmentView) {
- if (this.deletePromises[attachment.id] != null) {
- return;
- }
-
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "deleteAttachment" },
- content: { key: "deleteAttachmentConfirmation" },
- type: "warning",
- });
-
- if (!confirmed) {
- return;
- }
-
- try {
- const activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
-
- this.deletePromises[attachment.id] = this.deleteCipherAttachment(attachment.id, activeUserId);
- const updatedCipher = await this.deletePromises[attachment.id];
-
- const cipher = new Cipher(updatedCipher);
- this.cipher = await this.cipherService.decrypt(cipher, activeUserId);
-
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("deletedAttachment"),
- });
- const i = this.cipher.attachments.indexOf(attachment);
- if (i > -1) {
- this.cipher.attachments.splice(i, 1);
- }
- } catch (e) {
- this.logService.error(e);
- }
-
- this.deletePromises[attachment.id] = null;
- this.onDeletedAttachment.emit(this.cipher);
- }
-
- async download(attachment: AttachmentView) {
- const a = attachment as any;
- if (a.downloading) {
- return;
- }
-
- if (!this.canAccessAttachments) {
- 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,
- this.emergencyAccessId,
- );
- 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;
- }
- }
-
- a.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"),
- });
- a.downloading = false;
- return;
- }
-
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const decBuf = await this.cipherService.getDecryptedAttachmentBuffer(
- this.cipherDomain.id as CipherId,
- attachment,
- response,
- activeUserId,
- );
-
- this.fileDownloadService.download({
- fileName: attachment.fileName,
- blobData: decBuf,
- });
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("fileSavedToDevice"),
- });
- // FIXME: Remove when updating file. Eslint update
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (e) {
- this.toastService.showToast({
- variant: "error",
- title: null,
- message: this.i18nService.t("errorOccurred"),
- });
- }
-
- a.downloading = false;
- }
-
- protected async init() {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- this.cipherDomain = await this.loadCipher(activeUserId);
- this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
-
- const canAccessPremium = await firstValueFrom(
- this.billingAccountProfileStateService.hasPremiumFromAnySource$(activeUserId),
- );
- this.canAccessAttachments = canAccessPremium || this.cipher.organizationId != null;
-
- if (!this.canAccessAttachments) {
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "premiumRequired" },
- content: { key: "premiumRequiredDesc" },
- acceptButtonText: { key: "learnMore" },
- type: "success",
- });
-
- if (confirmed) {
- this.platformUtilsService.launchUri(
- "https://vault.bitwarden.com/#/settings/subscription/premium",
- );
- }
- }
- }
-
- protected async reuploadCipherAttachment(attachment: AttachmentView, admin: boolean) {
- const a = attachment as any;
- if (attachment.key != null || a.downloading || this.reuploadPromises[attachment.id] != null) {
- return;
- }
-
- try {
- this.reuploadPromises[attachment.id] = Promise.resolve().then(async () => {
- // 1. Download
- a.downloading = true;
- const response = await fetch(new Request(attachment.url, { cache: "no-store" }));
- if (response.status !== 200) {
- this.toastService.showToast({
- variant: "error",
- title: null,
- message: this.i18nService.t("errorOccurred"),
- });
- a.downloading = false;
- return;
- }
-
- try {
- // 2. Resave
- const activeUserId = await firstValueFrom(
- this.accountService.activeAccount$.pipe(getUserId),
- );
-
- const decBuf = await this.cipherService.getDecryptedAttachmentBuffer(
- this.cipherDomain.id as CipherId,
- attachment,
- response,
- activeUserId,
- );
-
- this.cipherDomain = await this.cipherService.saveAttachmentRawWithServer(
- this.cipherDomain,
- attachment.fileName,
- decBuf,
- activeUserId,
- admin,
- );
- this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
-
- // 3. Delete old
- this.deletePromises[attachment.id] = this.deleteCipherAttachment(
- attachment.id,
- activeUserId,
- );
- await this.deletePromises[attachment.id];
- const foundAttachment = this.cipher.attachments.filter((a2) => a2.id === attachment.id);
- if (foundAttachment.length > 0) {
- const i = this.cipher.attachments.indexOf(foundAttachment[0]);
- if (i > -1) {
- this.cipher.attachments.splice(i, 1);
- }
- }
-
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("attachmentSaved"),
- });
- this.onReuploadedAttachment.emit();
- // FIXME: Remove when updating file. Eslint update
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (e) {
- this.toastService.showToast({
- variant: "error",
- title: null,
- message: this.i18nService.t("errorOccurred"),
- });
- }
-
- a.downloading = false;
- });
- await this.reuploadPromises[attachment.id];
- } catch (e) {
- this.logService.error(e);
- }
- }
-
- protected loadCipher(userId: UserId) {
- return this.cipherService.get(this.cipherId, userId);
- }
-
- protected saveCipherAttachment(file: File, userId: UserId) {
- return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, userId);
- }
-
- protected deleteCipherAttachment(attachmentId: string, userId: UserId) {
- return this.cipherService.deleteAttachmentWithServer(
- this.cipher.id,
- attachmentId,
- userId,
- false,
- );
- }
-
- protected async reupload(attachment: AttachmentView) {
- // TODO: This should be removed but is needed since we re-use the same template
- }
-}
diff --git a/libs/angular/src/vault/components/password-history.component.ts b/libs/angular/src/vault/components/password-history.component.ts
deleted file mode 100644
index acb89b82191..00000000000
--- a/libs/angular/src/vault/components/password-history.component.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Directive, OnInit } from "@angular/core";
-import { firstValueFrom } from "rxjs";
-
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { PasswordHistoryView } from "@bitwarden/common/vault/models/view/password-history.view";
-import { ToastService } from "@bitwarden/components";
-
-@Directive()
-export class PasswordHistoryComponent implements OnInit {
- cipherId: string;
- history: PasswordHistoryView[] = [];
-
- constructor(
- protected cipherService: CipherService,
- protected platformUtilsService: PlatformUtilsService,
- protected i18nService: I18nService,
- protected accountService: AccountService,
- private win: Window,
- private toastService: ToastService,
- ) {}
-
- async ngOnInit() {
- await this.init();
- }
-
- copy(password: string) {
- const copyOptions = this.win != null ? { window: this.win } : null;
- this.platformUtilsService.copyToClipboard(password, copyOptions);
- this.toastService.showToast({
- variant: "info",
- title: null,
- message: this.i18nService.t("valueCopied", this.i18nService.t("password")),
- });
- }
-
- protected async init() {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const cipher = await this.cipherService.get(this.cipherId, activeUserId);
- const decCipher = await this.cipherService.decrypt(cipher, activeUserId);
- this.history = decCipher.passwordHistory == null ? [] : decCipher.passwordHistory;
- }
-}
diff --git a/libs/angular/src/vault/components/view-custom-fields.component.ts b/libs/angular/src/vault/components/view-custom-fields.component.ts
deleted file mode 100644
index d991a0260c1..00000000000
--- a/libs/angular/src/vault/components/view-custom-fields.component.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Directive, Input } from "@angular/core";
-
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { EventType } from "@bitwarden/common/enums";
-import { FieldType } from "@bitwarden/common/vault/enums";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
-
-@Directive()
-export class ViewCustomFieldsComponent {
- @Input() cipher: CipherView;
- @Input() promptPassword: () => Promise;
- @Input() copy: (value: string, typeI18nKey: string, aType: string) => void;
-
- fieldType = FieldType;
-
- constructor(private eventCollectionService: EventCollectionService) {}
-
- async toggleFieldValue(field: FieldView) {
- if (!(await this.promptPassword())) {
- return;
- }
-
- const f = field as any;
- f.showValue = !f.showValue;
- f.showCount = false;
- if (f.showValue) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(
- EventType.Cipher_ClientToggledHiddenFieldVisible,
- this.cipher.id,
- );
- }
- }
-
- async toggleFieldCount(field: FieldView) {
- if (!field.showValue) {
- return;
- }
-
- field.showCount = !field.showCount;
- }
-
- setTextDataOnDrag(event: DragEvent, data: string) {
- event.dataTransfer.setData("text", data);
- }
-}
diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts
deleted file mode 100644
index e3eb50cb29e..00000000000
--- a/libs/angular/src/vault/components/view.component.ts
+++ /dev/null
@@ -1,568 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { DatePipe } from "@angular/common";
-import {
- ChangeDetectorRef,
- Directive,
- EventEmitter,
- Input,
- NgZone,
- OnDestroy,
- OnInit,
- Output,
-} from "@angular/core";
-import {
- BehaviorSubject,
- combineLatest,
- filter,
- firstValueFrom,
- map,
- Observable,
- of,
- switchMap,
- tap,
-} from "rxjs";
-
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { AuditService } from "@bitwarden/common/abstractions/audit.service";
-import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
-import { EventType } from "@bitwarden/common/enums";
-import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
-import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
-import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
-import { CipherId, UserId } from "@bitwarden/common/types/guid";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
-import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
-import { CipherType, FieldType } from "@bitwarden/common/vault/enums";
-import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
-import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
-import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
-import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
-import { TotpInfo } from "@bitwarden/common/vault/services/totp.service";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { KeyService } from "@bitwarden/key-management";
-// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
-// eslint-disable-next-line no-restricted-imports
-import { PasswordRepromptService } from "@bitwarden/vault";
-
-const BroadcasterSubscriptionId = "BaseViewComponent";
-
-@Directive()
-export class ViewComponent implements OnDestroy, OnInit {
- /** Observable of cipherId$ that will update each time the `Input` updates */
- private _cipherId$ = new BehaviorSubject(null);
-
- @Input()
- set cipherId(value: string) {
- this._cipherId$.next(value);
- }
-
- get cipherId(): string {
- return this._cipherId$.getValue();
- }
-
- @Input() collectionId: string;
- @Output() onEditCipher = new EventEmitter();
- @Output() onCloneCipher = new EventEmitter();
- @Output() onShareCipher = new EventEmitter();
- @Output() onDeletedCipher = new EventEmitter();
- @Output() onRestoredCipher = new EventEmitter();
-
- canDeleteCipher$: Observable;
- canRestoreCipher$: Observable;
- cipher: CipherView;
- showPassword: boolean;
- showPasswordCount: boolean;
- showCardNumber: boolean;
- showCardCode: boolean;
- showPrivateKey: boolean;
- canAccessPremium: boolean;
- showPremiumRequiredTotp: boolean;
- fieldType = FieldType;
- checkPasswordPromise: Promise;
- folder: FolderView;
- cipherType = CipherType;
-
- private previousCipherId: string;
- protected passwordReprompted = false;
-
- /**
- * Represents TOTP information including display formatting and timing
- */
- protected totpInfo$: Observable | undefined;
-
- get fido2CredentialCreationDateValue(): string {
- const dateCreated = this.i18nService.t("dateCreated");
- const creationDate = this.datePipe.transform(
- this.cipher?.login?.fido2Credentials?.[0]?.creationDate,
- "short",
- );
- return `${dateCreated} ${creationDate}`;
- }
-
- constructor(
- protected cipherService: CipherService,
- protected folderService: FolderService,
- protected totpService: TotpService,
- protected tokenService: TokenService,
- protected i18nService: I18nService,
- protected keyService: KeyService,
- protected encryptService: EncryptService,
- protected platformUtilsService: PlatformUtilsService,
- protected auditService: AuditService,
- protected win: Window,
- protected broadcasterService: BroadcasterService,
- protected ngZone: NgZone,
- protected changeDetectorRef: ChangeDetectorRef,
- protected eventCollectionService: EventCollectionService,
- protected apiService: ApiService,
- protected passwordRepromptService: PasswordRepromptService,
- private logService: LogService,
- protected stateService: StateService,
- protected fileDownloadService: FileDownloadService,
- protected dialogService: DialogService,
- protected datePipe: DatePipe,
- protected accountService: AccountService,
- private billingAccountProfileStateService: BillingAccountProfileStateService,
- protected toastService: ToastService,
- private cipherAuthorizationService: CipherAuthorizationService,
- protected configService: ConfigService,
- ) {}
-
- ngOnInit() {
- this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.ngZone.run(async () => {
- switch (message.command) {
- case "syncCompleted":
- if (message.successfully) {
- this.changeDetectorRef.detectChanges();
- }
- break;
- }
- });
- });
-
- // Set up the subscription to the activeAccount$ and cipherId$ observables
- combineLatest([this.accountService.activeAccount$.pipe(getUserId), this._cipherId$])
- .pipe(
- tap(() => this.cleanUp()),
- switchMap(([userId, cipherId]) => {
- const cipher$ = this.cipherService.cipherViews$(userId).pipe(
- map((ciphers) => ciphers?.find((c) => c.id === cipherId)),
- filter((cipher) => !!cipher),
- );
- return combineLatest([of(userId), cipher$]);
- }),
- )
- .subscribe(([userId, cipher]) => {
- this.cipher = cipher;
-
- void this.constructCipherDetails(userId);
- });
- }
-
- ngOnDestroy() {
- this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
- this.cleanUp();
- }
-
- async edit() {
- this.onEditCipher.emit(this.cipher);
- }
-
- async clone() {
- if (this.cipher.login?.hasFido2Credentials) {
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "passkeyNotCopied" },
- content: { key: "passkeyNotCopiedAlert" },
- type: "info",
- });
-
- if (!confirmed) {
- return false;
- }
- }
-
- if (await this.promptPassword()) {
- this.onCloneCipher.emit(this.cipher);
- return true;
- }
-
- return false;
- }
-
- async share() {
- if (await this.promptPassword()) {
- this.onShareCipher.emit(this.cipher);
- return true;
- }
-
- return false;
- }
-
- async delete(): Promise {
- if (!(await this.promptPassword())) {
- return;
- }
-
- const confirmed = await this.dialogService.openSimpleDialog({
- title: { key: "deleteItem" },
- content: {
- key: this.cipher.isDeleted ? "permanentlyDeleteItemConfirmation" : "deleteItemConfirmation",
- },
- type: "warning",
- });
-
- if (!confirmed) {
- return false;
- }
-
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- await this.deleteCipher(activeUserId);
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t(
- this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem",
- ),
- });
- this.onDeletedCipher.emit(this.cipher);
- } catch (e) {
- this.logService.error(e);
- }
-
- return true;
- }
-
- async restore(): Promise {
- if (!this.cipher.isDeleted) {
- return false;
- }
-
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- await this.restoreCipher(activeUserId);
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("restoredItem"),
- });
- this.onRestoredCipher.emit(this.cipher);
- } catch (e) {
- this.logService.error(e);
- }
-
- return true;
- }
-
- async togglePassword() {
- if (!(await this.promptPassword())) {
- return;
- }
-
- this.showPassword = !this.showPassword;
- this.showPasswordCount = false;
- if (this.showPassword) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(
- EventType.Cipher_ClientToggledPasswordVisible,
- this.cipherId,
- );
- }
- }
-
- async togglePasswordCount() {
- if (!this.showPassword) {
- return;
- }
-
- this.showPasswordCount = !this.showPasswordCount;
- }
-
- async toggleCardNumber() {
- if (!(await this.promptPassword())) {
- return;
- }
-
- this.showCardNumber = !this.showCardNumber;
- if (this.showCardNumber) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(
- EventType.Cipher_ClientToggledCardNumberVisible,
- this.cipherId,
- );
- }
- }
-
- async toggleCardCode() {
- if (!(await this.promptPassword())) {
- return;
- }
-
- this.showCardCode = !this.showCardCode;
- if (this.showCardCode) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(
- EventType.Cipher_ClientToggledCardCodeVisible,
- this.cipherId,
- );
- }
- }
-
- togglePrivateKey() {
- this.showPrivateKey = !this.showPrivateKey;
- }
-
- async checkPassword() {
- if (
- this.cipher.login == null ||
- this.cipher.login.password == null ||
- this.cipher.login.password === ""
- ) {
- return;
- }
-
- this.checkPasswordPromise = this.auditService.passwordLeaked(this.cipher.login.password);
- const matches = await this.checkPasswordPromise;
-
- if (matches > 0) {
- this.toastService.showToast({
- variant: "warning",
- title: null,
- message: this.i18nService.t("passwordExposed", matches.toString()),
- });
- } else {
- this.toastService.showToast({
- variant: "success",
- title: null,
- message: this.i18nService.t("passwordSafe"),
- });
- }
- }
-
- async launch(uri: Launchable, cipherId?: string) {
- if (!uri.canLaunch) {
- return;
- }
-
- if (cipherId) {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- await this.cipherService.updateLastLaunchedDate(cipherId, activeUserId);
- }
-
- this.platformUtilsService.launchUri(uri.launchUri);
- }
-
- async copy(value: string, typeI18nKey: string, aType: string): Promise {
- if (value == null) {
- return false;
- }
-
- if (
- this.passwordRepromptService.protectedFields().includes(aType) &&
- !(await this.promptPassword())
- ) {
- return false;
- }
-
- const copyOptions = this.win != null ? { window: this.win } : null;
- this.platformUtilsService.copyToClipboard(value, copyOptions);
- this.toastService.showToast({
- variant: "info",
- title: null,
- message: this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
- });
-
- if (typeI18nKey === "password") {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, this.cipherId);
- } else if (typeI18nKey === "securityCode") {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, this.cipherId);
- } else if (aType === "H_Field") {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientCopiedHiddenField, this.cipherId);
- }
-
- return true;
- }
-
- setTextDataOnDrag(event: DragEvent, data: string) {
- event.dataTransfer.setData("text", data);
- }
-
- async downloadAttachment(attachment: AttachmentView) {
- if (!(await this.promptPassword())) {
- return;
- }
- const a = attachment as any;
- if (a.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;
- }
- }
-
- a.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"),
- });
- a.downloading = false;
- return;
- }
-
- try {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const decBuf = await this.cipherService.getDecryptedAttachmentBuffer(
- this.cipher.id as CipherId,
- attachment,
- response,
- activeUserId,
- );
-
- this.fileDownloadService.download({
- fileName: attachment.fileName,
- blobData: decBuf,
- });
- } catch {
- this.toastService.showToast({
- variant: "error",
- title: null,
- message: this.i18nService.t("errorOccurred"),
- });
- }
-
- a.downloading = false;
- }
-
- protected deleteCipher(userId: UserId) {
- return this.cipher.isDeleted
- ? this.cipherService.deleteWithServer(this.cipher.id, userId)
- : this.cipherService.softDeleteWithServer(this.cipher.id, userId);
- }
-
- protected restoreCipher(userId: UserId) {
- return this.cipherService.restoreWithServer(this.cipher.id, userId);
- }
-
- protected async promptPassword() {
- if (this.cipher.reprompt === CipherRepromptType.None || this.passwordReprompted) {
- return true;
- }
-
- return (this.passwordReprompted = await this.passwordRepromptService.showPasswordPrompt());
- }
-
- private cleanUp() {
- this.cipher = null;
- this.folder = null;
- this.showPassword = false;
- this.showCardNumber = false;
- this.showCardCode = false;
- this.passwordReprompted = false;
- }
-
- /**
- * When a cipher is viewed, construct all details for the view that are not directly
- * available from the cipher object itself.
- */
- private async constructCipherDetails(userId: UserId) {
- this.canAccessPremium = await firstValueFrom(
- this.billingAccountProfileStateService.hasPremiumFromAnySource$(userId),
- );
- this.showPremiumRequiredTotp =
- this.cipher.login.totp && !this.canAccessPremium && !this.cipher.organizationUseTotp;
- this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher);
- this.canRestoreCipher$ = this.cipherAuthorizationService.canRestoreCipher$(this.cipher);
-
- if (this.cipher.folderId) {
- this.folder = await (
- await firstValueFrom(this.folderService.folderViews$(userId))
- ).find((f) => f.id == this.cipher.folderId);
- }
-
- const canGenerateTotp =
- this.cipher.type === CipherType.Login &&
- this.cipher.login.totp &&
- (this.cipher.organizationUseTotp || this.canAccessPremium);
-
- this.totpInfo$ = canGenerateTotp
- ? this.totpService.getCode$(this.cipher.login.totp).pipe(
- map((response) => {
- const epoch = Math.round(new Date().getTime() / 1000.0);
- const mod = epoch % response.period;
-
- // Format code
- const totpCodeFormatted =
- response.code.length > 4
- ? `${response.code.slice(0, Math.floor(response.code.length / 2))} ${response.code.slice(Math.floor(response.code.length / 2))}`
- : response.code;
-
- return {
- totpCode: response.code,
- totpCodeFormatted,
- totpDash: +(Math.round(((78.6 / response.period) * mod + "e+2") as any) + "e-2"),
- totpSec: response.period - mod,
- totpLow: response.period - mod <= 7,
- } as TotpInfo;
- }),
- )
- : undefined;
-
- if (this.previousCipherId !== this.cipherId) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientViewed, this.cipherId);
- }
- this.previousCipherId = this.cipherId;
- }
-}
diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts
index 71de8fb5433..fcc81070331 100644
--- a/libs/common/src/enums/feature-flag.enum.ts
+++ b/libs/common/src/enums/feature-flag.enum.ts
@@ -53,7 +53,6 @@ export enum FeatureFlag {
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
PM19941MigrateCipherDomainToSdk = "pm-19941-migrate-cipher-domain-to-sdk",
CipherKeyEncryption = "cipher-key-encryption",
- PM18520_UpdateDesktopCipherForm = "pm-18520-desktop-cipher-forms",
EndUserNotifications = "pm-10609-end-user-notifications",
RemoveCardItemTypePolicy = "pm-16442-remove-card-item-type-policy",
PM19315EndUserActivationMvp = "pm-19315-end-user-activation-mvp",
@@ -97,7 +96,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.PM8851_BrowserOnboardingNudge]: FALSE,
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,
[FeatureFlag.CipherKeyEncryption]: FALSE,
- [FeatureFlag.PM18520_UpdateDesktopCipherForm]: FALSE,
[FeatureFlag.EndUserNotifications]: FALSE,
[FeatureFlag.PM19941MigrateCipherDomainToSdk]: FALSE,
[FeatureFlag.RemoveCardItemTypePolicy]: FALSE,