1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-17 09:59:41 +00:00

Merge branch 'main' into ps/extension-refresh

This commit is contained in:
Victoria League
2024-09-16 15:32:14 -04:00
committed by GitHub
64 changed files with 2075 additions and 658 deletions

View File

@@ -1,8 +1,10 @@
<bit-simple-dialog>
<i bitDialogIcon class="bwi bwi-info-circle tw-text-3xl" aria-hidden="true"></i>
<span bitDialogTitle>{{ "yourAccountsFingerprint" | i18n }}:</span>
<span bitDialogTitle
><strong>{{ "yourAccountsFingerprint" | i18n }}:</strong></span
>
<span bitDialogContent>
<strong>{{ data.fingerprint.join("-") }}</strong>
{{ data.fingerprint.join("-") }}
</span>
<ng-container bitDialogFooter>
<a

View File

@@ -1,29 +1,47 @@
<div [formGroup]="form">
<bit-form-field>
<bit-label>{{ "vaultTimeout" | i18n }}</bit-label>
<div [formGroup]="form" class="tw-mb-4">
<bit-form-field [disableMargin]="!showCustom">
<bit-label>{{ "vaultTimeout1" | i18n }}</bit-label>
<bit-select formControlName="vaultTimeout">
<bit-option
*ngFor="let o of vaultTimeoutOptions"
*ngFor="let o of filteredVaultTimeoutOptions"
[value]="o.value"
[label]="o.name"
></bit-option>
</bit-select>
<bit-hint class="tw-text-sm">{{
((canLockVault$ | async) ? "vaultTimeoutDesc" : "vaultTimeoutLogoutDesc") | i18n
}}</bit-hint>
</bit-form-field>
<div class="tw-grid tw-grid-cols-12 tw-gap-4" *ngIf="showCustom" formGroupName="custom">
<bit-form-field class="tw-col-span-6">
<bit-label>{{ "customVaultTimeout" | i18n }}</bit-label>
<input bitInput type="number" min="0" formControlName="hours" />
<bit-hint>{{ "hours" | i18n }}</bit-hint>
<bit-form-field class="tw-col-span-6" disableMargin>
<input
bitInput
type="number"
min="0"
formControlName="hours"
aria-labelledby="maximum-error"
/>
<bit-label>{{ "hours" | i18n }}</bit-label>
</bit-form-field>
<bit-form-field class="tw-col-span-6 tw-self-end">
<input bitInput type="number" min="0" name="minutes" formControlName="minutes" />
<bit-hint>{{ "minutes" | i18n }}</bit-hint>
<bit-form-field class="tw-col-span-6 tw-self-end" disableMargin>
<input
bitInput
type="number"
min="0"
name="minutes"
formControlName="minutes"
aria-labelledby="maximum-error"
/>
<bit-label>{{ "minutes" | i18n }}</bit-label>
</bit-form-field>
</div>
<small *ngIf="!exceedsMinimumTimout" class="tw-text-danger">
<bit-hint *ngIf="vaultTimeoutPolicy != null && !exceedsMaximumTimeout">
{{ "vaultTimeoutPolicyInEffect1" | i18n: vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes }}
</bit-hint>
<small *ngIf="!exceedsMinimumTimeout" class="tw-text-danger">
<i class="bwi bwi-error" aria-hidden="true"></i> {{ "vaultCustomTimeoutMinimum" | i18n }}
</small>
<small class="tw-text-danger" *ngIf="exceedsMaximumTimeout" id="maximum-error">
<i class="bwi bwi-error" aria-hidden="true"></i>
{{
"vaultTimeoutPolicyMaximumError" | i18n: vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes
}}
</small>
</div>

View File

@@ -55,16 +55,41 @@ type VaultTimeoutFormValue = VaultTimeoutForm["value"];
export class VaultTimeoutInputComponent
implements ControlValueAccessor, Validator, OnInit, OnDestroy, OnChanges
{
protected readonly VaultTimeoutAction = VaultTimeoutAction;
get showCustom() {
return this.form.get("vaultTimeout").value === VaultTimeoutInputComponent.CUSTOM_VALUE;
}
get exceedsMinimumTimout(): boolean {
get exceedsMinimumTimeout(): boolean {
return (
!this.showCustom || this.customTimeInMinutes() > VaultTimeoutInputComponent.MIN_CUSTOM_MINUTES
);
}
get exceedsMaximumTimeout(): boolean {
return (
this.showCustom &&
this.customTimeInMinutes() >
this.vaultTimeoutPolicyMinutes + 60 * this.vaultTimeoutPolicyHours
);
}
get filteredVaultTimeoutOptions(): VaultTimeoutOption[] {
// by policy max value
if (this.vaultTimeoutPolicy == null || this.vaultTimeoutPolicy.data == null) {
return this.vaultTimeoutOptions;
}
return this.vaultTimeoutOptions.filter((option) => {
if (typeof option.value === "number") {
return option.value <= this.vaultTimeoutPolicy.data.minutes;
}
return false;
});
}
static CUSTOM_VALUE = -100;
static MIN_CUSTOM_MINUTES = 0;
@@ -77,6 +102,7 @@ export class VaultTimeoutInputComponent
});
@Input() vaultTimeoutOptions: VaultTimeoutOption[];
vaultTimeoutPolicy: Policy;
vaultTimeoutPolicyHours: number;
vaultTimeoutPolicyMinutes: number;
@@ -207,7 +233,7 @@ export class VaultTimeoutInputComponent
return { policyError: true };
}
if (!this.exceedsMinimumTimout) {
if (!this.exceedsMinimumTimeout) {
return { minTimeoutError: true };
}

View File

@@ -107,3 +107,5 @@ export const ExtensionCommand = {
export type ExtensionCommandType = (typeof ExtensionCommand)[keyof typeof ExtensionCommand];
export const CLEAR_NOTIFICATION_LOGIN_DATA_DURATION = 60 * 1000; // 1 minute
export const MAX_DEEP_QUERY_RECURSION_DEPTH = 4;

View File

@@ -19,6 +19,7 @@
bitSuffix
bitPasswordInputToggle
data-testid="visibility-for-card-number"
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardNumberVisible)"
></button>
</bit-form-field>
@@ -60,6 +61,7 @@
bitSuffix
bitPasswordInputToggle
data-testid="visibility-for-card-code"
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardCodeVisible)"
></button>
</bit-form-field>
</bit-card>

View File

@@ -4,6 +4,7 @@ import { ReactiveFormsModule } from "@angular/forms";
import { By } from "@angular/platform-browser";
import { mock, MockProxy } from "jest-mock-extended";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -27,6 +28,7 @@ describe("CardDetailsSectionComponent", () => {
await TestBed.configureTestingModule({
imports: [CardDetailsSectionComponent, CommonModule, ReactiveFormsModule],
providers: [
{ provide: EventCollectionService, useValue: mock<EventCollectionService>() },
{ provide: CipherFormContainer, useValue: cipherFormProvider },
{ provide: I18nService, useValue: { t: (key: string) => key } },
],

View File

@@ -4,6 +4,8 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
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 { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -91,10 +93,13 @@ export class CardDetailsSectionComponent implements OnInit {
{ name: "12 - " + this.i18nService.t("december"), value: "12" },
];
EventType = EventType;
constructor(
private cipherFormContainer: CipherFormContainer,
private formBuilder: FormBuilder,
private i18nService: I18nService,
private eventCollectionService: EventCollectionService,
) {
this.cipherFormContainer.registerChildForm("cardDetails", this.cardDetailsForm);
@@ -149,6 +154,21 @@ export class CardDetailsSectionComponent implements OnInit {
return this.i18nService.t("cardDetails");
}
async logCardEvent(hiddenFieldVisible: boolean, event: EventType) {
const { mode, originalCipher } = this.cipherFormContainer.config;
const isEdit = ["edit", "partial-edit"].includes(mode);
if (hiddenFieldVisible && isEdit) {
await this.eventCollectionService.collect(
event,
originalCipher.id,
false,
originalCipher.organizationId,
);
}
}
/** Set form initial form values from the current cipher */
private setInitialValues() {
const { cardholderName, number, brand, expMonth, expYear, code } = this.originalCipherView.card;

View File

@@ -46,6 +46,7 @@
bitPasswordInputToggle
data-testid="visibility-for-custom-hidden-field"
[disabled]="!canViewPasswords(i)"
(toggledChange)="logHiddenEvent($event)"
></button>
</bit-form-field>

View File

@@ -4,7 +4,9 @@ import { CdkDragDrop } from "@angular/cdk/drag-drop";
import { DebugElement } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock } from "jest-mock-extended";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
CardLinkedId,
@@ -50,6 +52,7 @@ describe("CustomFieldsComponent", () => {
await TestBed.configureTestingModule({
imports: [CustomFieldsComponent],
providers: [
{ provide: EventCollectionService, useValue: mock<EventCollectionService>() },
{
provide: I18nService,
useValue: { t: (...keys: string[]) => keys.filter(Boolean).join(" ") },

View File

@@ -19,6 +19,8 @@ import { FormArray, FormBuilder, FormsModule, ReactiveFormsModule } from "@angul
import { Subject, zip } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
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 { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
@@ -118,6 +120,7 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit {
private formBuilder: FormBuilder,
private i18nService: I18nService,
private liveAnnouncer: LiveAnnouncer,
private eventCollectionService: EventCollectionService,
) {
this.destroyed$ = inject(DestroyRef);
this.cipherFormContainer.registerChildForm("customFields", this.customFieldsForm);
@@ -301,6 +304,21 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit {
}
}
async logHiddenEvent(hiddenFieldVisible: boolean) {
const { mode, originalCipher } = this.cipherFormContainer.config;
const isEdit = ["edit", "partial-edit"].includes(mode);
if (hiddenFieldVisible && isEdit) {
await this.eventCollectionService.collect(
EventType.Cipher_ClientToggledHiddenFieldVisible,
originalCipher.id,
false,
originalCipher.organizationId,
);
}
}
/**
* Returns the linked field options for the current cipher type
*

View File

@@ -57,6 +57,7 @@
*ngIf="viewHiddenFields"
data-testid="toggle-password-visibility"
bitPasswordInputToggle
(toggledChange)="logVisibleEvent($event, EventType.Cipher_ClientToggledPasswordVisible)"
></button>
<button
type="button"
@@ -113,6 +114,7 @@
*ngIf="viewHiddenFields"
data-testid="toggle-totp-visibility"
bitPasswordInputToggle
(toggledChange)="logVisibleEvent($event, EventType.Cipher_ClientToggledTOTPSeedVisible)"
></button>
<button
type="button"

View File

@@ -1,14 +1,19 @@
import { DatePipe } from "@angular/common";
import { Component } from "@angular/core";
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock, MockProxy } from "jest-mock-extended";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
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 { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
import { ToastService } from "@bitwarden/components";
import { BitPasswordInputToggleDirective } from "@bitwarden/components/src/form-field/password-input-toggle.directive";
import { CipherFormGenerationService } from "../../abstractions/cipher-form-generation.service";
import { TotpCaptureService } from "../../abstractions/totp-capture.service";
@@ -34,6 +39,7 @@ describe("LoginDetailsSectionComponent", () => {
let toastService: MockProxy<ToastService>;
let totpCaptureService: MockProxy<TotpCaptureService>;
let i18nService: MockProxy<I18nService>;
const collect = jest.fn().mockResolvedValue(null);
beforeEach(async () => {
cipherFormContainer = mock<CipherFormContainer>();
@@ -43,6 +49,7 @@ describe("LoginDetailsSectionComponent", () => {
toastService = mock<ToastService>();
totpCaptureService = mock<TotpCaptureService>();
i18nService = mock<I18nService>();
collect.mockClear();
await TestBed.configureTestingModule({
imports: [LoginDetailsSectionComponent],
@@ -53,6 +60,7 @@ describe("LoginDetailsSectionComponent", () => {
{ provide: ToastService, useValue: toastService },
{ provide: TotpCaptureService, useValue: totpCaptureService },
{ provide: I18nService, useValue: i18nService },
{ provide: EventCollectionService, useValue: { collect } },
],
})
.overrideComponent(LoginDetailsSectionComponent, {
@@ -255,6 +263,32 @@ describe("LoginDetailsSectionComponent", () => {
expect(getTogglePasswordVisibilityBtn()).toBeNull();
});
it("logs password viewed event when toggledChange is true", async () => {
cipherFormContainer.config.mode = "edit";
cipherFormContainer.config.originalCipher = {
id: "111-222-333",
organizationId: "333-444-555",
} as Cipher;
jest.spyOn(component, "viewHiddenFields", "get").mockReturnValue(true);
fixture.detectChanges();
const passwordToggle = fixture.debugElement.query(
By.directive(BitPasswordInputToggleDirective),
);
await passwordToggle.triggerEventHandler("toggledChange", true);
expect(collect).toHaveBeenCalledWith(
EventType.Cipher_ClientToggledPasswordVisible,
"111-222-333",
false,
"333-444-555",
);
await passwordToggle.triggerEventHandler("toggledChange", false);
expect(collect).toHaveBeenCalledTimes(1);
});
describe("password generation", () => {
it("should show generate password button when editable", () => {
expect(getGeneratePasswordBtn()).not.toBeNull();

View File

@@ -6,6 +6,8 @@ import { map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
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 { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
@@ -48,6 +50,7 @@ import { AutofillOptionsComponent } from "../autofill-options/autofill-options.c
],
})
export class LoginDetailsSectionComponent implements OnInit {
EventType = EventType;
loginDetailsForm = this.formBuilder.group({
username: [""],
password: [""],
@@ -106,6 +109,7 @@ export class LoginDetailsSectionComponent implements OnInit {
private generationService: CipherFormGenerationService,
private auditService: AuditService,
private toastService: ToastService,
private eventCollectionService: EventCollectionService,
@Optional() private totpCaptureService?: TotpCaptureService,
) {
this.cipherFormContainer.registerChildForm("loginDetails", this.loginDetailsForm);
@@ -163,6 +167,24 @@ export class LoginDetailsSectionComponent implements OnInit {
});
}
/** Logs the givin event when in edit mode */
logVisibleEvent = async (passwordVisible: boolean, event: EventType) => {
const { mode, originalCipher } = this.cipherFormContainer.config;
const isEdit = ["edit", "partial-edit"].includes(mode);
if (!passwordVisible || !isEdit || !originalCipher) {
return;
}
await this.eventCollectionService.collect(
event,
originalCipher.id,
false,
originalCipher.organizationId,
);
};
captureTotp = async () => {
if (!this.canCaptureTotp) {
return;

View File

@@ -30,6 +30,7 @@
bitIconButton
bitPasswordInputToggle
data-testid="toggle-number"
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardNumberVisible)"
></button>
<button
bitIconButton="bwi-clone"
@@ -69,6 +70,7 @@
bitIconButton
bitPasswordInputToggle
data-testid="toggle-code"
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardCodeVisible)"
></button>
<button
bitIconButton="bwi-clone"
@@ -79,6 +81,7 @@
[valueLabel]="'securityCode' | i18n"
[appA11yTitle]="'copyValue' | i18n"
data-testid="copy-code"
(click)="logCardEvent(true, EventType.Cipher_ClientCopiedCardCode)"
></button>
</bit-form-field>
</read-only-cipher-card>

View File

@@ -2,8 +2,10 @@ import { CommonModule } from "@angular/common";
import { Component, Input } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
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 { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
CardComponent,
SectionComponent,
@@ -32,9 +34,17 @@ import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-
],
})
export class CardDetailsComponent {
@Input() card: CardView;
@Input() cipher: CipherView;
EventType = EventType;
constructor(private i18nService: I18nService) {}
constructor(
private i18nService: I18nService,
private eventCollectionService: EventCollectionService,
) {}
get card() {
return this.cipher.card;
}
get setSectionTitle() {
if (this.card.brand && this.card.brand !== "Other") {
@@ -42,4 +52,15 @@ export class CardDetailsComponent {
}
return this.i18nService.t("cardDetails");
}
async logCardEvent(conditional: boolean, event: EventType) {
if (conditional) {
await this.eventCollectionService.collect(
event,
this.cipher.id,
false,
this.cipher.organizationId,
);
}
}
}

View File

@@ -29,7 +29,7 @@
</app-autofill-options-view>
<!-- CARD DETAILS -->
<app-card-details-view *ngIf="hasCard" [card]="cipher.card"></app-card-details-view>
<app-card-details-view *ngIf="hasCard" [cipher]="cipher"></app-card-details-view>
<!-- IDENTITY SECTIONS -->
<app-view-identity-sections *ngIf="cipher.identity" [cipher]="cipher">
@@ -42,8 +42,7 @@
<!-- CUSTOM FIELDS -->
<ng-container *ngIf="cipher.fields">
<app-custom-fields-v2 [fields]="cipher.fields" [cipherType]="cipher.type">
</app-custom-fields-v2>
<app-custom-fields-v2 [cipher]="cipher"> </app-custom-fields-v2>
</ng-container>
<!-- ATTACHMENTS SECTION -->

View File

@@ -5,7 +5,7 @@
<bit-card>
<div
class="tw-border-secondary-300 [&_bit-form-field:last-of-type]:tw-mb-0"
*ngFor="let field of fields; let last = last"
*ngFor="let field of cipher.fields; let last = last"
[ngClass]="{ 'tw-mb-4': !last }"
>
<bit-form-field *ngIf="field.type === fieldType.Text" [disableReadOnlyBorder]="last">
@@ -24,7 +24,13 @@
<bit-form-field *ngIf="field.type === fieldType.Hidden" [disableReadOnlyBorder]="last">
<bit-label>{{ field.name }}</bit-label>
<input readonly bitInput type="password" [value]="field.value" aria-readonly="true" />
<button bitSuffix type="button" bitIconButton bitPasswordInputToggle></button>
<button
bitSuffix
type="button"
bitIconButton
bitPasswordInputToggle
(toggledChange)="logHiddenEvent($event)"
></button>
<button
bitIconButton="bwi-clone"
bitSuffix
@@ -33,6 +39,7 @@
showToast
[valueLabel]="field.name"
[appA11yTitle]="'copyValue' | i18n"
(click)="logCopyEvent()"
></button>
</bit-form-field>
<bit-form-control *ngIf="field.type === fieldType.Boolean">

View File

@@ -2,10 +2,12 @@ import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
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 { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
import {
@@ -37,12 +39,14 @@ import {
],
})
export class CustomFieldV2Component implements OnInit {
@Input() fields: FieldView[];
@Input() cipherType: CipherType;
@Input() cipher: CipherView;
fieldType = FieldType;
fieldOptions: any;
constructor(private i18nService: I18nService) {}
constructor(
private i18nService: I18nService,
private eventCollectionService: EventCollectionService,
) {}
ngOnInit(): void {
this.fieldOptions = this.getLinkedFieldsOptionsForCipher();
@@ -53,8 +57,28 @@ export class CustomFieldV2Component implements OnInit {
return this.i18nService.t(linkedType.i18nKey);
}
async logHiddenEvent(hiddenFieldVisible: boolean) {
if (hiddenFieldVisible) {
await this.eventCollectionService.collect(
EventType.Cipher_ClientToggledHiddenFieldVisible,
this.cipher.id,
false,
this.cipher.organizationId,
);
}
}
async logCopyEvent() {
await this.eventCollectionService.collect(
EventType.Cipher_ClientCopiedHiddenField,
this.cipher.id,
false,
this.cipher.organizationId,
);
}
private getLinkedFieldsOptionsForCipher() {
switch (this.cipherType) {
switch (this.cipher.type) {
case CipherType.Login:
return LoginView.prototype.linkedFieldOptions;
case CipherType.Card:

View File

@@ -66,6 +66,7 @@
showToast
[appA11yTitle]="'copyValue' | i18n"
data-testid="copy-password"
(click)="logCopyEvent()"
></button>
</bit-form-field>
<div

View File

@@ -4,7 +4,9 @@ import { Router } from "@angular/router";
import { Observable, shareReplay } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { EventType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
@@ -61,6 +63,7 @@ export class LoginCredentialsViewComponent {
private billingAccountProfileStateService: BillingAccountProfileStateService,
private router: Router,
private i18nService: I18nService,
private eventCollectionService: EventCollectionService,
) {}
get fido2CredentialCreationDateValue(): string {
@@ -76,8 +79,17 @@ export class LoginCredentialsViewComponent {
await this.router.navigate(["/premium"]);
}
pwToggleValue(evt: boolean) {
this.passwordRevealed = evt;
async pwToggleValue(passwordVisible: boolean) {
this.passwordRevealed = passwordVisible;
if (passwordVisible) {
await this.eventCollectionService.collect(
EventType.Cipher_ClientToggledPasswordVisible,
this.cipher.id,
false,
this.cipher.organizationId,
);
}
}
togglePasswordCount() {
@@ -87,4 +99,13 @@ export class LoginCredentialsViewComponent {
setTotpCopyCode(e: TotpCodeValues) {
this.totpCodeCopyObj = e;
}
async logCopyEvent() {
await this.eventCollectionService.collect(
EventType.Cipher_ClientCopiedPassword,
this.cipher.id,
false,
this.cipher.organizationId,
);
}
}

View File

@@ -158,6 +158,8 @@ describe("CopyCipherFieldService", () => {
expect(eventCollectionService.collect).toHaveBeenCalledWith(
EventType.Cipher_ClientCopiedPassword,
cipher.id,
false,
cipher.organizationId,
);
});
});

View File

@@ -125,7 +125,12 @@ export class CopyCipherFieldService {
});
if (action.event !== undefined) {
await this.eventCollectionService.collect(action.event, cipher.id);
await this.eventCollectionService.collect(
action.event,
cipher.id,
false,
cipher.organizationId,
);
}
}