1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-22 16:29:09 +01:00

[AC-1104] Fix access import/export with custom permission (#5014)

* [AC-1104] Allow importBlockedByPolicy to be overridden

Adjust the import component so that the importBlockedByPolicy flag can be overridden by the org import component to always return false.

* [AC-1104] Allow disabledByPolicy to be overridden in export component

Adjust the export component so that the disabledByPolicy flag can be overridden by the org export component to always return false.

* [AC-1104] Cleanup logic that disables export form

* [AC-1104] Use observable subscription for assigning importBlockedByPolicy flag

* [AC-1264] Add optional success callback for import component

Use the optional callback in org-import.component.ts to clear the file and file contents when the user does not have access to the vault page

* [AC-1264] Re-order properties

* [AC-1104] Refactor import component to only use onSuccess callback that can be overridden
This commit is contained in:
Shane Melton 2023-05-30 16:30:15 -07:00 committed by GitHub
parent 2f44b9b0dd
commit e092d42b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 39 deletions

View File

@ -54,6 +54,10 @@ export class OrganizationExportComponent extends ExportComponent {
); );
} }
protected get disabledByPolicy(): boolean {
return false;
}
async ngOnInit() { async ngOnInit() {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.parent.parent.params.subscribe(async (params) => { this.route.parent.parent.params.subscribe(async (params) => {
@ -62,10 +66,6 @@ export class OrganizationExportComponent extends ExportComponent {
await super.ngOnInit(); await super.ngOnInit();
} }
async checkExportDisabled() {
return;
}
getExportData() { getExportData() {
if (this.isFileEncryptedExport) { if (this.isFileEncryptedExport) {
return this.exportService.getPasswordProtectedExport(this.filePassword, this.organizationId); return this.exportService.getPasswordProtectedExport(this.filePassword, this.organizationId);

View File

@ -1,13 +1,18 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { switchMap, takeUntil } from "rxjs/operators";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ModalService } from "@bitwarden/angular/services/modal.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service"; 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 { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import {
canAccessVaultTab,
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 { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ImportServiceAbstraction } from "@bitwarden/importer"; import { ImportServiceAbstraction } from "@bitwarden/importer";
@ -19,7 +24,11 @@ import { ImportComponent } from "../../../../tools/import-export/import.componen
}) })
// eslint-disable-next-line rxjs-angular/prefer-takeuntil // eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class OrganizationImportComponent extends ImportComponent { export class OrganizationImportComponent extends ImportComponent {
organizationName: string; organization: Organization;
protected get importBlockedByPolicy(): boolean {
return false;
}
constructor( constructor(
i18nService: I18nService, i18nService: I18nService,
@ -47,21 +56,32 @@ export class OrganizationImportComponent extends ImportComponent {
); );
} }
async ngOnInit() { ngOnInit() {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.params
this.route.parent.parent.params.subscribe(async (params) => { .pipe(
this.organizationId = params.organizationId; switchMap((params) => this.organizationService.get$(params.organizationId)),
this.successNavigate = ["organizations", this.organizationId, "vault"]; takeUntil(this.destroy$)
await super.ngOnInit(); )
}); .subscribe((organization) => {
const organization = await this.organizationService.get(this.organizationId); this.organizationId = organization.id;
this.organizationName = organization.name; this.organization = organization;
});
super.ngOnInit();
}
protected async onSuccessfulImport(): Promise<void> {
if (canAccessVaultTab(this.organization)) {
await this.router.navigate(["organizations", this.organizationId, "vault"]);
} else {
this.fileSelected = null;
this.fileContents = "";
}
} }
async submit() { async submit() {
const confirmed = await this.dialogService.openSimpleDialog({ const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "warning" }, title: { key: "warning" },
content: { key: "importWarning", placeholders: [this.organizationName] }, content: { key: "importWarning", placeholders: [this.organization.name] },
type: SimpleDialogType.WARNING, type: SimpleDialogType.WARNING,
}); });

View File

@ -1,7 +1,8 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import * as JSZip from "jszip"; import * as JSZip from "jszip";
import { firstValueFrom } from "rxjs"; import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import Swal, { SweetAlertIcon } from "sweetalert2"; import Swal, { SweetAlertIcon } from "sweetalert2";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
@ -14,28 +15,29 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { import {
ImportOption, ImportOption,
ImportType,
ImportResult, ImportResult,
ImportServiceAbstraction, ImportServiceAbstraction,
ImportType,
} from "@bitwarden/importer"; } from "@bitwarden/importer";
import { ImportSuccessDialogComponent, FilePasswordPromptComponent } from "./dialog"; import { FilePasswordPromptComponent, ImportSuccessDialogComponent } from "./dialog";
@Component({ @Component({
selector: "app-import", selector: "app-import",
templateUrl: "import.component.html", templateUrl: "import.component.html",
}) })
export class ImportComponent implements OnInit { export class ImportComponent implements OnInit, OnDestroy {
featuredImportOptions: ImportOption[]; featuredImportOptions: ImportOption[];
importOptions: ImportOption[]; importOptions: ImportOption[];
format: ImportType = null; format: ImportType = null;
fileContents: string; fileContents: string;
fileSelected: File; fileSelected: File;
loading = false; loading = false;
importBlockedByPolicy = false;
protected organizationId: string = null; protected organizationId: string = null;
protected successNavigate: any[] = ["vault"]; protected destroy$ = new Subject<void>();
private _importBlockedByPolicy = false;
constructor( constructor(
protected i18nService: I18nService, protected i18nService: I18nService,
@ -49,12 +51,26 @@ export class ImportComponent implements OnInit {
protected dialogService: DialogServiceAbstraction protected dialogService: DialogServiceAbstraction
) {} ) {}
async ngOnInit() { protected get importBlockedByPolicy(): boolean {
return this._importBlockedByPolicy;
}
/**
* Callback that is called after a successful import.
*/
protected async onSuccessfulImport(): Promise<void> {
await this.router.navigate(["vault"]);
}
ngOnInit() {
this.setImportOptions(); this.setImportOptions();
this.importBlockedByPolicy = await firstValueFrom( this.policyService
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership) .policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
); .pipe(takeUntil(this.destroy$))
.subscribe((policyAppliesToActiveUser) => {
this._importBlockedByPolicy = policyAppliesToActiveUser;
});
} }
async submit() { async submit() {
@ -134,7 +150,7 @@ export class ImportComponent implements OnInit {
}); });
this.syncService.fullSync(true); this.syncService.fullSync(true);
this.router.navigate(this.successNavigate); await this.onSuccessfulImport();
} catch (e) { } catch (e) {
this.error(e); this.error(e);
this.logService.error(e); this.logService.error(e);
@ -264,4 +280,9 @@ export class ImportComponent implements OnInit {
return await ref.onClosedPromise(); return await ref.onClosedPromise();
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@ -1,6 +1,6 @@
import { Directive, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core"; import { Directive, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core";
import { UntypedFormBuilder, Validators } from "@angular/forms"; import { UntypedFormBuilder, Validators } from "@angular/forms";
import { merge, takeUntil, Subject, startWith } from "rxjs"; import { merge, startWith, Subject, takeUntil } from "rxjs";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
@ -21,7 +21,11 @@ export class ExportComponent implements OnInit, OnDestroy {
@Output() onSaved = new EventEmitter(); @Output() onSaved = new EventEmitter();
formPromise: Promise<string>; formPromise: Promise<string>;
disabledByPolicy = false; private _disabledByPolicy = false;
protected get disabledByPolicy(): boolean {
return this._disabledByPolicy;
}
exportForm = this.formBuilder.group({ exportForm = this.formBuilder.group({
format: ["json"], format: ["json"],
@ -59,11 +63,12 @@ export class ExportComponent implements OnInit, OnDestroy {
.policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport) .policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport)
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((policyAppliesToActiveUser) => { .subscribe((policyAppliesToActiveUser) => {
this.disabledByPolicy = policyAppliesToActiveUser; this._disabledByPolicy = policyAppliesToActiveUser;
if (this.disabledByPolicy) {
this.exportForm.disable();
}
}); });
await this.checkExportDisabled();
merge( merge(
this.exportForm.get("format").valueChanges, this.exportForm.get("format").valueChanges,
this.exportForm.get("fileEncryptionType").valueChanges this.exportForm.get("fileEncryptionType").valueChanges
@ -77,12 +82,6 @@ export class ExportComponent implements OnInit, OnDestroy {
this.destroy$.next(); this.destroy$.next();
} }
async checkExportDisabled() {
if (this.disabledByPolicy) {
this.exportForm.disable();
}
}
get encryptedFormat() { get encryptedFormat() {
return this.format === "encrypted_json"; return this.format === "encrypted_json";
} }