mirror of
https://github.com/bitwarden/browser
synced 2026-01-05 10:03:21 +00:00
* add `CipherViewLike` and utilities to handle `CipherView` and `CipherViewLike` * migrate libs needed for web vault to support `CipherViewLike` * migrate web vault components to support * add for CipherView. will have to be later * fetch full CipherView for copying a password * have only the cipher service utilize SDK migration flag - This keeps feature flag logic away from the component - Also cuts down on what is needed for other platforms * strongly type CipherView for AC vault - Probably temporary before migration of the AC vault to `CipherListView` SDK * fix build icon tests by being more gracious with the uri structure * migrate desktop components to CipherListViews$ * consume card from sdk * add browser implementation for `CipherListView` * update copy message for single copiable items * refactor `getCipherViewLikeLogin` to `getLogin` * refactor `getCipherViewLikeCard` to `getCard` * add `hasFido2Credentials` helper * add decryption failure to cipher like utils * add todo with ticket * fix decryption failure typing * fix copy card messages * fix addition of organizations and collections for `PopupCipherViewLike` - accessors were being lost * refactor to getters to fix re-rendering bug * fix decryption failure helper * fix sorting functions for `CipherViewLike` * formatting * add `CipherViewLikeUtils` tests * refactor "copiable" to "copyable" to match SDK * use `hasOldAttachments` from cipherlistview * fix typing * update SDK version * add feature flag for cipher list view work * use `CipherViewLikeUtils` for copyable values rather than referring to the cipher directly * update restricted item type to support CipherViewLike * add cipher support to `CipherViewLikeUtils` * update `isCipherListView` check * refactor CipherLike to a separate type * refactor `getFullCipherView` into the cipher service * add optional chaining for `uriChecksum` * set empty array for decrypted CipherListView * migrate nudge service to use `cipherListViews` * update web vault to not depend on `cipherViews$` * update popup list filters to use `CipherListView` * fix storybook * fix tests * accept undefined as a MY VAULT filter value for cipher list views * use `LoginUriView` for uri logic (#15530) * filter out null ciphers from the `_allDecryptedCiphers$` (#15539) * use `launchUri` to avoid any unexpected behavior in URIs - this appends `http://` when missing
176 lines
5.3 KiB
TypeScript
176 lines
5.3 KiB
TypeScript
import { Injectable } from "@angular/core";
|
|
import { firstValueFrom } from "rxjs";
|
|
|
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
|
import { EventType } from "@bitwarden/common/enums";
|
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
|
import { CipherRepromptType } from "@bitwarden/common/vault/enums";
|
|
import {
|
|
CipherViewLike,
|
|
CipherViewLikeUtils,
|
|
} from "@bitwarden/common/vault/utils/cipher-view-like-utils";
|
|
import { ToastService } from "@bitwarden/components";
|
|
import { PasswordRepromptService } from "@bitwarden/vault";
|
|
|
|
/**
|
|
* The types of fields that can be copied from a cipher.
|
|
*/
|
|
export type CopyAction =
|
|
| "username"
|
|
| "password"
|
|
| "totp"
|
|
| "cardNumber"
|
|
| "securityCode"
|
|
| "email"
|
|
| "phone"
|
|
| "address"
|
|
| "secureNote"
|
|
| "hiddenField"
|
|
| "privateKey"
|
|
| "publicKey"
|
|
| "keyFingerprint";
|
|
|
|
type CopyActionInfo = {
|
|
/**
|
|
* The i18n key for the type of field being copied. Will be used to display a toast message.
|
|
*/
|
|
typeI18nKey: string;
|
|
|
|
/**
|
|
* Whether the field is protected and requires password re-prompting before being copied.
|
|
*/
|
|
protected: boolean;
|
|
|
|
/**
|
|
* Optional event to collect when the field is copied.
|
|
*/
|
|
event?: EventType;
|
|
};
|
|
|
|
const CopyActions: Record<CopyAction, CopyActionInfo> = {
|
|
username: { typeI18nKey: "username", protected: false },
|
|
password: {
|
|
typeI18nKey: "password",
|
|
protected: true,
|
|
event: EventType.Cipher_ClientCopiedPassword,
|
|
},
|
|
totp: { typeI18nKey: "verificationCodeTotp", protected: true },
|
|
cardNumber: { typeI18nKey: "number", protected: true },
|
|
securityCode: {
|
|
typeI18nKey: "securityCode",
|
|
protected: true,
|
|
event: EventType.Cipher_ClientCopiedCardCode,
|
|
},
|
|
email: { typeI18nKey: "email", protected: true },
|
|
phone: { typeI18nKey: "phone", protected: true },
|
|
address: { typeI18nKey: "address", protected: true },
|
|
secureNote: { typeI18nKey: "note", protected: true },
|
|
privateKey: { typeI18nKey: "sshPrivateKey", protected: true },
|
|
publicKey: { typeI18nKey: "sshPublicKey", protected: true },
|
|
keyFingerprint: { typeI18nKey: "sshFingerprint", protected: true },
|
|
hiddenField: {
|
|
typeI18nKey: "value",
|
|
protected: true,
|
|
event: EventType.Cipher_ClientCopiedHiddenField,
|
|
},
|
|
};
|
|
|
|
@Injectable({
|
|
providedIn: "root",
|
|
})
|
|
export class CopyCipherFieldService {
|
|
constructor(
|
|
private platformUtilsService: PlatformUtilsService,
|
|
private toastService: ToastService,
|
|
private eventCollectionService: EventCollectionService,
|
|
private passwordRepromptService: PasswordRepromptService,
|
|
private totpService: TotpService,
|
|
private i18nService: I18nService,
|
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
|
private accountService: AccountService,
|
|
) {}
|
|
|
|
/**
|
|
* Copy a field value from a cipher to the clipboard.
|
|
* @param valueToCopy The value to copy.
|
|
* @param actionType The type of field being copied.
|
|
* @param cipher The cipher containing the field to copy.
|
|
* @param skipReprompt Whether to skip password re-prompting.
|
|
*
|
|
* @returns Whether the field was copied successfully.
|
|
*/
|
|
async copy(
|
|
valueToCopy: string,
|
|
actionType: CopyAction,
|
|
cipher: CipherViewLike,
|
|
skipReprompt: boolean = false,
|
|
): Promise<boolean> {
|
|
const action = CopyActions[actionType];
|
|
if (
|
|
!skipReprompt &&
|
|
cipher.reprompt !== CipherRepromptType.None &&
|
|
action.protected &&
|
|
!(await this.passwordRepromptService.showPasswordPrompt())
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if (valueToCopy == null) {
|
|
return false;
|
|
}
|
|
|
|
if (actionType === "totp") {
|
|
if (!(await this.totpAllowed(cipher))) {
|
|
return false;
|
|
}
|
|
const totpResponse = await firstValueFrom(this.totpService.getCode$(valueToCopy));
|
|
if (!totpResponse?.code) {
|
|
return false;
|
|
}
|
|
valueToCopy = totpResponse.code;
|
|
}
|
|
|
|
this.platformUtilsService.copyToClipboard(valueToCopy);
|
|
this.toastService.showToast({
|
|
variant: "success",
|
|
message: this.i18nService.t("valueCopied", this.i18nService.t(action.typeI18nKey)),
|
|
title: "",
|
|
});
|
|
|
|
if (action.event !== undefined) {
|
|
await this.eventCollectionService.collect(
|
|
action.event,
|
|
cipher.id,
|
|
false,
|
|
cipher.organizationId,
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Determines if TOTP generation is allowed for a cipher and user.
|
|
*/
|
|
async totpAllowed(cipher: CipherViewLike): Promise<boolean> {
|
|
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
|
if (!activeAccount?.id) {
|
|
return false;
|
|
}
|
|
|
|
const login = CipherViewLikeUtils.getLogin(cipher);
|
|
|
|
return (
|
|
!!login?.totp &&
|
|
(cipher.organizationUseTotp ||
|
|
(await firstValueFrom(
|
|
this.billingAccountProfileStateService.hasPremiumFromAnySource$(activeAccount.id),
|
|
)))
|
|
);
|
|
}
|
|
}
|