diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-application-data-update.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-application-data-update.api.ts new file mode 100644 index 000000000000..3ed8a58a4948 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-application-data-update.api.ts @@ -0,0 +1,6 @@ +/** + * Request body for PATCH /reports/organizations/{organizationId}/data/application/{reportId} + */ +export class AccessReportApplicationDataUpdateApi { + applicationData?: string; +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-create.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-create.api.ts new file mode 100644 index 000000000000..4a701645791a --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-create.api.ts @@ -0,0 +1,17 @@ +import { AccessReportMetricsApi } from "./access-report-metrics.api"; + +/** + * Request body for POST /reports/organizations/{organizationId} + * + * When the Access Intelligence V2 feature flag is enabled, `fileSize` is required + * and `reportData`/`summaryData`/`applicationData` are omitted in favour of an + * uploaded file. Otherwise the encrypted blobs are sent inline. + */ +export class AccessReportCreateApi { + reportData?: string; + contentEncryptionKey?: string; + summaryData?: string; + applicationData?: string; + metrics?: AccessReportMetricsApi; + fileSize?: number; +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-file.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-file.api.ts new file mode 100644 index 000000000000..9c782164682e --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-file.api.ts @@ -0,0 +1,29 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; +import { FileUploadType } from "@bitwarden/common/platform/enums"; + +import { AccessReportApi } from "./access-report.api"; + +/** + * Response model returned when creating a report with the Access Intelligence V2 feature flag + * enabled. Contains a presigned upload URL for the report file along with the created report. + * + * - See {@link AccessReportApi} for the nested report response model + */ +export class AccessReportFileApi extends BaseResponse { + reportFileUploadUrl: string = ""; + reportResponse: AccessReportApi = new AccessReportApi(); + fileUploadType: FileUploadType = FileUploadType.Direct; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + + this.reportFileUploadUrl = this.getResponseProperty("reportFileUploadUrl") ?? ""; + const reportResponse = this.getResponseProperty("reportResponse"); + this.reportResponse = + reportResponse != null ? new AccessReportApi(reportResponse) : new AccessReportApi(); + this.fileUploadType = this.getResponseProperty("fileUploadType") ?? FileUploadType.Direct; + } +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary-update.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary-update.api.ts new file mode 100644 index 000000000000..4ffe643648e4 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary-update.api.ts @@ -0,0 +1,9 @@ +import { AccessReportMetricsApi } from "./access-report-metrics.api"; + +/** + * Request body for PATCH /reports/organizations/{organizationId}/data/summary/{reportId} + */ +export class AccessReportSummaryUpdateApi { + summaryData?: string; + metrics?: AccessReportMetricsApi; +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary.api.ts index 9d0c43764244..1eabdfe5f3f1 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary.api.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-summary.api.ts @@ -15,7 +15,6 @@ import { AccessReportSummaryView } from "../view/access-report-summary.view"; * - See {@link AccessReportSummaryView} for view model */ export class AccessReportSummaryApi extends BaseResponse { - organizationId: string = ""; encryptedData: string = ""; encryptionKey: string = ""; date: string = ""; @@ -23,7 +22,6 @@ export class AccessReportSummaryApi extends BaseResponse { constructor(data: any) { super(data); - this.organizationId = this.getResponseProperty("OrganizationId") ?? ""; this.encryptedData = this.getResponseProperty("EncryptedData") ?? ""; this.encryptionKey = this.getResponseProperty("EncryptionKey") ?? ""; this.date = this.getResponseProperty("Date") ?? ""; diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-update.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-update.api.ts new file mode 100644 index 000000000000..d2317c050756 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report-update.api.ts @@ -0,0 +1,12 @@ +import { AccessReportMetricsApi } from "./access-report-metrics.api"; + +/** + * Request body for PUT /reports/organizations/{organizationId}/{reportId} + */ +export class AccessReportUpdateApi { + reportData?: string; + contentEncryptionKey?: string; + summaryData?: string; + applicationData?: string; + metrics?: AccessReportMetricsApi; +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report.api.ts index 0470f4331647..1b76e2c517b5 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report.api.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/access-report.api.ts @@ -7,6 +7,8 @@ import { AccessReport } from "../domain/access-report"; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { AccessReportView } from "../view/access-report.view"; +import { ReportFileApi } from "./report-file.api"; + /** * Converts an AccessReport API response * @@ -21,9 +23,11 @@ export class AccessReportApi extends BaseResponse { reports: string = ""; applications: string = ""; summary: string = ""; - memberRegistry: string = ""; creationDate: string = ""; + revisionDate: string = ""; contentEncryptionKey: string = ""; + reportFile: ReportFileApi | undefined; + reportFileDownloadUrl: string = ""; constructor(data: any = null) { super(data); @@ -34,11 +38,14 @@ export class AccessReportApi extends BaseResponse { this.id = this.getResponseProperty("id"); this.organizationId = this.getResponseProperty("organizationId"); this.creationDate = this.getResponseProperty("creationDate"); + this.revisionDate = this.getResponseProperty("revisionDate"); this.reports = this.getResponseProperty("reportData"); this.applications = this.getResponseProperty("applicationData"); this.summary = this.getResponseProperty("summaryData"); - this.memberRegistry = this.getResponseProperty("memberRegistry") ?? ""; this.contentEncryptionKey = this.getResponseProperty("contentEncryptionKey"); + const reportFile = this.getResponseProperty("reportFile"); + this.reportFile = reportFile != null ? new ReportFileApi(reportFile) : undefined; + this.reportFileDownloadUrl = this.getResponseProperty("reportFileDownloadUrl"); // Use when individual values are encrypted // const summary = this.getResponseProperty("summaryData"); diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/report-file.api.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/report-file.api.ts new file mode 100644 index 000000000000..62e5acb122cc --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/api/report-file.api.ts @@ -0,0 +1,20 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +/** + * Metadata for an uploaded report file. + */ +export class ReportFileApi extends BaseResponse { + id: string | undefined; + fileName: string = ""; + /** File size in bytes. Serialized as a string by the server. */ + size: number = 0; + validated: boolean = false; + + constructor(data: any) { + super(data); + this.id = this.getResponseProperty("id") ?? undefined; + this.fileName = this.getResponseProperty("fileName") ?? ""; + this.size = Number(this.getResponseProperty("size") ?? 0); + this.validated = this.getResponseProperty("validated") ?? false; + } +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report-summary.data.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report-summary.data.ts index 8131d5926266..18dc46f7c2fd 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report-summary.data.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report-summary.data.ts @@ -11,7 +11,6 @@ import { AccessReportSummaryView } from "../view/access-report-summary.view"; * * - See {@link AccessReportSummaryView} from View Model */ export class AccessReportSummaryData { - organizationId?: string; encryptedData?: string; encryptionKey?: string; date?: string; @@ -21,7 +20,6 @@ export class AccessReportSummaryData { return; } - this.organizationId = data.organizationId; this.encryptedData = data.encryptedData; this.encryptionKey = data.encryptionKey; this.date = data.date; diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report.data.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report.data.ts index 12848cb6feb8..6dd8ec8f52ac 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report.data.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/access-report.data.ts @@ -4,6 +4,8 @@ import { AccessReport } from "../domain/access-report"; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { AccessReportView } from "../view/access-report.view"; +import { ReportFileData } from "./report-file.data"; + /** * Serializable data model for the access report * @@ -22,7 +24,11 @@ export class AccessReportData { // applications: AccessReportSettingsData[]; // Previously OrganizationReportApplication Data type // summary: AccessReportSummaryData; // Previously OrganizationReportSummary Data type creationDate: string = ""; + revisionDate: string = ""; contentEncryptionKey: string = ""; + reportFile: ReportFileData | undefined; + /** Presigned download URL — not persisted, populated from API response only. */ + reportFileDownloadUrl: string = ""; constructor(response?: AccessReportApi) { if (response == null) { @@ -35,7 +41,11 @@ export class AccessReportData { this.applications = response.applications; this.summary = response.summary; this.creationDate = response.creationDate; + this.revisionDate = response.revisionDate; this.contentEncryptionKey = response.contentEncryptionKey; + this.reportFile = + response.reportFile != null ? new ReportFileData(response.reportFile) : undefined; + this.reportFileDownloadUrl = response.reportFileDownloadUrl; // [TODO] Update types when individual values are encrypted instead of the entire object // this.summary = new AccessReportSummaryData(response.summaryData); diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/report-file.data.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/report-file.data.ts new file mode 100644 index 000000000000..83a7c35e2804 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/data/report-file.data.ts @@ -0,0 +1,22 @@ +import { ReportFileApi } from "../api/report-file.api"; + +/** + * Serializable data model for report file metadata. + */ +export class ReportFileData { + id?: string; + fileName: string = ""; + size: number = 0; + validated: boolean = false; + + constructor(response?: ReportFileApi) { + if (response == null) { + return; + } + + this.id = response.id; + this.fileName = response.fileName; + this.size = response.size; + this.validated = response.validated; + } +} diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report-summary.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report-summary.ts index dbbaa759f6ae..d71552fa8a01 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report-summary.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report-summary.ts @@ -16,7 +16,6 @@ import { AccessReportSummaryView } from "../view/access-report-summary.view"; * - See {@link AccessReportSummaryView} from View Model */ export class AccessReportSummary extends Domain { - organizationId: string = ""; encryptedData: EncString | undefined; encryptionKey: EncString | undefined; date: Date = new Date(0); @@ -27,7 +26,6 @@ export class AccessReportSummary extends Domain { return; } - this.organizationId = obj.organizationId ?? ""; this.encryptedData = obj.encryptedData ? new EncString(obj.encryptedData) : undefined; this.encryptionKey = obj.encryptionKey ? new EncString(obj.encryptionKey) : undefined; this.date = obj.date ? new Date(obj.date) : new Date(0); @@ -35,7 +33,6 @@ export class AccessReportSummary extends Domain { toData(): AccessReportSummaryData { const data = new AccessReportSummaryData(); - data.organizationId = this.organizationId; data.encryptedData = this.encryptedData?.encryptedString ?? ""; data.encryptionKey = this.encryptionKey?.encryptedString ?? ""; data.date = this.date.toISOString(); diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report.ts index 96c27dba884a..282a78189ed0 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/domain/access-report.ts @@ -13,6 +13,7 @@ import { import { AccessReportData } from "../data/access-report.data"; import { ApplicationHealthData } from "../data/application-health.data"; import { MemberRegistryEntryData } from "../data/member-registry-entry.data"; +import { ReportFileData } from "../data/report-file.data"; import { AccessReportSettingsView } from "../view/access-report-settings.view"; import { AccessReportSummaryView } from "../view/access-report-summary.view"; import { AccessReportView } from "../view/access-report.view"; @@ -26,7 +27,11 @@ export class AccessReport extends Domain { applications: EncString = new EncString(""); summary: EncString = new EncString(""); creationDate: Date; + revisionDate: Date | undefined; contentEncryptionKey?: EncString; + reportFile: ReportFileData | undefined; + /** Presigned download URL — transient, not persisted via toData(). */ + reportFileDownloadUrl: string = ""; constructor(obj?: AccessReportData) { super(); @@ -40,7 +45,10 @@ export class AccessReport extends Domain { this.applications = conditionalEncString(obj.applications) ?? new EncString(""); this.summary = conditionalEncString(obj.summary) ?? new EncString(""); this.creationDate = new Date(obj.creationDate); + this.revisionDate = obj.revisionDate ? new Date(obj.revisionDate) : undefined; this.contentEncryptionKey = conditionalEncString(obj.contentEncryptionKey); + this.reportFile = obj.reportFile; + this.reportFileDownloadUrl = obj.reportFileDownloadUrl; } /** @@ -107,7 +115,9 @@ export class AccessReport extends Domain { data.applications = this.applications.encryptedString ?? ""; data.summary = this.summary.encryptedString ?? ""; data.creationDate = this.creationDate.toISOString(); + data.revisionDate = this.revisionDate?.toISOString() ?? ""; data.contentEncryptionKey = this.contentEncryptionKey?.encryptedString ?? ""; + data.reportFile = this.reportFile; return data; } diff --git a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/index.ts b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/index.ts index 46f30be4848e..b5a0d3015628 100644 --- a/bitwarden_license/bit-common/src/dirt/access-intelligence/models/index.ts +++ b/bitwarden_license/bit-common/src/dirt/access-intelligence/models/index.ts @@ -1,9 +1,15 @@ // API layer export * from "./api/access-report.api"; +export * from "./api/access-report-create.api"; +export * from "./api/access-report-update.api"; +export * from "./api/access-report-summary-update.api"; +export * from "./api/access-report-application-data-update.api"; +export * from "./api/access-report-file.api"; export * from "./api/application-health.api"; export * from "./api/access-report-settings.api"; export * from "./api/access-report-summary.api"; export * from "./api/access-report-metrics.api"; +export * from "./api/report-file.api"; // Data layer export * from "./data/access-report.data"; @@ -12,6 +18,7 @@ export * from "./data/access-report-settings.data"; export * from "./data/access-report-summary.data"; export * from "./data/access-report-metrics.data"; export * from "./data/member-registry-entry.data"; +export * from "./data/report-file.data"; // Domain layer export * from "./domain/access-report";