mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 06:43:35 +00:00
[PM-13455] Wire up results from RiskInsightsReportService (#12206)
* 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 <ttalty@bitwarden.com> Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> Co-authored-by: Tom <144813356+ttalty@users.noreply.github.com> Co-authored-by: voommen-livefront <voommen@livefront.com>
This commit is contained in:
committed by
GitHub
parent
12b698b11d
commit
12eb77fd45
@@ -1,5 +1,6 @@
|
|||||||
import { TestBed } from "@angular/core/testing";
|
import { mock } from "jest-mock-extended";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
import { ZXCVBNResult } from "zxcvbn";
|
||||||
|
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||||
@@ -12,42 +13,31 @@ import { RiskInsightsReportService } from "./risk-insights-report.service";
|
|||||||
|
|
||||||
describe("RiskInsightsReportService", () => {
|
describe("RiskInsightsReportService", () => {
|
||||||
let service: RiskInsightsReportService;
|
let service: RiskInsightsReportService;
|
||||||
|
const pwdStrengthService = mock<PasswordStrengthServiceAbstraction>();
|
||||||
|
const auditService = mock<AuditService>();
|
||||||
|
const cipherService = mock<CipherService>();
|
||||||
|
const memberCipherDetailsService = mock<MemberCipherDetailsApiService>();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
pwdStrengthService.getPasswordStrength.mockImplementation((password: string) => {
|
||||||
providers: [
|
const score = password.length < 4 ? 1 : 4;
|
||||||
RiskInsightsReportService,
|
return { score } as ZXCVBNResult;
|
||||||
{
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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 () => {
|
it("should generate the raw data report correctly", async () => {
|
||||||
|
|||||||
@@ -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
|
// @ts-strict-ignore
|
||||||
|
|
||||||
import { Injectable } from "@angular/core";
|
|
||||||
import { concatMap, first, from, map, Observable, zip } from "rxjs";
|
import { concatMap, first, from, map, Observable, zip } from "rxjs";
|
||||||
|
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
@@ -24,7 +22,6 @@ import {
|
|||||||
|
|
||||||
import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";
|
import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class RiskInsightsReportService {
|
export class RiskInsightsReportService {
|
||||||
constructor(
|
constructor(
|
||||||
private passwordStrengthService: PasswordStrengthServiceAbstraction,
|
private passwordStrengthService: PasswordStrengthServiceAbstraction,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { MaximumVaultTimeoutPolicyComponent } from "./admin-console/policies/max
|
|||||||
import { AppRoutingModule } from "./app-routing.module";
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import { AppComponent } from "./app.component";
|
import { AppComponent } from "./app.component";
|
||||||
import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-families-sponsorship.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.
|
* This is the AppModule for the commercial version of Bitwarden.
|
||||||
@@ -41,6 +42,7 @@ import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
OssRoutingModule,
|
OssRoutingModule,
|
||||||
OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
|
OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
|
||||||
|
AccessIntelligenceModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
import { NgModule } from "@angular/core";
|
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 { AccessIntelligenceRoutingModule } from "./access-intelligence-routing.module";
|
||||||
import { RiskInsightsComponent } from "./risk-insights.component";
|
import { RiskInsightsComponent } from "./risk-insights.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RiskInsightsComponent, AccessIntelligenceRoutingModule],
|
imports: [RiskInsightsComponent, AccessIntelligenceRoutingModule],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: MemberCipherDetailsApiService,
|
||||||
|
deps: [ApiService],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RiskInsightsReportService,
|
||||||
|
deps: [
|
||||||
|
PasswordStrengthServiceAbstraction,
|
||||||
|
AuditService,
|
||||||
|
CipherService,
|
||||||
|
MemberCipherDetailsApiService,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class AccessIntelligenceModule {}
|
export class AccessIntelligenceModule {}
|
||||||
|
|||||||
@@ -34,10 +34,10 @@
|
|||||||
<td bitCell class="tw-text-right">
|
<td bitCell class="tw-text-right">
|
||||||
<span
|
<span
|
||||||
bitBadge
|
bitBadge
|
||||||
*ngIf="passwordStrengthMap.has(r.id)"
|
*ngIf="r.weakPasswordDetail"
|
||||||
[variant]="passwordStrengthMap.get(r.id)[1]"
|
[variant]="r.weakPasswordDetail?.detailValue.badgeVariant"
|
||||||
>
|
>
|
||||||
{{ passwordStrengthMap.get(r.id)[0] | i18n }}
|
{{ r.weakPasswordDetail?.detailValue.label | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td bitCell class="tw-text-right">
|
<td bitCell class="tw-text-right">
|
||||||
@@ -46,8 +46,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td bitCell class="tw-text-right">
|
<td bitCell class="tw-text-right">
|
||||||
<span bitBadge *ngIf="exposedPasswordMap.has(r.id)" variant="warning">
|
<span bitBadge variant="warning" *ngIf="r.exposedPasswordDetail">
|
||||||
{{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }}
|
{{ "exposedXTimes" | i18n: r.exposedPasswordDetail?.exposedXTimes }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -3,15 +3,8 @@ import { ActivatedRoute, convertToParamMap } from "@angular/router";
|
|||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import {
|
import { RiskInsightsReportService } from "@bitwarden/bit-common/tools/reports/risk-insights";
|
||||||
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
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 { TableModule } from "@bitwarden/components";
|
||||||
import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared";
|
import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared";
|
||||||
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
|
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
|
||||||
@@ -28,19 +21,8 @@ describe("PasswordHealthComponent", () => {
|
|||||||
imports: [PasswordHealthComponent, PipesModule, TableModule, LooseComponentsModule],
|
imports: [PasswordHealthComponent, PipesModule, TableModule, LooseComponentsModule],
|
||||||
declarations: [],
|
declarations: [],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CipherService, useValue: mock<CipherService>() },
|
{ provide: RiskInsightsReportService, useValue: mock<RiskInsightsReportService>() },
|
||||||
{ provide: I18nService, useValue: mock<I18nService>() },
|
{ 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,
|
provide: ActivatedRoute,
|
||||||
useValue: {
|
useValue: {
|
||||||
|
|||||||
@@ -4,21 +4,14 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { Component, DestroyRef, inject, OnInit } from "@angular/core";
|
import { Component, DestroyRef, inject, OnInit } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import {
|
import { RiskInsightsReportService } from "@bitwarden/bit-common/tools/reports/risk-insights";
|
||||||
MemberCipherDetailsApiService,
|
import { CipherHealthReportDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
|
||||||
PasswordHealthService,
|
|
||||||
} from "@bitwarden/bit-common/tools/reports/risk-insights";
|
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
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 {
|
import {
|
||||||
BadgeModule,
|
BadgeModule,
|
||||||
BadgeVariant,
|
|
||||||
ContainerComponent,
|
ContainerComponent,
|
||||||
TableDataSource,
|
TableDataSource,
|
||||||
TableModule,
|
TableModule,
|
||||||
@@ -41,28 +34,19 @@ import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pip
|
|||||||
HeaderModule,
|
HeaderModule,
|
||||||
TableModule,
|
TableModule,
|
||||||
],
|
],
|
||||||
providers: [PasswordHealthService, MemberCipherDetailsApiService],
|
|
||||||
})
|
})
|
||||||
export class PasswordHealthComponent implements OnInit {
|
export class PasswordHealthComponent implements OnInit {
|
||||||
passwordStrengthMap = new Map<string, [string, BadgeVariant]>();
|
|
||||||
|
|
||||||
passwordUseMap = new Map<string, number>();
|
passwordUseMap = new Map<string, number>();
|
||||||
|
dataSource = new TableDataSource<CipherHealthReportDetail>();
|
||||||
exposedPasswordMap = new Map<string, number>();
|
|
||||||
|
|
||||||
dataSource = new TableDataSource<CipherView>();
|
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
private destroyRef = inject(DestroyRef);
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected cipherService: CipherService,
|
protected riskInsightsReportService: RiskInsightsReportService,
|
||||||
protected passwordStrengthService: PasswordStrengthServiceAbstraction,
|
|
||||||
protected auditService: AuditService,
|
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected activatedRoute: ActivatedRoute,
|
protected activatedRoute: ActivatedRoute,
|
||||||
protected memberCipherDetailsApiService: MemberCipherDetailsApiService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -78,20 +62,9 @@ export class PasswordHealthComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setCiphers(organizationId: string) {
|
async setCiphers(organizationId: string) {
|
||||||
const passwordHealthService = new PasswordHealthService(
|
this.dataSource.data = await firstValueFrom(
|
||||||
this.passwordStrengthService,
|
this.riskInsightsReportService.generateRawDataReport$(organizationId),
|
||||||
this.auditService,
|
|
||||||
this.cipherService,
|
|
||||||
this.memberCipherDetailsApiService,
|
|
||||||
organizationId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await passwordHealthService.generateReport();
|
|
||||||
|
|
||||||
this.dataSource.data = passwordHealthService.reportCiphers;
|
|
||||||
this.exposedPasswordMap = passwordHealthService.exposedPasswordMap;
|
|
||||||
this.passwordStrengthMap = passwordHealthService.passwordStrengthMap;
|
|
||||||
this.passwordUseMap = passwordHealthService.passwordUseMap;
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user