Skip to content

Commit

Permalink
[PM-13455] Wire up results from RiskInsightsReportService (#12206)
Browse files Browse the repository at this point in the history
* Risk insights aggregation in a new service. Initial PR.

* Wire up results from RiskInsightsReportService

* Ignoring all non-login items and refactoring into a method

* Cleaning up the documentation a little

* logic for generating the report summary

* application summary to list at risk applications not passwords

* Adding more documentation and moving types to it's own file

* Awaiting the raw data report and adding the start of the test file

* Extend access-intelligence.module to provide needed services

* Register access-intelligence.module with bit-web AppModule

* Use provided RiskInsightsService instead of new'ing one in the component

* Removing the injectable attribute from RiskInsightsReportService

* Fix tests

* Adding more test cases

* Removing unnecessary file

* Test cases update

* Fixing memeber details test to have new member

* Fixing password health tests

* Moving to observables

* removing commented code

* commented code

* Switching from ternary to if/else

* nullable types

* one more nullable type

* Adding the fixme for strict types

* moving the fixme

* No need to access the password use map and switching to the observable

* PM-13455 fixes to unit tests

---------

Co-authored-by: Tom <[email protected]>
Co-authored-by: Daniel James Smith <[email protected]>
Co-authored-by: Tom <[email protected]>
Co-authored-by: voommen-livefront <[email protected]>
  • Loading branch information
5 people authored Dec 18, 2024
1 parent 12b698b commit 12eb77f
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TestBed } from "@angular/core/testing";
import { mock } from "jest-mock-extended";
import { firstValueFrom } from "rxjs";
import { ZXCVBNResult } from "zxcvbn";

import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
Expand All @@ -12,42 +13,31 @@ import { RiskInsightsReportService } from "./risk-insights-report.service";

describe("RiskInsightsReportService", () => {
let service: RiskInsightsReportService;
const pwdStrengthService = mock<PasswordStrengthServiceAbstraction>();
const auditService = mock<AuditService>();
const cipherService = mock<CipherService>();
const memberCipherDetailsService = mock<MemberCipherDetailsApiService>();

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
RiskInsightsReportService,
{
provide: PasswordStrengthServiceAbstraction,
useValue: {
getPasswordStrength: (password: string) => {
const score = password.length < 4 ? 1 : 4;
return { score };
},
},
},
{
provide: AuditService,
useValue: {
passwordLeaked: (password: string) => Promise.resolve(password === "123" ? 100 : 0),
},
},
{
provide: CipherService,
useValue: {
getAllFromApiForOrganization: jest.fn().mockResolvedValue(mockCiphers),
},
},
{
provide: MemberCipherDetailsApiService,
useValue: {
getMemberCipherDetails: jest.fn().mockResolvedValue(mockMemberCipherDetails),
},
},
],
pwdStrengthService.getPasswordStrength.mockImplementation((password: string) => {
const score = password.length < 4 ? 1 : 4;
return { score } as ZXCVBNResult;
});

service = TestBed.inject(RiskInsightsReportService);
auditService.passwordLeaked.mockImplementation((password: string) =>
Promise.resolve(password === "123" ? 100 : 0),
);

cipherService.getAllFromApiForOrganization.mockResolvedValue(mockCiphers);

memberCipherDetailsService.getMemberCipherDetails.mockResolvedValue(mockMemberCipherDetails);

service = new RiskInsightsReportService(
pwdStrengthService,
auditService,
cipherService,
memberCipherDetailsService,
);
});

it("should generate the raw data report correctly", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// FIXME: Update this file to be type safe
// @ts-strict-ignore

import { Injectable } from "@angular/core";
import { concatMap, first, from, map, Observable, zip } from "rxjs";

import { AuditService } from "@bitwarden/common/abstractions/audit.service";
Expand All @@ -24,7 +22,6 @@ import {

import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";

@Injectable()
export class RiskInsightsReportService {
constructor(
private passwordStrengthService: PasswordStrengthServiceAbstraction,
Expand Down
2 changes: 2 additions & 0 deletions bitwarden_license/bit-web/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { MaximumVaultTimeoutPolicyComponent } from "./admin-console/policies/max
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-families-sponsorship.component";
import { AccessIntelligenceModule } from "./tools/access-intelligence/access-intelligence.module";

/**
* This is the AppModule for the commercial version of Bitwarden.
Expand All @@ -41,6 +42,7 @@ import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-
AppRoutingModule,
OssRoutingModule,
OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
AccessIntelligenceModule,
RouterModule,
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
import { NgModule } from "@angular/core";

import {
MemberCipherDetailsApiService,
RiskInsightsReportService,
} from "@bitwarden/bit-common/tools/reports/risk-insights/services";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength/password-strength.service.abstraction";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";

import { AccessIntelligenceRoutingModule } from "./access-intelligence-routing.module";
import { RiskInsightsComponent } from "./risk-insights.component";

@NgModule({
imports: [RiskInsightsComponent, AccessIntelligenceRoutingModule],
providers: [
{
provide: MemberCipherDetailsApiService,
deps: [ApiService],
},
{
provide: RiskInsightsReportService,
deps: [
PasswordStrengthServiceAbstraction,
AuditService,
CipherService,
MemberCipherDetailsApiService,
],
},
],
})
export class AccessIntelligenceModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
<td bitCell class="tw-text-right">
<span
bitBadge
*ngIf="passwordStrengthMap.has(r.id)"
[variant]="passwordStrengthMap.get(r.id)[1]"
*ngIf="r.weakPasswordDetail"
[variant]="r.weakPasswordDetail?.detailValue.badgeVariant"
>
{{ passwordStrengthMap.get(r.id)[0] | i18n }}
{{ r.weakPasswordDetail?.detailValue.label | i18n }}
</span>
</td>
<td bitCell class="tw-text-right">
Expand All @@ -46,8 +46,8 @@
</span>
</td>
<td bitCell class="tw-text-right">
<span bitBadge *ngIf="exposedPasswordMap.has(r.id)" variant="warning">
{{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }}
<span bitBadge variant="warning" *ngIf="r.exposedPasswordDetail">
{{ "exposedXTimes" | i18n: r.exposedPasswordDetail?.exposedXTimes }}
</span>
</td>
</tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,8 @@ import { ActivatedRoute, convertToParamMap } from "@angular/router";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";

import {
MemberCipherDetailsApiService,
PasswordHealthService,
} from "@bitwarden/bit-common/tools/reports/risk-insights";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { RiskInsightsReportService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { TableModule } from "@bitwarden/components";
import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared";
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
Expand All @@ -28,19 +21,8 @@ describe("PasswordHealthComponent", () => {
imports: [PasswordHealthComponent, PipesModule, TableModule, LooseComponentsModule],
declarations: [],
providers: [
{ provide: CipherService, useValue: mock<CipherService>() },
{ provide: RiskInsightsReportService, useValue: mock<RiskInsightsReportService>() },
{ provide: I18nService, useValue: mock<I18nService>() },
{ provide: AuditService, useValue: mock<AuditService>() },
{ provide: ApiService, useValue: mock<ApiService>() },
{ provide: MemberCipherDetailsApiService, useValue: mock<MemberCipherDetailsApiService>() },
{
provide: PasswordStrengthServiceAbstraction,
useValue: mock<PasswordStrengthServiceAbstraction>(),
},
{
provide: PasswordHealthService,
useValue: mock<PasswordHealthService>(),
},
{
provide: ActivatedRoute,
useValue: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,14 @@ import { CommonModule } from "@angular/common";
import { Component, DestroyRef, inject, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute } from "@angular/router";
import { map } from "rxjs";
import { firstValueFrom, map } from "rxjs";

import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
MemberCipherDetailsApiService,
PasswordHealthService,
} from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { RiskInsightsReportService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { CipherHealthReportDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
BadgeModule,
BadgeVariant,
ContainerComponent,
TableDataSource,
TableModule,
Expand All @@ -41,28 +34,19 @@ import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pip
HeaderModule,
TableModule,
],
providers: [PasswordHealthService, MemberCipherDetailsApiService],
})
export class PasswordHealthComponent implements OnInit {
passwordStrengthMap = new Map<string, [string, BadgeVariant]>();

passwordUseMap = new Map<string, number>();

exposedPasswordMap = new Map<string, number>();

dataSource = new TableDataSource<CipherView>();
dataSource = new TableDataSource<CipherHealthReportDetail>();

loading = true;

private destroyRef = inject(DestroyRef);

constructor(
protected cipherService: CipherService,
protected passwordStrengthService: PasswordStrengthServiceAbstraction,
protected auditService: AuditService,
protected riskInsightsReportService: RiskInsightsReportService,
protected i18nService: I18nService,
protected activatedRoute: ActivatedRoute,
protected memberCipherDetailsApiService: MemberCipherDetailsApiService,
) {}

ngOnInit() {
Expand All @@ -78,20 +62,9 @@ export class PasswordHealthComponent implements OnInit {
}

async setCiphers(organizationId: string) {
const passwordHealthService = new PasswordHealthService(
this.passwordStrengthService,
this.auditService,
this.cipherService,
this.memberCipherDetailsApiService,
organizationId,
this.dataSource.data = await firstValueFrom(
this.riskInsightsReportService.generateRawDataReport$(organizationId),
);

await passwordHealthService.generateReport();

this.dataSource.data = passwordHealthService.reportCiphers;
this.exposedPasswordMap = passwordHealthService.exposedPasswordMap;
this.passwordStrengthMap = passwordHealthService.passwordStrengthMap;
this.passwordUseMap = passwordHealthService.passwordUseMap;
this.loading = false;
}
}

0 comments on commit 12eb77f

Please sign in to comment.