Skip to content

Commit

Permalink
AC-1333 vault report org ciphers (#5998)
Browse files Browse the repository at this point in the history
* updated report components to only show can edit ciphers, added badges, spec files
---------
Co-authored-by: Daniel James Smith <[email protected]>
  • Loading branch information
Jingo88 authored Nov 17, 2023
1 parent 3952af0 commit a141890
Show file tree
Hide file tree
Showing 24 changed files with 690 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ActivatedRoute } from "@angular/router";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
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";
Expand All @@ -25,12 +24,11 @@ export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportC
cipherService: CipherService,
auditService: AuditService,
modalService: ModalService,
messagingService: MessagingService,
private organizationService: OrganizationService,
organizationService: OrganizationService,
private route: ActivatedRoute,
passwordRepromptService: PasswordRepromptService
) {
super(cipherService, auditService, modalService, messagingService, passwordRepromptService);
super(cipherService, auditService, organizationService, modalService, passwordRepromptService);
}

async ngOnInit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ActivatedRoute } from "@angular/router";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
Expand All @@ -21,13 +20,12 @@ export class InactiveTwoFactorReportComponent extends BaseInactiveTwoFactorRepor
constructor(
cipherService: CipherService,
modalService: ModalService,
messagingService: MessagingService,
private route: ActivatedRoute,
logService: LogService,
passwordRepromptService: PasswordRepromptService,
private organizationService: OrganizationService
organizationService: OrganizationService
) {
super(cipherService, modalService, messagingService, logService, passwordRepromptService);
super(cipherService, organizationService, modalService, logService, passwordRepromptService);
}

async ngOnInit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { ActivatedRoute } from "@angular/router";

import { ModalService } from "@bitwarden/angular/services/modal.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
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";
Expand All @@ -24,13 +22,11 @@ export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportCom
constructor(
cipherService: CipherService,
modalService: ModalService,
messagingService: MessagingService,
stateService: StateService,
private route: ActivatedRoute,
private organizationService: OrganizationService,
organizationService: OrganizationService,
passwordRepromptService: PasswordRepromptService
) {
super(cipherService, modalService, messagingService, stateService, passwordRepromptService);
super(cipherService, organizationService, modalService, passwordRepromptService);
}

async ngOnInit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ActivatedRoute } from "@angular/router";

import { ModalService } from "@bitwarden/angular/services/modal.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
Expand All @@ -20,12 +19,11 @@ export class UnsecuredWebsitesReportComponent extends BaseUnsecuredWebsitesRepor
constructor(
cipherService: CipherService,
modalService: ModalService,
messagingService: MessagingService,
private route: ActivatedRoute,
private organizationService: OrganizationService,
organizationService: OrganizationService,
passwordRepromptService: PasswordRepromptService
) {
super(cipherService, modalService, messagingService, passwordRepromptService);
super(cipherService, organizationService, modalService, passwordRepromptService);
}

async ngOnInit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ActivatedRoute } from "@angular/router";

import { ModalService } from "@bitwarden/angular/services/modal.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
Expand All @@ -25,16 +24,15 @@ export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportCompone
cipherService: CipherService,
passwordStrengthService: PasswordStrengthServiceAbstraction,
modalService: ModalService,
messagingService: MessagingService,
private route: ActivatedRoute,
private organizationService: OrganizationService,
organizationService: OrganizationService,
passwordRepromptService: PasswordRepromptService
) {
super(
cipherService,
passwordStrengthService,
organizationService,
modalService,
messagingService,
passwordRepromptService
);
}
Expand Down
13 changes: 8 additions & 5 deletions apps/web/src/app/reports/pages/cipher-report.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
import { Observable } from "rxjs";

import { ModalService } from "@bitwarden/angular/services/modal.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
Expand All @@ -19,13 +20,15 @@ export class CipherReportComponent {
hasLoaded = false;
ciphers: CipherView[] = [];
organization: Organization;
organizations$: Observable<Organization[]>;

constructor(
private modalService: ModalService,
protected messagingService: MessagingService,
public requiresPaid: boolean,
protected passwordRepromptService: PasswordRepromptService
) {}
protected passwordRepromptService: PasswordRepromptService,
protected organizationService: OrganizationService
) {
this.organizations$ = this.organizationService.organizations$;
}

async load() {
this.loading = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ <h1>{{ "exposedPasswordsReport" | i18n }}</h1>
<br />
<small>{{ c.subTitle }}</small>
</td>
<td>
<app-org-badge
*ngIf="!organization"
[disabled]="disabled"
[organizationId]="c.organizationId"
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
appStopProp
>
</app-org-badge>
</td>
<td class="text-right">
<span bitBadge badgeType="warning">
{{ "exposedXTimes" | i18n : (exposedPasswordMap.get(c.id) | number) }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// eslint-disable-next-line no-restricted-imports
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { mock, MockProxy } from "jest-mock-extended";

import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/vault";

import { ExposedPasswordsReportComponent } from "./exposed-passwords-report.component";
import { cipherData } from "./reports-ciphers.mock";

describe("ExposedPasswordsReportComponent", () => {
let component: ExposedPasswordsReportComponent;
let fixture: ComponentFixture<ExposedPasswordsReportComponent>;
let auditService: MockProxy<AuditService>;

beforeEach(() => {
auditService = mock<AuditService>();
TestBed.configureTestingModule({
declarations: [ExposedPasswordsReportComponent, I18nPipe],
providers: [
{
provide: CipherService,
useValue: mock<CipherService>(),
},
{
provide: AuditService,
useValue: auditService,
},
{
provide: OrganizationService,
useValue: mock<OrganizationService>(),
},
{
provide: ModalService,
useValue: mock<ModalService>(),
},
{
provide: PasswordRepromptService,
useValue: mock<PasswordRepromptService>(),
},
{
provide: I18nService,
useValue: mock<I18nService>(),
},
],
schemas: [],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(ExposedPasswordsReportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it("should initialize component", () => {
expect(component).toBeTruthy();
});

it('should get only ciphers with exposed passwords that the user has "Can Edit" access to', async () => {
const expectedIdOne: any = "cbea34a8-bde4-46ad-9d19-b05001228ab2";
const expectedIdTwo = "cbea34a8-bde4-46ad-9d19-b05001228cd3";

jest.spyOn(auditService, "passwordLeaked").mockReturnValue(Promise.resolve<any>(1234));
jest.spyOn(component as any, "getAllCiphers").mockReturnValue(Promise.resolve<any>(cipherData));
await component.setCiphers();

expect(component.ciphers.length).toEqual(2);
expect(component.ciphers[0].id).toEqual(expectedIdOne);
expect(component.ciphers[0].edit).toEqual(true);
expect(component.ciphers[1].id).toEqual(expectedIdTwo);
expect(component.ciphers[1].edit).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Component, OnInit } from "@angular/core";

import { ModalService } from "@bitwarden/angular/services/modal.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
Expand All @@ -16,15 +16,16 @@ import { CipherReportComponent } from "./cipher-report.component";
})
export class ExposedPasswordsReportComponent extends CipherReportComponent implements OnInit {
exposedPasswordMap = new Map<string, number>();
disabled = true;

constructor(
protected cipherService: CipherService,
protected auditService: AuditService,
protected organizationService: OrganizationService,
modalService: ModalService,
messagingService: MessagingService,
passwordRepromptService: PasswordRepromptService
) {
super(modalService, messagingService, true, passwordRepromptService);
super(modalService, passwordRepromptService, organizationService);
}

async ngOnInit() {
Expand All @@ -35,25 +36,28 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple
const allCiphers = await this.getAllCiphers();
const exposedPasswordCiphers: CipherView[] = [];
const promises: Promise<void>[] = [];
allCiphers.forEach((c) => {
allCiphers.forEach((ciph) => {
const { type, login, isDeleted, edit, viewPassword, id } = ciph;
if (
c.type !== CipherType.Login ||
c.login.password == null ||
c.login.password === "" ||
c.isDeleted
type !== CipherType.Login ||
login.password == null ||
login.password === "" ||
isDeleted ||
(!this.organization && !edit) ||
!viewPassword
) {
return;
}
const promise = this.auditService.passwordLeaked(c.login.password).then((exposedCount) => {
const promise = this.auditService.passwordLeaked(login.password).then((exposedCount) => {
if (exposedCount > 0) {
exposedPasswordCiphers.push(c);
this.exposedPasswordMap.set(c.id, exposedCount);
exposedPasswordCiphers.push(ciph);
this.exposedPasswordMap.set(id, exposedCount);
}
});
promises.push(promise);
});
await Promise.all(promises);
this.ciphers = exposedPasswordCiphers;
this.ciphers = [...exposedPasswordCiphers];
}

protected getAllCiphers(): Promise<CipherView[]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ <h1>
<br />
<small>{{ c.subTitle }}</small>
</td>
<td>
<app-org-badge
*ngIf="!organization"
[disabled]="disabled"
[organizationId]="c.organizationId"
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
appStopProp
>
</app-org-badge>
</td>
<td class="text-right">
<a
bitBadge
Expand Down
Loading

0 comments on commit a141890

Please sign in to comment.