1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 22:33:35 +00:00

[PM-25607] Separate Models and account for legacy (#16448)

* organize password-health.ts contents into new model files

* revert naming

* revert to state of use save service pr draft

* LEGACY_MemberDetailsFlat

* legacy updates to password health file

* update imports

* fix import errors

* - revert unnecessary encrypteddatamodel changes
-add it back to password-health.ts
- revert the type changes of variables in EncryptedDataWithKey

* quick fix
This commit is contained in:
Alex
2025-09-18 14:08:07 -04:00
committed by GitHub
parent 73a9bd81e7
commit d6cd30cf54
17 changed files with 322 additions and 229 deletions

View File

@@ -2,16 +2,16 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { import {
MemberDetailsFlat, LEGACY_MemberDetailsFlat,
CipherHealthReportDetail, LEGACY_CipherHealthReportDetail,
CipherHealthReportUriDetail, LEGACY_CipherHealthReportUriDetail,
ApplicationHealthReportDetail,
} from "../models/password-health"; } from "../models/password-health";
import { ApplicationHealthReportDetail } from "../models/report-models";
import { MemberCipherDetailsResponse } from "../response/member-cipher-details.response"; import { MemberCipherDetailsResponse } from "../response/member-cipher-details.response";
export function flattenMemberDetails( export function flattenMemberDetails(
memberCiphers: MemberCipherDetailsResponse[], memberCiphers: MemberCipherDetailsResponse[],
): MemberDetailsFlat[] { ): LEGACY_MemberDetailsFlat[] {
return memberCiphers.flatMap((member) => return memberCiphers.flatMap((member) =>
member.cipherIds.map((cipherId) => ({ member.cipherIds.map((cipherId) => ({
userGuid: member.userGuid, userGuid: member.userGuid,
@@ -44,7 +44,9 @@ export function getTrimmedCipherUris(cipher: CipherView): string[] {
} }
// Returns a deduplicated array of members by email // Returns a deduplicated array of members by email
export function getUniqueMembers(orgMembers: MemberDetailsFlat[]): MemberDetailsFlat[] { export function getUniqueMembers(
orgMembers: LEGACY_MemberDetailsFlat[],
): LEGACY_MemberDetailsFlat[] {
const existingEmails = new Set<string>(); const existingEmails = new Set<string>();
return orgMembers.filter((member) => { return orgMembers.filter((member) => {
if (existingEmails.has(member.email)) { if (existingEmails.has(member.email)) {
@@ -68,7 +70,7 @@ export function getMemberDetailsFlat(
userName: string, userName: string,
email: string, email: string,
cipherId: string, cipherId: string,
): MemberDetailsFlat { ): LEGACY_MemberDetailsFlat {
return { return {
userGuid: userGuid, userGuid: userGuid,
userName: userName, userName: userName,
@@ -84,9 +86,9 @@ export function getMemberDetailsFlat(
* @returns Flattened cipher health details to URI * @returns Flattened cipher health details to URI
*/ */
export function getFlattenedCipherDetails( export function getFlattenedCipherDetails(
detail: CipherHealthReportDetail, detail: LEGACY_CipherHealthReportDetail,
uri: string, uri: string,
): CipherHealthReportUriDetail { ): LEGACY_CipherHealthReportUriDetail {
return { return {
cipherId: detail.id, cipherId: detail.id,
reusedPasswordCount: detail.reusedPasswordCount, reusedPasswordCount: detail.reusedPasswordCount,
@@ -107,7 +109,7 @@ export function getFlattenedCipherDetails(
* @returns The new or updated application health report detail * @returns The new or updated application health report detail
*/ */
export function getApplicationReportDetail( export function getApplicationReportDetail(
newUriDetail: CipherHealthReportUriDetail, newUriDetail: LEGACY_CipherHealthReportUriDetail,
isAtRisk: boolean, isAtRisk: boolean,
existingUriDetail?: ApplicationHealthReportDetail, existingUriDetail?: ApplicationHealthReportDetail,
): ApplicationHealthReportDetail { ): ApplicationHealthReportDetail {

View File

@@ -0,0 +1,49 @@
import { EncryptedString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { PasswordHealthReportApplicationId, RiskInsightsReport } from "./report-models";
// -------------------- Password Health Report Models --------------------
/**
* Request to drop a password health report application
* Model is expected by the API endpoint
*/
export interface PasswordHealthReportApplicationDropRequest {
organizationId: OrganizationId;
passwordHealthReportApplicationIds: string[];
}
/**
* Response from the API after marking an app as critical
*/
export interface PasswordHealthReportApplicationsResponse {
id: PasswordHealthReportApplicationId;
organizationId: OrganizationId;
uri: string;
}
/*
* Request to save a password health report application
* Model is expected by the API endpoint
*/
export interface PasswordHealthReportApplicationsRequest {
organizationId: OrganizationId;
url: string;
}
// -------------------- Risk Insights Report Models --------------------
export interface SaveRiskInsightsReportRequest {
data: RiskInsightsReport;
}
export interface SaveRiskInsightsReportResponse {
id: string;
}
export interface GetRiskInsightsReportResponse {
id: string;
organizationId: OrganizationId;
// TODO Update to use creationDate from server
date: string;
reportData: EncryptedString;
contentEncryptionKey: EncryptedString;
}

View File

@@ -1,75 +1,11 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { Opaque } from "type-fest";
import { OrganizationId } from "@bitwarden/common/types/guid"; import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { BadgeVariant } from "@bitwarden/components"; import { BadgeVariant } from "@bitwarden/components";
import { EncString } from "@bitwarden/sdk-internal"; import { EncString } from "@bitwarden/sdk-internal";
/** import { ApplicationHealthReportDetail } from "./report-models";
* All applications report summary. The total members,
* total at risk members, application, and at risk application
* counts. Aggregated from all calculated applications
*/
export type ApplicationHealthReportSummary = {
totalMemberCount: number;
totalAtRiskMemberCount: number;
totalApplicationCount: number;
totalAtRiskApplicationCount: number;
};
/**
* All applications report detail. Application is the cipher
* uri. Has the at risk, password, and member information
*/
export type ApplicationHealthReportDetail = {
applicationName: string;
passwordCount: number;
atRiskPasswordCount: number;
atRiskCipherIds: string[];
memberCount: number;
atRiskMemberCount: number;
memberDetails: MemberDetailsFlat[];
atRiskMemberDetails: MemberDetailsFlat[];
cipherIds: string[];
};
export type ApplicationHealthReportDetailWithCriticalFlag = ApplicationHealthReportDetail & {
isMarkedAsCritical: boolean;
};
export type ApplicationHealthReportDetailWithCriticalFlagAndCipher =
ApplicationHealthReportDetailWithCriticalFlag & {
ciphers: CipherView[];
};
/**
* Breaks the cipher health info out by uri and passes
* along the password health and member info
*/
export type CipherHealthReportUriDetail = {
cipherId: string;
reusedPasswordCount: number;
weakPasswordDetail: WeakPasswordDetail;
exposedPasswordDetail: ExposedPasswordDetail;
cipherMembers: MemberDetailsFlat[];
trimmedUri: string;
cipher: CipherView;
};
/**
* Associates a cipher with it's essential information.
* Gets the password health details, cipher members, and
* the trimmed uris for the cipher
*/
export type CipherHealthReportDetail = CipherView & {
reusedPasswordCount: number;
weakPasswordDetail: WeakPasswordDetail;
exposedPasswordDetail: ExposedPasswordDetail;
cipherMembers: MemberDetailsFlat[];
trimmedUris: string[];
};
/** /**
* Weak password details containing the score * Weak password details containing the score
@@ -97,41 +33,6 @@ export type ExposedPasswordDetail = {
exposedXTimes: number; exposedXTimes: number;
} | null; } | null;
/**
* Flattened member details that associates an
* organization member to a cipher
*/
export type MemberDetailsFlat = {
userGuid: string;
userName: string;
email: string;
cipherId: string;
};
/**
* Member email with the number of at risk passwords
* At risk member detail that contains the email
* and the count of at risk ciphers
*/
export type AtRiskMemberDetail = {
email: string;
atRiskPasswordCount: number;
};
/*
* A list of applications and the count of
* at risk passwords for each application
*/
export type AtRiskApplicationDetail = {
applicationName: string;
atRiskPasswordCount: number;
};
export type AppAtRiskMembersDialogParams = {
members: MemberDetailsFlat[];
applicationName: string;
};
/* /*
* After data is encrypted, it is returned with the * After data is encrypted, it is returned with the
* encryption key used to encrypt the data. * encryption key used to encrypt the data.
@@ -142,31 +43,39 @@ export interface EncryptedDataWithKey {
encryptionKey: EncString; encryptionKey: EncString;
} }
/** export type LEGACY_MemberDetailsFlat = {
* Request to drop a password health report application userGuid: string;
* Model is expected by the API endpoint userName: string;
*/ email: string;
export interface PasswordHealthReportApplicationDropRequest { cipherId: string;
organizationId: OrganizationId; };
passwordHealthReportApplicationIds: string[];
}
/** export type LEGACY_ApplicationHealthReportDetailWithCriticalFlag = ApplicationHealthReportDetail & {
* Response from the API after marking an app as critical isMarkedAsCritical: boolean;
*/ };
export interface PasswordHealthReportApplicationsResponse {
id: PasswordHealthReportApplicationId; export type LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher =
organizationId: OrganizationId; LEGACY_ApplicationHealthReportDetailWithCriticalFlag & {
uri: string; ciphers: CipherView[];
} };
/*
* Request to save a password health report application export type LEGACY_CipherHealthReportDetail = CipherView & {
* Model is expected by the API endpoint reusedPasswordCount: number;
*/ weakPasswordDetail: WeakPasswordDetail;
export interface PasswordHealthReportApplicationsRequest { exposedPasswordDetail: ExposedPasswordDetail;
organizationId: OrganizationId; cipherMembers: LEGACY_MemberDetailsFlat[];
url: string; trimmedUris: string[];
} };
export type LEGACY_CipherHealthReportUriDetail = {
cipherId: string;
reusedPasswordCount: number;
weakPasswordDetail: WeakPasswordDetail;
exposedPasswordDetail: ExposedPasswordDetail;
cipherMembers: LEGACY_MemberDetailsFlat[];
trimmedUri: string;
cipher: CipherView;
};
export interface EncryptedDataModel { export interface EncryptedDataModel {
organizationId: OrganizationId; organizationId: OrganizationId;
@@ -174,42 +83,3 @@ export interface EncryptedDataModel {
encryptionKey: string; encryptionKey: string;
date: Date; date: Date;
} }
// FIXME: update to use a const object instead of a typescript enum
// eslint-disable-next-line @bitwarden/platform/no-enums
export enum DrawerType {
None = 0,
AppAtRiskMembers = 1,
OrgAtRiskMembers = 2,
OrgAtRiskApps = 3,
}
export interface RiskInsightsReport {
organizationId: OrganizationId;
date: string;
reportData: string;
reportKey: string;
}
export interface ReportInsightsReportData {
data: ApplicationHealthReportDetail[];
summary: ApplicationHealthReportSummary;
}
export interface SaveRiskInsightsReportRequest {
data: RiskInsightsReport;
}
export interface SaveRiskInsightsReportResponse {
id: string;
}
export interface GetRiskInsightsReportResponse {
id: string;
organizationId: OrganizationId;
date: string;
reportData: EncString;
reportKey: EncString;
}
export type PasswordHealthReportApplicationId = Opaque<string, "PasswordHealthReportApplicationId">;

View File

@@ -0,0 +1,162 @@
import { Opaque } from "type-fest";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { BadgeVariant } from "@bitwarden/components";
import { ExposedPasswordDetail, WeakPasswordDetail } from "./password-health";
// -------------------- Drawer and UI Models --------------------
// FIXME: update to use a const object instead of a typescript enum
// eslint-disable-next-line @bitwarden/platform/no-enums
export enum DrawerType {
None = 0,
AppAtRiskMembers = 1,
OrgAtRiskMembers = 2,
OrgAtRiskApps = 3,
}
export type DrawerDetails = {
open: boolean;
invokerId: string;
activeDrawerType: DrawerType;
atRiskMemberDetails?: AtRiskMemberDetail[];
appAtRiskMembers?: AppAtRiskMembersDialogParams | null;
atRiskAppDetails?: AtRiskApplicationDetail[] | null;
};
export type AppAtRiskMembersDialogParams = {
members: MemberDetails[];
applicationName: string;
};
// -------------------- Member Models --------------------
/**
* Member email with the number of at risk passwords
* At risk member detail that contains the email
* and the count of at risk ciphers
*/
export type AtRiskMemberDetail = {
email: string;
atRiskPasswordCount: number;
};
/**
* Flattened member details that associates an
* organization member to a cipher
*/
export type MemberDetails = {
userGuid: string;
userName: string;
email: string;
cipherId: string;
};
// -------------------- Cipher Models --------------------
export type PasswordHealthData = {
reusedPasswordCount: number;
weakPasswordDetail: WeakPasswordDetail;
exposedPasswordDetail: ExposedPasswordDetail;
};
/**
* Associates a cipher with it's essential information.
* Gets the password health details, cipher members, and
* the trimmed uris for the cipher
*/
export type CipherHealthReport = {
applications: string[];
cipherMembers: MemberDetails[];
healthData: PasswordHealthData;
cipher: CipherView;
};
/**
* Breaks the cipher health info out by uri and passes
* along the password health and member info
*/
export type CipherApplicationView = {
cipherId: string;
cipher: CipherView;
cipherMembers: MemberDetails[];
application: string;
healthData: PasswordHealthData;
};
// -------------------- Application Health Report Models --------------------
/**
* All applications report summary. The total members,
* total at risk members, application, and at risk application
* counts. Aggregated from all calculated applications
*/
export type ApplicationHealthReportSummary = {
totalMemberCount: number;
totalAtRiskMemberCount: number;
totalApplicationCount: number;
totalAtRiskApplicationCount: number;
};
export type CriticalSummaryDetails = {
totalCriticalMembersCount: number;
totalCriticalApplicationsCount: number;
};
/**
* All applications report detail. Application is the cipher
* uri. Has the at risk, password, and member information
*/
export type ApplicationHealthReportDetail = {
applicationName: string;
passwordCount: number;
atRiskPasswordCount: number;
atRiskCipherIds: string[];
memberCount: number;
atRiskMemberCount: number;
memberDetails: MemberDetails[];
atRiskMemberDetails: MemberDetails[];
cipherIds: string[];
};
export type ApplicationHealthReportDetailEnriched = ApplicationHealthReportDetail & {
isMarkedAsCritical: boolean;
ciphers: CipherView[];
};
/*
* A list of applications and the count of
* at risk passwords for each application
*/
export type AtRiskApplicationDetail = {
applicationName: string;
atRiskPasswordCount: number;
};
// -------------------- Password Health Report Models --------------------
export type PasswordHealthReportApplicationId = Opaque<string, "PasswordHealthReportApplicationId">;
// -------------------- Risk Insights Report Models --------------------
export interface RiskInsightsReportData {
data: ApplicationHealthReportDetailEnriched[];
summary: ApplicationHealthReportSummary;
}
export interface RiskInsightsReport {
organizationId: OrganizationId;
date: string;
reportData: string;
reportKey: string;
}
export type ReportScore = { label: string; badgeVariant: BadgeVariant; sortOrder: number };
export type ReportResult = CipherView & {
score: number;
reportValue: ReportScore;
scoreKey: number;
};
export type ReportDetailsAndSummary = {
data: ApplicationHealthReportDetailEnriched[];
summary: ApplicationHealthReportSummary;
dateCreated: Date;
};

View File

@@ -5,10 +5,10 @@ import { OrganizationId } from "@bitwarden/common/types/guid";
import { import {
PasswordHealthReportApplicationDropRequest, PasswordHealthReportApplicationDropRequest,
PasswordHealthReportApplicationId,
PasswordHealthReportApplicationsRequest, PasswordHealthReportApplicationsRequest,
PasswordHealthReportApplicationsResponse, PasswordHealthReportApplicationsResponse,
} from "../models/password-health"; } from "../models/api-models.types";
import { PasswordHealthReportApplicationId } from "../models/report-models";
import { CriticalAppsApiService } from "./critical-apps-api.service"; import { CriticalAppsApiService } from "./critical-apps-api.service";

View File

@@ -7,7 +7,7 @@ import {
PasswordHealthReportApplicationDropRequest, PasswordHealthReportApplicationDropRequest,
PasswordHealthReportApplicationsRequest, PasswordHealthReportApplicationsRequest,
PasswordHealthReportApplicationsResponse, PasswordHealthReportApplicationsResponse,
} from "../models/password-health"; } from "../models/api-models.types";
export class CriticalAppsApiService { export class CriticalAppsApiService {
constructor(private apiService: ApiService) {} constructor(private apiService: ApiService) {}

View File

@@ -12,10 +12,10 @@ import { OrgKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management"; import { KeyService } from "@bitwarden/key-management";
import { import {
PasswordHealthReportApplicationId,
PasswordHealthReportApplicationsRequest, PasswordHealthReportApplicationsRequest,
PasswordHealthReportApplicationsResponse, PasswordHealthReportApplicationsResponse,
} from "../models/password-health"; } from "../models/api-models.types";
import { PasswordHealthReportApplicationId } from "../models/report-models";
import { CriticalAppsApiService } from "./critical-apps-api.service"; import { CriticalAppsApiService } from "./critical-apps-api.service";
import { CriticalAppsService } from "./critical-apps.service"; import { CriticalAppsService } from "./critical-apps.service";

View File

@@ -22,7 +22,7 @@ import { KeyService } from "@bitwarden/key-management";
import { import {
PasswordHealthReportApplicationsRequest, PasswordHealthReportApplicationsRequest,
PasswordHealthReportApplicationsResponse, PasswordHealthReportApplicationsResponse,
} from "../models/password-health"; } from "../models/api-models.types";
import { CriticalAppsApiService } from "./critical-apps-api.service"; import { CriticalAppsApiService } from "./critical-apps-api.service";

View File

@@ -4,7 +4,8 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { makeEncString } from "@bitwarden/common/spec"; import { makeEncString } from "@bitwarden/common/spec";
import { OrganizationId, OrganizationReportId } from "@bitwarden/common/types/guid"; import { OrganizationId, OrganizationReportId } from "@bitwarden/common/types/guid";
import { EncryptedDataModel, SaveRiskInsightsReportRequest } from "../models/password-health"; import { SaveRiskInsightsReportRequest } from "../models/api-models.types";
import { EncryptedDataModel } from "../models/password-health";
import { RiskInsightsApiService } from "./risk-insights-api.service"; import { RiskInsightsApiService } from "./risk-insights-api.service";
@@ -200,7 +201,9 @@ describe("RiskInsightsApiService", () => {
it("Get Applications: should call apiService.send with correct parameters and return an Observable", (done) => { it("Get Applications: should call apiService.send with correct parameters and return an Observable", (done) => {
const reportId = "report123" as OrganizationReportId; const reportId = "report123" as OrganizationReportId;
const mockResponse: EncryptedDataModel | null = { encryptedData: "abc" } as EncryptedDataModel; const mockResponse: EncryptedDataModel | null = {
encryptedData: "abc",
} as EncryptedDataModel;
mockApiService.send.mockResolvedValueOnce(mockResponse); mockApiService.send.mockResolvedValueOnce(mockResponse);

View File

@@ -4,11 +4,11 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationId, OrganizationReportId } from "@bitwarden/common/types/guid"; import { OrganizationId, OrganizationReportId } from "@bitwarden/common/types/guid";
import { import {
EncryptedDataModel,
GetRiskInsightsReportResponse, GetRiskInsightsReportResponse,
SaveRiskInsightsReportRequest, SaveRiskInsightsReportRequest,
SaveRiskInsightsReportResponse, SaveRiskInsightsReportResponse,
} from "../models/password-health"; } from "../models/api-models.types";
import { EncryptedDataModel } from "../models/password-health";
export class RiskInsightsApiService { export class RiskInsightsApiService {
constructor(private apiService: ApiService) {} constructor(private apiService: ApiService) {}

View File

@@ -5,11 +5,11 @@ import { OrganizationId } from "@bitwarden/common/types/guid";
import { import {
AppAtRiskMembersDialogParams, AppAtRiskMembersDialogParams,
ApplicationHealthReportDetail,
AtRiskApplicationDetail, AtRiskApplicationDetail,
AtRiskMemberDetail, AtRiskMemberDetail,
DrawerType, DrawerType,
} from "../models/password-health"; ApplicationHealthReportDetail,
} from "../models/report-models";
import { RiskInsightsReportService } from "./risk-insights-report.service"; import { RiskInsightsReportService } from "./risk-insights-report.service";
export class RiskInsightsDataService { export class RiskInsightsDataService {

View File

@@ -8,7 +8,7 @@ import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/pass
import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { GetRiskInsightsReportResponse } from "../models/password-health"; import { GetRiskInsightsReportResponse } from "../models/api-models.types";
import { mockCiphers } from "./ciphers.mock"; import { mockCiphers } from "./ciphers.mock";
import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service"; import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";
@@ -197,7 +197,7 @@ describe("RiskInsightsReportService", () => {
date: new Date().toISOString(), date: new Date().toISOString(),
organizationId: "orgId", organizationId: "orgId",
reportData: "encryptedReportData", reportData: "encryptedReportData",
reportKey: "encryptionKey", contentEncryptionKey: "encryptionKey",
} as GetRiskInsightsReportResponse; } as GetRiskInsightsReportResponse;
const organizationId = "orgId" as OrganizationId; const organizationId = "orgId" as OrganizationId;
@@ -227,7 +227,7 @@ describe("RiskInsightsReportService", () => {
date: new Date().toISOString(), date: new Date().toISOString(),
organizationId: organizationId as OrganizationId, organizationId: organizationId as OrganizationId,
reportData: "encryptedReportData", reportData: "encryptedReportData",
reportKey: "encryptionKey", contentEncryptionKey: "encryptionKey",
} as GetRiskInsightsReportResponse; } as GetRiskInsightsReportResponse;
const decryptedReport = { const decryptedReport = {

View File

@@ -29,20 +29,22 @@ import {
getTrimmedCipherUris, getTrimmedCipherUris,
getUniqueMembers, getUniqueMembers,
} from "../helpers/risk-insights-data-mappers"; } from "../helpers/risk-insights-data-mappers";
import {
LEGACY_CipherHealthReportDetail,
LEGACY_CipherHealthReportUriDetail,
ExposedPasswordDetail,
LEGACY_MemberDetailsFlat,
WeakPasswordDetail,
WeakPasswordScore,
LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher,
} from "../models/password-health";
import { import {
ApplicationHealthReportDetail, ApplicationHealthReportDetail,
ApplicationHealthReportSummary, ApplicationHealthReportSummary,
AtRiskMemberDetail, AtRiskMemberDetail,
AtRiskApplicationDetail, AtRiskApplicationDetail,
CipherHealthReportDetail, RiskInsightsReportData,
CipherHealthReportUriDetail, } from "../models/report-models";
ExposedPasswordDetail,
MemberDetailsFlat,
WeakPasswordDetail,
WeakPasswordScore,
ApplicationHealthReportDetailWithCriticalFlagAndCipher,
ReportInsightsReportData,
} from "../models/password-health";
import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service"; import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";
import { RiskInsightsApiService } from "./risk-insights-api.service"; import { RiskInsightsApiService } from "./risk-insights-api.service";
@@ -76,7 +78,9 @@ export class RiskInsightsReportService {
* @param organizationId * @param organizationId
* @returns Cipher health report data with members and trimmed uris * @returns Cipher health report data with members and trimmed uris
*/ */
generateRawDataReport$(organizationId: OrganizationId): Observable<CipherHealthReportDetail[]> { generateRawDataReport$(
organizationId: OrganizationId,
): Observable<LEGACY_CipherHealthReportDetail[]> {
const allCiphers$ = from(this.cipherService.getAllFromApiForOrganization(organizationId)); const allCiphers$ = from(this.cipherService.getAllFromApiForOrganization(organizationId));
const memberCiphers$ = from( const memberCiphers$ = from(
this.memberCipherDetailsApiService.getMemberCipherDetails(organizationId), this.memberCipherDetailsApiService.getMemberCipherDetails(organizationId),
@@ -84,7 +88,7 @@ export class RiskInsightsReportService {
const results$ = zip(allCiphers$, memberCiphers$).pipe( const results$ = zip(allCiphers$, memberCiphers$).pipe(
map(([allCiphers, memberCiphers]) => { map(([allCiphers, memberCiphers]) => {
const details: MemberDetailsFlat[] = memberCiphers.flatMap((dtl) => const details: LEGACY_MemberDetailsFlat[] = memberCiphers.flatMap((dtl) =>
dtl.cipherIds.map((c) => getMemberDetailsFlat(dtl.userGuid, dtl.userName, dtl.email, c)), dtl.cipherIds.map((c) => getMemberDetailsFlat(dtl.userGuid, dtl.userName, dtl.email, c)),
); );
return [allCiphers, details] as const; return [allCiphers, details] as const;
@@ -104,7 +108,7 @@ export class RiskInsightsReportService {
*/ */
generateRawDataUriReport$( generateRawDataUriReport$(
organizationId: OrganizationId, organizationId: OrganizationId,
): Observable<CipherHealthReportUriDetail[]> { ): Observable<LEGACY_CipherHealthReportUriDetail[]> {
const cipherHealthDetails$ = this.generateRawDataReport$(organizationId); const cipherHealthDetails$ = this.generateRawDataReport$(organizationId);
const results$ = cipherHealthDetails$.pipe( const results$ = cipherHealthDetails$.pipe(
map((healthDetails) => this.getCipherUriDetails(healthDetails)), map((healthDetails) => this.getCipherUriDetails(healthDetails)),
@@ -206,7 +210,7 @@ export class RiskInsightsReportService {
async identifyCiphers( async identifyCiphers(
data: ApplicationHealthReportDetail[], data: ApplicationHealthReportDetail[],
organizationId: OrganizationId, organizationId: OrganizationId,
): Promise<ApplicationHealthReportDetailWithCriticalFlagAndCipher[]> { ): Promise<LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher[]> {
const cipherViews = await this.cipherService.getAllFromApiForOrganization(organizationId); const cipherViews = await this.cipherService.getAllFromApiForOrganization(organizationId);
const dataWithCiphers = data.map( const dataWithCiphers = data.map(
@@ -214,7 +218,7 @@ export class RiskInsightsReportService {
({ ({
...app, ...app,
ciphers: cipherViews.filter((c) => app.cipherIds.some((a) => a === c.id)), ciphers: cipherViews.filter((c) => app.cipherIds.some((a) => a === c.id)),
}) as ApplicationHealthReportDetailWithCriticalFlagAndCipher, }) as LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher,
); );
return dataWithCiphers; return dataWithCiphers;
} }
@@ -226,7 +230,7 @@ export class RiskInsightsReportService {
switchMap((response) => { switchMap((response) => {
if (!response) { if (!response) {
// Return an empty report and summary if response is falsy // Return an empty report and summary if response is falsy
return of<ReportInsightsReportData>({ return of<RiskInsightsReportData>({
data: [], data: [],
summary: { summary: {
totalMemberCount: 0, totalMemberCount: 0,
@@ -237,12 +241,12 @@ export class RiskInsightsReportService {
}); });
} }
return from( return from(
this.riskInsightsEncryptionService.decryptRiskInsightsReport<ReportInsightsReportData>( this.riskInsightsEncryptionService.decryptRiskInsightsReport<RiskInsightsReportData>(
organizationId, organizationId,
userId, userId,
new EncString(response.reportData), new EncString(response.reportData),
new EncString(response.reportKey), new EncString(response.contentEncryptionKey),
(data) => data as ReportInsightsReportData, (data) => data as RiskInsightsReportData,
), ),
); );
}), }),
@@ -297,9 +301,9 @@ export class RiskInsightsReportService {
*/ */
private async getCipherDetails( private async getCipherDetails(
ciphers: CipherView[], ciphers: CipherView[],
memberDetails: MemberDetailsFlat[], memberDetails: LEGACY_MemberDetailsFlat[],
): Promise<CipherHealthReportDetail[]> { ): Promise<LEGACY_CipherHealthReportDetail[]> {
const cipherHealthReports: CipherHealthReportDetail[] = []; const cipherHealthReports: LEGACY_CipherHealthReportDetail[] = [];
const passwordUseMap = new Map<string, number>(); const passwordUseMap = new Map<string, number>();
const exposedDetails = await this.findExposedPasswords(ciphers); const exposedDetails = await this.findExposedPasswords(ciphers);
for (const cipher of ciphers) { for (const cipher of ciphers) {
@@ -329,7 +333,7 @@ export class RiskInsightsReportService {
exposedPasswordDetail: exposedPassword, exposedPasswordDetail: exposedPassword,
cipherMembers: cipherMembers, cipherMembers: cipherMembers,
trimmedUris: cipherTrimmedUris, trimmedUris: cipherTrimmedUris,
} as CipherHealthReportDetail; } as LEGACY_CipherHealthReportDetail;
cipherHealthReports.push(cipherHealth); cipherHealthReports.push(cipherHealth);
} }
@@ -348,8 +352,8 @@ export class RiskInsightsReportService {
* @returns Flattened cipher health details to uri * @returns Flattened cipher health details to uri
*/ */
private getCipherUriDetails( private getCipherUriDetails(
cipherHealthReport: CipherHealthReportDetail[], cipherHealthReport: LEGACY_CipherHealthReportDetail[],
): CipherHealthReportUriDetail[] { ): LEGACY_CipherHealthReportUriDetail[] {
return cipherHealthReport.flatMap((rpt) => return cipherHealthReport.flatMap((rpt) =>
rpt.trimmedUris.map((u) => getFlattenedCipherDetails(rpt, u)), rpt.trimmedUris.map((u) => getFlattenedCipherDetails(rpt, u)),
); );
@@ -362,7 +366,7 @@ export class RiskInsightsReportService {
* @returns Application health reports * @returns Application health reports
*/ */
private getApplicationHealthReport( private getApplicationHealthReport(
cipherHealthUriReport: CipherHealthReportUriDetail[], cipherHealthUriReport: LEGACY_CipherHealthReportUriDetail[],
): ApplicationHealthReportDetail[] { ): ApplicationHealthReportDetail[] {
const appReports: ApplicationHealthReportDetail[] = []; const appReports: ApplicationHealthReportDetail[] = [];
cipherHealthUriReport.forEach((uri) => { cipherHealthUriReport.forEach((uri) => {

View File

@@ -11,11 +11,13 @@ import {
RiskInsightsReportService, RiskInsightsReportService,
} from "@bitwarden/bit-common/dirt/reports/risk-insights"; } from "@bitwarden/bit-common/dirt/reports/risk-insights";
import { import {
ApplicationHealthReportDetail, LEGACY_ApplicationHealthReportDetailWithCriticalFlag,
ApplicationHealthReportDetailWithCriticalFlag, LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher,
ApplicationHealthReportDetailWithCriticalFlagAndCipher,
ApplicationHealthReportSummary,
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health"; } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
import {
ApplicationHealthReportDetail,
ApplicationHealthReportSummary,
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models";
import { RiskInsightsEncryptionService } from "@bitwarden/bit-common/dirt/reports/risk-insights/services/risk-insights-encryption.service"; import { RiskInsightsEncryptionService } from "@bitwarden/bit-common/dirt/reports/risk-insights/services/risk-insights-encryption.service";
import { import {
getOrganizationById, getOrganizationById,
@@ -60,7 +62,7 @@ import { ApplicationsLoadingComponent } from "./risk-insights-loading.component"
}) })
export class AllApplicationsComponent implements OnInit { export class AllApplicationsComponent implements OnInit {
protected dataSource = protected dataSource =
new TableDataSource<ApplicationHealthReportDetailWithCriticalFlagAndCipher>(); new TableDataSource<LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher>();
protected selectedUrls: Set<string> = new Set<string>(); protected selectedUrls: Set<string> = new Set<string>();
protected searchControl = new FormControl("", { nonNullable: true }); protected searchControl = new FormControl("", { nonNullable: true });
protected loading = true; protected loading = true;
@@ -99,7 +101,7 @@ export class AllApplicationsComponent implements OnInit {
const data = applications?.map((app) => ({ const data = applications?.map((app) => ({
...app, ...app,
isMarkedAsCritical: criticalUrls.includes(app.applicationName), isMarkedAsCritical: criticalUrls.includes(app.applicationName),
})) as ApplicationHealthReportDetailWithCriticalFlag[]; })) as LEGACY_ApplicationHealthReportDetailWithCriticalFlag[];
return { data, organization }; return { data, organization };
} }

View File

@@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common";
import { Component, Input } from "@angular/core"; import { Component, Input } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ApplicationHealthReportDetailWithCriticalFlagAndCipher } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health"; import { LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
import { MenuModule, TableDataSource, TableModule } from "@bitwarden/components"; import { MenuModule, TableDataSource, TableModule } from "@bitwarden/components";
import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { SharedModule } 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";
@@ -13,7 +13,8 @@ import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pip
templateUrl: "./app-table-row-scrollable.component.html", templateUrl: "./app-table-row-scrollable.component.html",
}) })
export class AppTableRowScrollableComponent { export class AppTableRowScrollableComponent {
@Input() dataSource!: TableDataSource<ApplicationHealthReportDetailWithCriticalFlagAndCipher>; @Input()
dataSource!: TableDataSource<LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher>;
@Input() showRowMenuForCriticalApps: boolean = false; @Input() showRowMenuForCriticalApps: boolean = false;
@Input() showRowCheckBox: boolean = false; @Input() showRowCheckBox: boolean = false;
@Input() selectedUrls: Set<string> = new Set<string>(); @Input() selectedUrls: Set<string> = new Set<string>();

View File

@@ -13,10 +13,10 @@ import {
RiskInsightsReportService, RiskInsightsReportService,
} from "@bitwarden/bit-common/dirt/reports/risk-insights"; } from "@bitwarden/bit-common/dirt/reports/risk-insights";
import { import {
ApplicationHealthReportDetailWithCriticalFlag, LEGACY_ApplicationHealthReportDetailWithCriticalFlag,
ApplicationHealthReportDetailWithCriticalFlagAndCipher, LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher,
ApplicationHealthReportSummary,
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health"; } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
import { ApplicationHealthReportSummary } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -51,7 +51,7 @@ import { RiskInsightsTabType } from "./risk-insights.component";
}) })
export class CriticalApplicationsComponent implements OnInit { export class CriticalApplicationsComponent implements OnInit {
protected dataSource = protected dataSource =
new TableDataSource<ApplicationHealthReportDetailWithCriticalFlagAndCipher>(); new TableDataSource<LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher>();
protected selectedIds: Set<number> = new Set<number>(); protected selectedIds: Set<number> = new Set<number>();
protected searchControl = new FormControl("", { nonNullable: true }); protected searchControl = new FormControl("", { nonNullable: true });
private destroyRef = inject(DestroyRef); private destroyRef = inject(DestroyRef);
@@ -79,7 +79,7 @@ export class CriticalApplicationsComponent implements OnInit {
const data = applications?.map((app) => ({ const data = applications?.map((app) => ({
...app, ...app,
isMarkedAsCritical: criticalUrls.includes(app.applicationName), isMarkedAsCritical: criticalUrls.includes(app.applicationName),
})) as ApplicationHealthReportDetailWithCriticalFlag[]; })) as LEGACY_ApplicationHealthReportDetailWithCriticalFlag[];
return data?.filter((app) => app.isMarkedAsCritical); return data?.filter((app) => app.isMarkedAsCritical);
}), }),
switchMap(async (data) => { switchMap(async (data) => {
@@ -200,7 +200,7 @@ export class CriticalApplicationsComponent implements OnInit {
this.dataService.setDrawerForOrgAtRiskApps(data, invokerId); this.dataService.setDrawerForOrgAtRiskApps(data, invokerId);
}; };
trackByFunction(_: number, item: ApplicationHealthReportDetailWithCriticalFlag) { trackByFunction(_: number, item: LEGACY_ApplicationHealthReportDetailWithCriticalFlag) {
return item.applicationName; return item.applicationName;
} }
isDrawerOpenForTableRow = (applicationName: string) => { isDrawerOpenForTableRow = (applicationName: string) => {

View File

@@ -10,11 +10,11 @@ import {
CriticalAppsService, CriticalAppsService,
RiskInsightsDataService, RiskInsightsDataService,
} from "@bitwarden/bit-common/dirt/reports/risk-insights"; } from "@bitwarden/bit-common/dirt/reports/risk-insights";
import { PasswordHealthReportApplicationsResponse } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/api-models.types";
import { import {
ApplicationHealthReportDetail, ApplicationHealthReportDetail,
DrawerType, DrawerType,
PasswordHealthReportApplicationsResponse, } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models";
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";