[EC-320] Add organization vault export to event logs (#3136)
* Added organizationId to EventData and EventRequest * Added EventType Organization_ClientExportedVault * Sending organizationId on Organization Export event * Checking that the user belongs to the organization * Added organizationExportResponse model * Added API method to get Organization vault export data * Updated getOrganizationDecryptedExport to use new API method
This commit is contained in:
parent
0f44789d0f
commit
b50de43556
|
@ -11,6 +11,7 @@ import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
|
import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||||
|
|
||||||
import { ExportComponent } from "../../../tools/import-export/export.component";
|
import { ExportComponent } from "../../../tools/import-export/export.component";
|
||||||
|
|
||||||
|
@ -66,7 +67,11 @@ export class OrganizationExportComponent extends ExportComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
async collectEvent(): Promise<any> {
|
async collectEvent(): Promise<any> {
|
||||||
// TODO
|
await this.eventService.collect(
|
||||||
// await this.eventService.collect(EventType.Organization_ClientExportedVault);
|
EventType.Organization_ClientExportedVault,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
this.organizationId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,11 +301,9 @@ export class EventService {
|
||||||
case EventType.Organization_PurgedVault:
|
case EventType.Organization_PurgedVault:
|
||||||
msg = humanReadableMsg = this.i18nService.t("purgedOrganizationVault");
|
msg = humanReadableMsg = this.i18nService.t("purgedOrganizationVault");
|
||||||
break;
|
break;
|
||||||
/*
|
|
||||||
case EventType.Organization_ClientExportedVault:
|
case EventType.Organization_ClientExportedVault:
|
||||||
msg = this.i18nService.t('exportedOrganizationVault');
|
msg = humanReadableMsg = this.i18nService.t("exportedOrganizationVault");
|
||||||
break;
|
break;
|
||||||
*/
|
|
||||||
case EventType.Organization_VaultAccessed:
|
case EventType.Organization_VaultAccessed:
|
||||||
msg = humanReadableMsg = this.i18nService.t("vaultAccessedByProvider");
|
msg = humanReadableMsg = this.i18nService.t("vaultAccessedByProvider");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -130,6 +130,7 @@ import {
|
||||||
OrganizationConnectionConfigApis,
|
OrganizationConnectionConfigApis,
|
||||||
OrganizationConnectionResponse,
|
OrganizationConnectionResponse,
|
||||||
} from "../models/response/organizationConnectionResponse";
|
} from "../models/response/organizationConnectionResponse";
|
||||||
|
import { OrganizationExportResponse } from "../models/response/organizationExportResponse";
|
||||||
import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse";
|
import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse";
|
||||||
import { OrganizationResponse } from "../models/response/organizationResponse";
|
import { OrganizationResponse } from "../models/response/organizationResponse";
|
||||||
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organizationSponsorshipSyncStatusResponse";
|
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organizationSponsorshipSyncStatusResponse";
|
||||||
|
@ -734,4 +735,5 @@ export abstract class ApiService {
|
||||||
request: KeyConnectorUserKeyRequest
|
request: KeyConnectorUserKeyRequest
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
getKeyConnectorAlive: (keyConnectorUrl: string) => Promise<void>;
|
getKeyConnectorAlive: (keyConnectorUrl: string) => Promise<void>;
|
||||||
|
getOrganizationExport: (organizationId: string) => Promise<OrganizationExportResponse>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { EventType } from "../enums/eventType";
|
import { EventType } from "../enums/eventType";
|
||||||
|
|
||||||
export abstract class EventService {
|
export abstract class EventService {
|
||||||
collect: (eventType: EventType, cipherId?: string, uploadImmediately?: boolean) => Promise<any>;
|
collect: (
|
||||||
|
eventType: EventType,
|
||||||
|
cipherId?: string,
|
||||||
|
uploadImmediately?: boolean,
|
||||||
|
organizationId?: string
|
||||||
|
) => Promise<any>;
|
||||||
uploadEvents: (userId?: string) => Promise<any>;
|
uploadEvents: (userId?: string) => Promise<any>;
|
||||||
clearEvents: (userId?: string) => Promise<any>;
|
clearEvents: (userId?: string) => Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ export enum EventType {
|
||||||
|
|
||||||
Organization_Updated = 1600,
|
Organization_Updated = 1600,
|
||||||
Organization_PurgedVault = 1601,
|
Organization_PurgedVault = 1601,
|
||||||
// Organization_ClientExportedVault = 1602,
|
Organization_ClientExportedVault = 1602,
|
||||||
Organization_VaultAccessed = 1603,
|
Organization_VaultAccessed = 1603,
|
||||||
Organization_EnabledSso = 1604,
|
Organization_EnabledSso = 1604,
|
||||||
Organization_DisabledSso = 1605,
|
Organization_DisabledSso = 1605,
|
||||||
|
|
|
@ -4,4 +4,5 @@ export class EventData {
|
||||||
type: EventType;
|
type: EventType;
|
||||||
cipherId: string;
|
cipherId: string;
|
||||||
date: string;
|
date: string;
|
||||||
|
organizationId: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,5 @@ export class EventRequest {
|
||||||
type: EventType;
|
type: EventType;
|
||||||
cipherId: string;
|
cipherId: string;
|
||||||
date: string;
|
date: string;
|
||||||
|
organizationId: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { BaseResponse } from "./baseResponse";
|
||||||
|
import { CipherResponse } from "./cipherResponse";
|
||||||
|
import { CollectionResponse } from "./collectionResponse";
|
||||||
|
import { ListResponse } from "./listResponse";
|
||||||
|
|
||||||
|
export class OrganizationExportResponse extends BaseResponse {
|
||||||
|
collections: ListResponse<CollectionResponse>;
|
||||||
|
ciphers: ListResponse<CipherResponse>;
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
this.collections = this.getResponseProperty("Collections");
|
||||||
|
this.ciphers = this.getResponseProperty("Ciphers");
|
||||||
|
}
|
||||||
|
}
|
|
@ -139,6 +139,7 @@ import {
|
||||||
OrganizationConnectionConfigApis,
|
OrganizationConnectionConfigApis,
|
||||||
OrganizationConnectionResponse,
|
OrganizationConnectionResponse,
|
||||||
} from "../models/response/organizationConnectionResponse";
|
} from "../models/response/organizationConnectionResponse";
|
||||||
|
import { OrganizationExportResponse } from "../models/response/organizationExportResponse";
|
||||||
import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse";
|
import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse";
|
||||||
import { OrganizationResponse } from "../models/response/organizationResponse";
|
import { OrganizationResponse } from "../models/response/organizationResponse";
|
||||||
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organizationSponsorshipSyncStatusResponse";
|
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organizationSponsorshipSyncStatusResponse";
|
||||||
|
@ -2323,6 +2324,17 @@ export class ApiService implements ApiServiceAbstraction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getOrganizationExport(organizationId: string): Promise<OrganizationExportResponse> {
|
||||||
|
const r = await this.send(
|
||||||
|
"GET",
|
||||||
|
"/organizations/" + organizationId + "/export",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
return new OrganizationExportResponse(r);
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
async getActiveBearerToken(): Promise<string> {
|
async getActiveBearerToken(): Promise<string> {
|
||||||
|
|
|
@ -34,7 +34,8 @@ export class EventService implements EventServiceAbstraction {
|
||||||
async collect(
|
async collect(
|
||||||
eventType: EventType,
|
eventType: EventType,
|
||||||
cipherId: string = null,
|
cipherId: string = null,
|
||||||
uploadImmediately = false
|
uploadImmediately = false,
|
||||||
|
organizationId: string = null
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const authed = await this.stateService.getIsAuthenticated();
|
const authed = await this.stateService.getIsAuthenticated();
|
||||||
if (!authed) {
|
if (!authed) {
|
||||||
|
@ -54,6 +55,11 @@ export class EventService implements EventServiceAbstraction {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (organizationId != null) {
|
||||||
|
if (!orgIds.has(organizationId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let eventCollection = await this.stateService.getEventCollection();
|
let eventCollection = await this.stateService.getEventCollection();
|
||||||
if (eventCollection == null) {
|
if (eventCollection == null) {
|
||||||
eventCollection = [];
|
eventCollection = [];
|
||||||
|
@ -62,6 +68,7 @@ export class EventService implements EventServiceAbstraction {
|
||||||
event.type = eventType;
|
event.type = eventType;
|
||||||
event.cipherId = cipherId;
|
event.cipherId = cipherId;
|
||||||
event.date = new Date().toISOString();
|
event.date = new Date().toISOString();
|
||||||
|
event.organizationId = organizationId;
|
||||||
eventCollection.push(event);
|
eventCollection.push(event);
|
||||||
await this.stateService.setEventCollection(eventCollection);
|
await this.stateService.setEventCollection(eventCollection);
|
||||||
if (uploadImmediately) {
|
if (uploadImmediately) {
|
||||||
|
@ -83,6 +90,7 @@ export class EventService implements EventServiceAbstraction {
|
||||||
req.type = e.type;
|
req.type = e.type;
|
||||||
req.cipherId = e.cipherId;
|
req.cipherId = e.cipherId;
|
||||||
req.date = e.date;
|
req.date = e.date;
|
||||||
|
req.organizationId = e.organizationId;
|
||||||
return req;
|
return req;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -245,38 +245,41 @@ export class ExportService implements ExportServiceAbstraction {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
this.apiService.getCollections(organizationId).then((collections) => {
|
this.apiService.getOrganizationExport(organizationId).then((exportData) => {
|
||||||
const collectionPromises: any = [];
|
const exportPromises: any = [];
|
||||||
if (collections != null && collections.data != null && collections.data.length > 0) {
|
if (exportData != null) {
|
||||||
collections.data.forEach((c) => {
|
if (
|
||||||
|
exportData.collections != null &&
|
||||||
|
exportData.collections.data != null &&
|
||||||
|
exportData.collections.data.length > 0
|
||||||
|
) {
|
||||||
|
exportData.collections.data.forEach((c) => {
|
||||||
const collection = new Collection(new CollectionData(c as CollectionDetailsResponse));
|
const collection = new Collection(new CollectionData(c as CollectionDetailsResponse));
|
||||||
collectionPromises.push(
|
exportPromises.push(
|
||||||
collection.decrypt().then((decCol) => {
|
collection.decrypt().then((decCol) => {
|
||||||
decCollections.push(decCol);
|
decCollections.push(decCol);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.all(collectionPromises);
|
if (
|
||||||
})
|
exportData.ciphers != null &&
|
||||||
);
|
exportData.ciphers.data != null &&
|
||||||
|
exportData.ciphers.data.length > 0
|
||||||
promises.push(
|
) {
|
||||||
this.apiService.getCiphersOrganization(organizationId).then((ciphers) => {
|
exportData.ciphers.data
|
||||||
const cipherPromises: any = [];
|
|
||||||
if (ciphers != null && ciphers.data != null && ciphers.data.length > 0) {
|
|
||||||
ciphers.data
|
|
||||||
.filter((c) => c.deletedDate === null)
|
.filter((c) => c.deletedDate === null)
|
||||||
.forEach((c) => {
|
.forEach((c) => {
|
||||||
const cipher = new Cipher(new CipherData(c));
|
const cipher = new Cipher(new CipherData(c));
|
||||||
cipherPromises.push(
|
exportPromises.push(
|
||||||
cipher.decrypt().then((decCipher) => {
|
cipher.decrypt().then((decCipher) => {
|
||||||
decCiphers.push(decCipher);
|
decCiphers.push(decCipher);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.all(cipherPromises);
|
}
|
||||||
|
return Promise.all(exportPromises);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue