mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
[PM-27287] Items in My Items should show in Inactive 2FA report (#17434)
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Directive, OnDestroy } from "@angular/core";
|
import { Directive, OnDestroy } from "@angular/core";
|
||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
@@ -36,7 +34,7 @@ import {
|
|||||||
import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service";
|
import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service";
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class CipherReportComponent implements OnDestroy {
|
export abstract class CipherReportComponent implements OnDestroy {
|
||||||
isAdminConsoleActive = false;
|
isAdminConsoleActive = false;
|
||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
@@ -44,16 +42,16 @@ export class CipherReportComponent implements OnDestroy {
|
|||||||
ciphers: CipherView[] = [];
|
ciphers: CipherView[] = [];
|
||||||
allCiphers: CipherView[] = [];
|
allCiphers: CipherView[] = [];
|
||||||
dataSource = new TableDataSource<CipherView>();
|
dataSource = new TableDataSource<CipherView>();
|
||||||
organization: Organization;
|
organization: Organization | undefined = undefined;
|
||||||
organizations: Organization[];
|
organizations: Organization[] = [];
|
||||||
organizations$: Observable<Organization[]>;
|
organizations$: Observable<Organization[]>;
|
||||||
|
|
||||||
filterStatus: any = [0];
|
filterStatus: any = [0];
|
||||||
showFilterToggle: boolean = false;
|
showFilterToggle: boolean = false;
|
||||||
vaultMsg: string = "vault";
|
vaultMsg: string = "vault";
|
||||||
currentFilterStatus: number | string;
|
currentFilterStatus: number | string = 0;
|
||||||
protected filterOrgStatus$ = new BehaviorSubject<number | string>(0);
|
protected filterOrgStatus$ = new BehaviorSubject<number | string>(0);
|
||||||
private destroyed$: Subject<void> = new Subject();
|
protected destroyed$: Subject<void> = new Subject();
|
||||||
private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined;
|
private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -107,7 +105,7 @@ export class CipherReportComponent implements OnDestroy {
|
|||||||
if (filterId === 0) {
|
if (filterId === 0) {
|
||||||
cipherCount = this.allCiphers.length;
|
cipherCount = this.allCiphers.length;
|
||||||
} else if (filterId === 1) {
|
} else if (filterId === 1) {
|
||||||
cipherCount = this.allCiphers.filter((c) => c.organizationId === null).length;
|
cipherCount = this.allCiphers.filter((c) => !c.organizationId).length;
|
||||||
} else {
|
} else {
|
||||||
this.organizations.filter((org: Organization) => {
|
this.organizations.filter((org: Organization) => {
|
||||||
if (org.id === filterId) {
|
if (org.id === filterId) {
|
||||||
@@ -121,9 +119,9 @@ export class CipherReportComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async filterOrgToggle(status: any) {
|
async filterOrgToggle(status: any) {
|
||||||
let filter = null;
|
let filter = (c: CipherView) => true;
|
||||||
if (typeof status === "number" && status === 1) {
|
if (typeof status === "number" && status === 1) {
|
||||||
filter = (c: CipherView) => c.organizationId == null;
|
filter = (c: CipherView) => !c.organizationId;
|
||||||
} else if (typeof status === "string") {
|
} else if (typeof status === "string") {
|
||||||
const orgId = status as OrganizationId;
|
const orgId = status as OrganizationId;
|
||||||
filter = (c: CipherView) => c.organizationId === orgId;
|
filter = (c: CipherView) => c.organizationId === orgId;
|
||||||
@@ -185,7 +183,7 @@ export class CipherReportComponent implements OnDestroy {
|
|||||||
cipher: CipherView,
|
cipher: CipherView,
|
||||||
activeCollectionId?: CollectionId,
|
activeCollectionId?: CollectionId,
|
||||||
) {
|
) {
|
||||||
const disableForm = cipher ? !cipher.edit && !this.organization.canEditAllCiphers : false;
|
const disableForm = cipher ? !cipher.edit && !this.organization?.canEditAllCiphers : false;
|
||||||
|
|
||||||
this.vaultItemDialogRef = VaultItemDialogComponent.open(this.dialogService, {
|
this.vaultItemDialogRef = VaultItemDialogComponent.open(this.dialogService, {
|
||||||
mode,
|
mode,
|
||||||
@@ -230,10 +228,11 @@ export class CipherReportComponent implements OnDestroy {
|
|||||||
let updatedCipher = await this.cipherService.get(cipher.id, activeUserId);
|
let updatedCipher = await this.cipherService.get(cipher.id, activeUserId);
|
||||||
|
|
||||||
if (this.isAdminConsoleActive) {
|
if (this.isAdminConsoleActive) {
|
||||||
updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher(
|
updatedCipher =
|
||||||
|
(await this.adminConsoleCipherFormConfigService.getCipher(
|
||||||
cipher.id as CipherId,
|
cipher.id as CipherId,
|
||||||
this.organization,
|
this.organization!,
|
||||||
);
|
)) ?? updatedCipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert cipher to cipher view model
|
// convert cipher to cipher view model
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ describe("ExposedPasswordsReportComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
fixture = TestBed.createComponent(ExposedPasswordsReportComponent);
|
fixture = TestBed.createComponent(ExposedPasswordsReportComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
|
||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { MockProxy, mock } from "jest-mock-extended";
|
import { MockProxy, mock } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
@@ -29,14 +30,13 @@ describe("InactiveTwoFactorReportComponent", () => {
|
|||||||
const userId = Utils.newGuid() as UserId;
|
const userId = Utils.newGuid() as UserId;
|
||||||
const accountService: FakeAccountService = mockAccountServiceWith(userId);
|
const accountService: FakeAccountService = mockAccountServiceWith(userId);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
let cipherFormConfigServiceMock: MockProxy<CipherFormConfigService>;
|
let cipherFormConfigServiceMock: MockProxy<CipherFormConfigService>;
|
||||||
organizationService = mock<OrganizationService>();
|
organizationService = mock<OrganizationService>();
|
||||||
organizationService.organizations$.mockReturnValue(of([]));
|
organizationService.organizations$.mockReturnValue(of([]));
|
||||||
syncServiceMock = mock<SyncService>();
|
syncServiceMock = mock<SyncService>();
|
||||||
// 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
|
await TestBed.configureTestingModule({
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [InactiveTwoFactorReportComponent, I18nPipe],
|
declarations: [InactiveTwoFactorReportComponent, I18nPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -80,9 +80,7 @@ describe("InactiveTwoFactorReportComponent", () => {
|
|||||||
useValue: adminConsoleCipherFormConfigServiceMock,
|
useValue: adminConsoleCipherFormConfigServiceMock,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
schemas: [],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
// FIXME(PM-18598): Replace unknownElements and unknownProperties with actual imports
|
|
||||||
errorOnUnknownElements: false,
|
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from "@angular/core";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@@ -19,9 +17,8 @@ import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/se
|
|||||||
|
|
||||||
import { CipherReportComponent } from "./cipher-report.component";
|
import { CipherReportComponent } from "./cipher-report.component";
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
selector: "app-inactive-two-factor-report",
|
selector: "app-inactive-two-factor-report",
|
||||||
templateUrl: "inactive-two-factor-report.component.html",
|
templateUrl: "inactive-two-factor-report.component.html",
|
||||||
standalone: false,
|
standalone: false,
|
||||||
@@ -42,6 +39,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
|
|||||||
syncService: SyncService,
|
syncService: SyncService,
|
||||||
cipherFormConfigService: CipherFormConfigService,
|
cipherFormConfigService: CipherFormConfigService,
|
||||||
adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
||||||
|
protected changeDetectorRef: ChangeDetectorRef,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
cipherService,
|
cipherService,
|
||||||
@@ -86,6 +84,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
|
|||||||
|
|
||||||
this.filterCiphersByOrg(inactive2faCiphers);
|
this.filterCiphersByOrg(inactive2faCiphers);
|
||||||
this.cipherDocs = docs;
|
this.cipherDocs = docs;
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +156,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
|
|||||||
}
|
}
|
||||||
this.services.set(serviceData.domain, serviceData.documentation);
|
this.services.set(serviceData.domain, serviceData.documentation);
|
||||||
}
|
}
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { ChangeDetectorRef, Component, OnInit, ChangeDetectionStrategy } from "@angular/core";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import {
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
getOrganizationById,
|
|
||||||
OrganizationService,
|
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { getById } from "@bitwarden/common/platform/misc";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||||
@@ -23,9 +19,8 @@ import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vau
|
|||||||
import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service";
|
import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service";
|
||||||
import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../inactive-two-factor-report.component";
|
import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../inactive-two-factor-report.component";
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
selector: "app-inactive-two-factor-report",
|
selector: "app-inactive-two-factor-report",
|
||||||
templateUrl: "../inactive-two-factor-report.component.html",
|
templateUrl: "../inactive-two-factor-report.component.html",
|
||||||
providers: [
|
providers: [
|
||||||
@@ -44,7 +39,7 @@ export class InactiveTwoFactorReportComponent
|
|||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
// Contains a list of ciphers, the user running the report, can manage
|
// Contains a list of ciphers, the user running the report, can manage
|
||||||
private manageableCiphers: Cipher[];
|
private manageableCiphers: Cipher[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
cipherService: CipherService,
|
cipherService: CipherService,
|
||||||
@@ -58,6 +53,7 @@ export class InactiveTwoFactorReportComponent
|
|||||||
syncService: SyncService,
|
syncService: SyncService,
|
||||||
cipherFormConfigService: CipherFormConfigService,
|
cipherFormConfigService: CipherFormConfigService,
|
||||||
adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
||||||
|
protected changeDetectorRef: ChangeDetectorRef,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
cipherService,
|
cipherService,
|
||||||
@@ -70,28 +66,37 @@ export class InactiveTwoFactorReportComponent
|
|||||||
syncService,
|
syncService,
|
||||||
cipherFormConfigService,
|
cipherFormConfigService,
|
||||||
adminConsoleCipherFormConfigService,
|
adminConsoleCipherFormConfigService,
|
||||||
|
changeDetectorRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.isAdminConsoleActive = true;
|
this.isAdminConsoleActive = true;
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
|
||||||
this.route.parent.parent.params.subscribe(async (params) => {
|
this.route.parent?.parent?.params
|
||||||
|
?.pipe(takeUntil(this.destroyed$))
|
||||||
|
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||||
|
.subscribe(async (params) => {
|
||||||
const userId = await firstValueFrom(
|
const userId = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
this.organization = await firstValueFrom(
|
this.organization = await firstValueFrom(
|
||||||
this.organizationService
|
this.organizationService.organizations$(userId).pipe(getById(params.organizationId)),
|
||||||
.organizations$(userId)
|
|
||||||
.pipe(getOrganizationById(params.organizationId)),
|
|
||||||
);
|
);
|
||||||
this.manageableCiphers = await this.cipherService.getAll(userId);
|
this.manageableCiphers = await this.cipherService.getAll(userId);
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
|
}
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllCiphers(): Promise<CipherView[]> {
|
async getAllCiphers(): Promise<CipherView[]> {
|
||||||
return this.cipherService.getAllFromApiForOrganization(this.organization.id);
|
if (this.organization) {
|
||||||
|
return await this.cipherService.getAllFromApiForOrganization(this.organization.id, true);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected canManageCipher(c: CipherView): boolean {
|
protected canManageCipher(c: CipherView): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user