mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +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,8 +1,7 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { isId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { ExportComponent } from "@bitwarden/vault-export-ui";
|
import { ExportComponent } from "@bitwarden/vault-export-ui";
|
||||||
|
|
||||||
import { HeaderModule } from "../../layouts/header/header.module";
|
import { HeaderModule } from "../../layouts/header/header.module";
|
||||||
@@ -13,18 +12,27 @@ import { SharedModule } from "../../shared";
|
|||||||
imports: [SharedModule, ExportComponent, HeaderModule],
|
imports: [SharedModule, ExportComponent, HeaderModule],
|
||||||
})
|
})
|
||||||
export class OrganizationVaultExportComponent implements OnInit {
|
export class OrganizationVaultExportComponent implements OnInit {
|
||||||
protected routeOrgId: string = null;
|
protected routeOrgId: OrganizationId | undefined = undefined;
|
||||||
protected loading = false;
|
protected loading = false;
|
||||||
protected disabled = false;
|
protected disabled = false;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute) {}
|
constructor(private route: ActivatedRoute) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.routeOrgId = this.route.snapshot.paramMap.get("organizationId");
|
const orgIdParam = this.route.snapshot.paramMap.get("organizationId");
|
||||||
|
if (orgIdParam === undefined) {
|
||||||
|
throw new Error("`organizationId` is a required route parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isId<OrganizationId>(orgIdParam)) {
|
||||||
|
throw new Error("Invalid OrganizationId provided in route parameter `organizationId`");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.routeOrgId = orgIdParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback that is called after a successful export.
|
* Callback that is called after a successful export.
|
||||||
*/
|
*/
|
||||||
protected async onSuccessfulExport(organizationId: string): Promise<void> {}
|
protected async onSuccessfulExport(organizationId: OrganizationId): Promise<void> {}
|
||||||
}
|
}
|
||||||
|
|||||||
35
libs/common/src/types/guid.spec.ts
Normal file
35
libs/common/src/types/guid.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { isId, emptyGuid, OrganizationId } from "./guid";
|
||||||
|
|
||||||
|
describe("isId tests", () => {
|
||||||
|
it("should return true for a valid guid string", () => {
|
||||||
|
// Example valid GUID
|
||||||
|
const validGuid = "12345678-1234-1234-1234-123456789abc";
|
||||||
|
expect(isId(validGuid)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an invalid guid string", () => {
|
||||||
|
// Example invalid GUID
|
||||||
|
const invalidGuid = "not-a-guid";
|
||||||
|
expect(isId(invalidGuid)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for non-string values", () => {
|
||||||
|
expect(isId(undefined)).toBe(false);
|
||||||
|
expect(isId(null)).toBe(false);
|
||||||
|
expect(isId(123)).toBe(false);
|
||||||
|
expect(isId({})).toBe(false);
|
||||||
|
expect(isId([])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for the emptyGuid constant if it is a valid guid", () => {
|
||||||
|
expect(isId(emptyGuid)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should infer type OrganizationId when using isId<OrganizationId>", () => {
|
||||||
|
const orgId: string = "12345678-1234-1234-1234-123456789abc";
|
||||||
|
if (isId<OrganizationId>(orgId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error("Type guard failed, orgId is not a valid OrganizationId");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Opaque } from "type-fest";
|
import { Opaque } from "type-fest";
|
||||||
|
|
||||||
|
import { isGuid } from "@bitwarden/guid";
|
||||||
|
|
||||||
export type Guid = Opaque<string, "Guid">;
|
export type Guid = Opaque<string, "Guid">;
|
||||||
|
|
||||||
// Convenience re-export of UserId from it's original location, any library that
|
// Convenience re-export of UserId from it's original location, any library that
|
||||||
@@ -26,3 +28,23 @@ export type OrganizationReportId = Opaque<string, "OrganizationReportId">;
|
|||||||
* A string representation of an empty guid.
|
* A string representation of an empty guid.
|
||||||
*/
|
*/
|
||||||
export const emptyGuid = "00000000-0000-0000-0000-000000000000";
|
export const emptyGuid = "00000000-0000-0000-0000-000000000000";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the provided value is a valid GUID string.
|
||||||
|
*
|
||||||
|
* @typeParam SomeGuid - The input type, defaults to `Guid`.
|
||||||
|
* @typeParam Output - The output type, resolves to `SomeGuid` if it is an opaque string, otherwise to `Guid` if `SomeGuid` is a string, or `never`.
|
||||||
|
* @param id - The value to check.
|
||||||
|
* @returns `true` if `id` is a string and a valid GUID, otherwise `false`.
|
||||||
|
*/
|
||||||
|
export function isId<
|
||||||
|
SomeGuid extends string = Guid,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
Output = SomeGuid extends Opaque<string, infer T>
|
||||||
|
? SomeGuid
|
||||||
|
: SomeGuid extends string
|
||||||
|
? Guid
|
||||||
|
: never,
|
||||||
|
>(id: unknown): id is Output {
|
||||||
|
return typeof id === "string" && isGuid(id);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { ExportedVaultAsString } from "../types";
|
import { ExportedVaultAsString } from "../types";
|
||||||
|
|
||||||
import { ExportFormat } from "./vault-export.service.abstraction";
|
import { ExportFormat } from "./vault-export.service.abstraction";
|
||||||
|
|
||||||
export abstract class OrganizationVaultExportServiceAbstraction {
|
export abstract class OrganizationVaultExportServiceAbstraction {
|
||||||
abstract getPasswordProtectedExport: (
|
abstract getPasswordProtectedExport: (
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
password: string,
|
password: string,
|
||||||
onlyManagedCollections: boolean,
|
onlyManagedCollections: boolean,
|
||||||
) => Promise<ExportedVaultAsString>;
|
) => Promise<ExportedVaultAsString>;
|
||||||
abstract getOrganizationExport: (
|
abstract getOrganizationExport: (
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
format: ExportFormat,
|
format: ExportFormat,
|
||||||
onlyManagedCollections: boolean,
|
onlyManagedCollections: boolean,
|
||||||
) => Promise<ExportedVaultAsString>;
|
) => Promise<ExportedVaultAsString>;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class OrganizationVaultExportService
|
|||||||
* @returns The exported vault
|
* @returns The exported vault
|
||||||
*/
|
*/
|
||||||
async getPasswordProtectedExport(
|
async getPasswordProtectedExport(
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
password: string,
|
password: string,
|
||||||
onlyManagedCollections: boolean,
|
onlyManagedCollections: boolean,
|
||||||
): Promise<ExportedVaultAsString> {
|
): Promise<ExportedVaultAsString> {
|
||||||
@@ -94,7 +94,7 @@ export class OrganizationVaultExportService
|
|||||||
* @throws Error if the organization policies prevent the export
|
* @throws Error if the organization policies prevent the export
|
||||||
*/
|
*/
|
||||||
async getOrganizationExport(
|
async getOrganizationExport(
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
format: ExportFormat = "csv",
|
format: ExportFormat = "csv",
|
||||||
onlyManagedCollections: boolean,
|
onlyManagedCollections: boolean,
|
||||||
): Promise<ExportedVaultAsString> {
|
): Promise<ExportedVaultAsString> {
|
||||||
@@ -128,7 +128,7 @@ export class OrganizationVaultExportService
|
|||||||
|
|
||||||
private async getOrganizationDecryptedExport(
|
private async getOrganizationDecryptedExport(
|
||||||
activeUserId: UserId,
|
activeUserId: UserId,
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
format: "json" | "csv",
|
format: "json" | "csv",
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const decCollections: CollectionView[] = [];
|
const decCollections: CollectionView[] = [];
|
||||||
@@ -138,49 +138,42 @@ export class OrganizationVaultExportService
|
|||||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
this.vaultExportApiService
|
this.vaultExportApiService.getOrganizationExport(organizationId).then((exportData) => {
|
||||||
.getOrganizationExport(organizationId as OrganizationId)
|
const exportPromises: Promise<void>[] = [];
|
||||||
.then((exportData) => {
|
if (exportData != null) {
|
||||||
const exportPromises: Promise<void>[] = [];
|
if (exportData.collections != null && exportData.collections.length > 0) {
|
||||||
if (exportData != null) {
|
exportData.collections.forEach((c) => {
|
||||||
if (exportData.collections != null && exportData.collections.length > 0) {
|
const collection = Collection.fromCollectionData(
|
||||||
exportData.collections.forEach((c) => {
|
new CollectionData(c as CollectionDetailsResponse),
|
||||||
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(
|
exportPromises.push(
|
||||||
firstValueFrom(this.keyService.activeUserOrgKeys$)
|
this.cipherService.decrypt(cipher, activeUserId).then((decCipher) => {
|
||||||
.then((keys) =>
|
if (
|
||||||
collection.decrypt(
|
!this.restrictedItemTypesService.isCipherRestricted(decCipher, restrictions)
|
||||||
keys[organizationId as OrganizationId],
|
) {
|
||||||
this.encryptService,
|
decCiphers.push(decCipher);
|
||||||
),
|
}
|
||||||
)
|
}),
|
||||||
.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(
|
|
||||||
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);
|
await Promise.all(promises);
|
||||||
@@ -191,15 +184,13 @@ export class OrganizationVaultExportService
|
|||||||
return this.buildJsonExport(decCollections, decCiphers);
|
return this.buildJsonExport(decCollections, decCiphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getOrganizationEncryptedExport(organizationId: string): Promise<string> {
|
private async getOrganizationEncryptedExport(organizationId: OrganizationId): Promise<string> {
|
||||||
const collections: Collection[] = [];
|
const collections: Collection[] = [];
|
||||||
const ciphers: Cipher[] = [];
|
const ciphers: Cipher[] = [];
|
||||||
|
|
||||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||||
|
|
||||||
const exportData = await this.vaultExportApiService.getOrganizationExport(
|
const exportData = await this.vaultExportApiService.getOrganizationExport(organizationId);
|
||||||
organizationId as OrganizationId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (exportData == null) {
|
if (exportData == null) {
|
||||||
return;
|
return;
|
||||||
@@ -229,7 +220,7 @@ export class OrganizationVaultExportService
|
|||||||
|
|
||||||
private async getDecryptedManagedExport(
|
private async getDecryptedManagedExport(
|
||||||
activeUserId: UserId,
|
activeUserId: UserId,
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
format: "json" | "csv",
|
format: "json" | "csv",
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
let decCiphers: CipherView[] = [];
|
let decCiphers: CipherView[] = [];
|
||||||
@@ -271,7 +262,7 @@ export class OrganizationVaultExportService
|
|||||||
|
|
||||||
private async getEncryptedManagedExport(
|
private async getEncryptedManagedExport(
|
||||||
activeUserId: UserId,
|
activeUserId: UserId,
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
let encCiphers: Cipher[] = [];
|
let encCiphers: Cipher[] = [];
|
||||||
let allCiphers: Cipher[] = [];
|
let allCiphers: Cipher[] = [];
|
||||||
@@ -308,7 +299,7 @@ export class OrganizationVaultExportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async BuildEncryptedExport(
|
private async BuildEncryptedExport(
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
collections: Collection[],
|
collections: Collection[],
|
||||||
ciphers: Cipher[],
|
ciphers: Cipher[],
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { ExportedVault } from "../types";
|
import { ExportedVault } from "../types";
|
||||||
|
|
||||||
export const EXPORT_FORMATS = ["csv", "json", "encrypted_json", "zip"] as const;
|
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 {
|
export abstract class VaultExportServiceAbstraction {
|
||||||
abstract getExport: (format: ExportFormat, password: string) => Promise<ExportedVault>;
|
abstract getExport: (format: ExportFormat, password: string) => Promise<ExportedVault>;
|
||||||
abstract getOrganizationExport: (
|
abstract getOrganizationExport: (
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
format: ExportFormat,
|
format: ExportFormat,
|
||||||
password: string,
|
password: string,
|
||||||
onlyManagedCollections?: boolean,
|
onlyManagedCollections?: boolean,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { ExportedVault } from "../types";
|
import { ExportedVault } from "../types";
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ export class VaultExportService implements VaultExportServiceAbstraction {
|
|||||||
* @throws Error if the organization policies prevent the export
|
* @throws Error if the organization policies prevent the export
|
||||||
*/
|
*/
|
||||||
async getOrganizationExport(
|
async getOrganizationExport(
|
||||||
organizationId: string,
|
organizationId: OrganizationId,
|
||||||
format: ExportFormat,
|
format: ExportFormat,
|
||||||
password: string,
|
password: string,
|
||||||
onlyManagedCollections = false,
|
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 { PasswordStrengthV2Component } from "@bitwarden/angular/tools/password-strength/password-strength-v2.component";
|
||||||
import { UserVerificationDialogComponent } from "@bitwarden/auth/angular";
|
import { UserVerificationDialogComponent } from "@bitwarden/auth/angular";
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import {
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
getOrganizationById,
|
|
||||||
OrganizationService,
|
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
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 { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { pin } from "@bitwarden/common/tools/rx";
|
import { pin } from "@bitwarden/common/tools/rx";
|
||||||
|
import { isId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import {
|
import {
|
||||||
AsyncActionsModule,
|
AsyncActionsModule,
|
||||||
BitSubmitDirective,
|
BitSubmitDirective,
|
||||||
@@ -84,9 +83,9 @@ import { ExportScopeCalloutComponent } from "./export-scope-callout.component";
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
private _organizationId: string;
|
private _organizationId: OrganizationId | undefined;
|
||||||
|
|
||||||
get organizationId(): string {
|
get organizationId(): OrganizationId | undefined {
|
||||||
return this._organizationId;
|
return this._organizationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,14 +93,23 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
* Enables the hosting control to pass in an organizationId
|
* Enables the hosting control to pass in an organizationId
|
||||||
* If a organizationId is provided, the organization selection is disabled.
|
* 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;
|
this._organizationId = value;
|
||||||
|
|
||||||
getUserId(this.accountService.activeAccount$)
|
getUserId(this.accountService.activeAccount$)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
this.organizationService
|
this.organizationService.organizations$(userId).pipe(getById(this._organizationId)),
|
||||||
.organizations$(userId)
|
|
||||||
.pipe(getOrganizationById(this._organizationId)),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.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 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
|
* - Emits the organizationId when exporting from an organizationl vault
|
||||||
* */
|
* */
|
||||||
@Output()
|
@Output()
|
||||||
onSuccessfulExport = new EventEmitter<string>();
|
onSuccessfulExport = new EventEmitter<OrganizationId | undefined>();
|
||||||
|
|
||||||
@ViewChild(PasswordStrengthV2Component) passwordStrengthComponent: PasswordStrengthV2Component;
|
@ViewChild(PasswordStrengthV2Component) passwordStrengthComponent: PasswordStrengthV2Component;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user