mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
Introduce a stricter use of the OrganizationId type on org-vault exports (#15836)
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
68d7cb4846
commit
b091719748
@@ -1,15 +1,17 @@
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { ExportedVaultAsString } from "../types";
|
||||
|
||||
import { ExportFormat } from "./vault-export.service.abstraction";
|
||||
|
||||
export abstract class OrganizationVaultExportServiceAbstraction {
|
||||
abstract getPasswordProtectedExport: (
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
password: string,
|
||||
onlyManagedCollections: boolean,
|
||||
) => Promise<ExportedVaultAsString>;
|
||||
abstract getOrganizationExport: (
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
format: ExportFormat,
|
||||
onlyManagedCollections: boolean,
|
||||
) => Promise<ExportedVaultAsString>;
|
||||
|
||||
@@ -65,7 +65,7 @@ export class OrganizationVaultExportService
|
||||
* @returns The exported vault
|
||||
*/
|
||||
async getPasswordProtectedExport(
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
password: string,
|
||||
onlyManagedCollections: boolean,
|
||||
): Promise<ExportedVaultAsString> {
|
||||
@@ -94,7 +94,7 @@ export class OrganizationVaultExportService
|
||||
* @throws Error if the organization policies prevent the export
|
||||
*/
|
||||
async getOrganizationExport(
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
format: ExportFormat = "csv",
|
||||
onlyManagedCollections: boolean,
|
||||
): Promise<ExportedVaultAsString> {
|
||||
@@ -128,7 +128,7 @@ export class OrganizationVaultExportService
|
||||
|
||||
private async getOrganizationDecryptedExport(
|
||||
activeUserId: UserId,
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
format: "json" | "csv",
|
||||
): Promise<string> {
|
||||
const decCollections: CollectionView[] = [];
|
||||
@@ -138,49 +138,42 @@ export class OrganizationVaultExportService
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
promises.push(
|
||||
this.vaultExportApiService
|
||||
.getOrganizationExport(organizationId as OrganizationId)
|
||||
.then((exportData) => {
|
||||
const exportPromises: Promise<void>[] = [];
|
||||
if (exportData != null) {
|
||||
if (exportData.collections != null && exportData.collections.length > 0) {
|
||||
exportData.collections.forEach((c) => {
|
||||
const collection = Collection.fromCollectionData(
|
||||
new CollectionData(c as CollectionDetailsResponse),
|
||||
);
|
||||
this.vaultExportApiService.getOrganizationExport(organizationId).then((exportData) => {
|
||||
const exportPromises: Promise<void>[] = [];
|
||||
if (exportData != null) {
|
||||
if (exportData.collections != null && exportData.collections.length > 0) {
|
||||
exportData.collections.forEach((c) => {
|
||||
const collection = Collection.fromCollectionData(
|
||||
new CollectionData(c as CollectionDetailsResponse),
|
||||
);
|
||||
exportPromises.push(
|
||||
firstValueFrom(this.keyService.activeUserOrgKeys$)
|
||||
.then((keys) => collection.decrypt(keys[organizationId], this.encryptService))
|
||||
.then((decCol) => {
|
||||
decCollections.push(decCol);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
if (exportData.ciphers != null && exportData.ciphers.length > 0) {
|
||||
exportData.ciphers
|
||||
.filter((c) => c.deletedDate === null)
|
||||
.forEach(async (c) => {
|
||||
const cipher = new Cipher(new CipherData(c));
|
||||
exportPromises.push(
|
||||
firstValueFrom(this.keyService.activeUserOrgKeys$)
|
||||
.then((keys) =>
|
||||
collection.decrypt(
|
||||
keys[organizationId as OrganizationId],
|
||||
this.encryptService,
|
||||
),
|
||||
)
|
||||
.then((decCol) => {
|
||||
decCollections.push(decCol);
|
||||
}),
|
||||
this.cipherService.decrypt(cipher, activeUserId).then((decCipher) => {
|
||||
if (
|
||||
!this.restrictedItemTypesService.isCipherRestricted(decCipher, restrictions)
|
||||
) {
|
||||
decCiphers.push(decCipher);
|
||||
}
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
if (exportData.ciphers != null && exportData.ciphers.length > 0) {
|
||||
exportData.ciphers
|
||||
.filter((c) => c.deletedDate === null)
|
||||
.forEach(async (c) => {
|
||||
const cipher = new Cipher(new CipherData(c));
|
||||
exportPromises.push(
|
||||
this.cipherService.decrypt(cipher, activeUserId).then((decCipher) => {
|
||||
if (
|
||||
!this.restrictedItemTypesService.isCipherRestricted(decCipher, restrictions)
|
||||
) {
|
||||
decCiphers.push(decCipher);
|
||||
}
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.all(exportPromises);
|
||||
}),
|
||||
}
|
||||
return Promise.all(exportPromises);
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(promises);
|
||||
@@ -191,15 +184,13 @@ export class OrganizationVaultExportService
|
||||
return this.buildJsonExport(decCollections, decCiphers);
|
||||
}
|
||||
|
||||
private async getOrganizationEncryptedExport(organizationId: string): Promise<string> {
|
||||
private async getOrganizationEncryptedExport(organizationId: OrganizationId): Promise<string> {
|
||||
const collections: Collection[] = [];
|
||||
const ciphers: Cipher[] = [];
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
const exportData = await this.vaultExportApiService.getOrganizationExport(
|
||||
organizationId as OrganizationId,
|
||||
);
|
||||
const exportData = await this.vaultExportApiService.getOrganizationExport(organizationId);
|
||||
|
||||
if (exportData == null) {
|
||||
return;
|
||||
@@ -229,7 +220,7 @@ export class OrganizationVaultExportService
|
||||
|
||||
private async getDecryptedManagedExport(
|
||||
activeUserId: UserId,
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
format: "json" | "csv",
|
||||
): Promise<string> {
|
||||
let decCiphers: CipherView[] = [];
|
||||
@@ -271,7 +262,7 @@ export class OrganizationVaultExportService
|
||||
|
||||
private async getEncryptedManagedExport(
|
||||
activeUserId: UserId,
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
): Promise<string> {
|
||||
let encCiphers: Cipher[] = [];
|
||||
let allCiphers: Cipher[] = [];
|
||||
@@ -308,7 +299,7 @@ export class OrganizationVaultExportService
|
||||
}
|
||||
|
||||
private async BuildEncryptedExport(
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
collections: Collection[],
|
||||
ciphers: Cipher[],
|
||||
): Promise<string> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { ExportedVault } from "../types";
|
||||
|
||||
export const EXPORT_FORMATS = ["csv", "json", "encrypted_json", "zip"] as const;
|
||||
@@ -6,7 +8,7 @@ export type ExportFormat = (typeof EXPORT_FORMATS)[number];
|
||||
export abstract class VaultExportServiceAbstraction {
|
||||
abstract getExport: (format: ExportFormat, password: string) => Promise<ExportedVault>;
|
||||
abstract getOrganizationExport: (
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
format: ExportFormat,
|
||||
password: string,
|
||||
onlyManagedCollections?: boolean,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { ExportedVault } from "../types";
|
||||
|
||||
@@ -42,7 +43,7 @@ export class VaultExportService implements VaultExportServiceAbstraction {
|
||||
* @throws Error if the organization policies prevent the export
|
||||
*/
|
||||
async getOrganizationExport(
|
||||
organizationId: string,
|
||||
organizationId: OrganizationId,
|
||||
format: ExportFormat,
|
||||
password: string,
|
||||
onlyManagedCollections = false,
|
||||
|
||||
@@ -29,10 +29,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { PasswordStrengthV2Component } from "@bitwarden/angular/tools/password-strength/password-strength-v2.component";
|
||||
import { UserVerificationDialogComponent } from "@bitwarden/auth/angular";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import {
|
||||
getOrganizationById,
|
||||
OrganizationService,
|
||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
@@ -42,8 +39,10 @@ import { EventType } from "@bitwarden/common/enums";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { getById } from "@bitwarden/common/platform/misc";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { pin } from "@bitwarden/common/tools/rx";
|
||||
import { isId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
AsyncActionsModule,
|
||||
BitSubmitDirective,
|
||||
@@ -84,9 +83,9 @@ import { ExportScopeCalloutComponent } from "./export-scope-callout.component";
|
||||
],
|
||||
})
|
||||
export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private _organizationId: string;
|
||||
private _organizationId: OrganizationId | undefined;
|
||||
|
||||
get organizationId(): string {
|
||||
get organizationId(): OrganizationId | undefined {
|
||||
return this._organizationId;
|
||||
}
|
||||
|
||||
@@ -94,14 +93,23 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
* Enables the hosting control to pass in an organizationId
|
||||
* If a organizationId is provided, the organization selection is disabled.
|
||||
*/
|
||||
@Input() set organizationId(value: string) {
|
||||
@Input() set organizationId(value: OrganizationId | string | undefined) {
|
||||
if (Utils.isNullOrEmpty(value)) {
|
||||
this._organizationId = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isId<OrganizationId>(value)) {
|
||||
this._organizationId = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this._organizationId = value;
|
||||
|
||||
getUserId(this.accountService.activeAccount$)
|
||||
.pipe(
|
||||
switchMap((userId) =>
|
||||
this.organizationService
|
||||
.organizations$(userId)
|
||||
.pipe(getOrganizationById(this._organizationId)),
|
||||
this.organizationService.organizations$(userId).pipe(getById(this._organizationId)),
|
||||
),
|
||||
)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
@@ -133,11 +141,11 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
/**
|
||||
* Emits when the creation and download of the export-file have succeeded
|
||||
* - Emits an null/empty string when exporting from an individual vault
|
||||
* - Emits an undefined when exporting from an individual vault
|
||||
* - Emits the organizationId when exporting from an organizationl vault
|
||||
* */
|
||||
@Output()
|
||||
onSuccessfulExport = new EventEmitter<string>();
|
||||
onSuccessfulExport = new EventEmitter<OrganizationId | undefined>();
|
||||
|
||||
@ViewChild(PasswordStrengthV2Component) passwordStrengthComponent: PasswordStrengthV2Component;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user