From cbb22230fc41a7d72cd040432c795b334e8d8b32 Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Tue, 22 Nov 2022 10:08:19 -0800 Subject: [PATCH] Revert "[EC-73] edit collection modal (#3638)" This reverts commit 39655ebe29b9921fdbd6843cad1468dcf1509729. --- .../access-selector.component.html | 0 .../access-selector.component.spec.ts | 2 +- .../access-selector.component.ts | 0 .../access-selector/access-selector.models.ts | 2 +- .../access-selector/access-selector.module.ts | 2 +- .../access-selector.stories.ts | 2 +- .../components/access-selector/index.ts | 0 .../access-selector/user-type.pipe.ts | 0 .../core/core-organization.module.ts | 4 - apps/web/src/app/organizations/core/index.ts | 4 - .../core/services/collection-admin.service.ts | 123 -------- .../views/collection-access-selection-view.ts | 25 -- .../core/views/collection-admin-view.ts | 25 -- .../manage/collection-add-edit.component.html | 162 +++++++++++ .../manage/collection-add-edit.component.ts | 181 ++++++++++++ .../manage/collections.component.html | 10 - .../manage/collections.component.ts | 46 +-- .../manage/group-add-edit.component.ts | 5 +- .../organizations/manage/groups.component.ts | 10 + .../organization-routing.module.ts | 15 - .../app/organizations/organization.module.ts | 22 +- .../collection-dialog.component.html | 94 ------- .../collection-dialog.component.ts | 266 ------------------ .../collection-dialog.module.ts | 13 - .../collection-dialog.stories.ts | 245 ---------------- .../components/collection-dialog/index.ts | 2 - .../web/src/app/organizations/shared/index.ts | 2 - .../shared/shared-organization.module.ts | 9 - .../src/app/shared/loose-components.module.ts | 2 +- apps/web/src/app/shared/shared.module.ts | 5 +- apps/web/src/locales/en/messages.json | 46 +-- .../src/services/jslib-services.module.ts | 7 - libs/common/src/abstractions/api.service.ts | 4 +- .../group/group.service.abstraction.ts | 9 - libs/common/src/abstractions/group/index.ts | 2 - .../group/responses/group-response.ts | 31 -- libs/common/src/misc/utils.ts | 4 - .../src/models/request/collection.request.ts | 1 - .../models/response/collection.response.ts | 8 +- .../common/src/models/view/collection.view.ts | 4 +- libs/common/src/models/view/group-view.ts | 17 -- libs/common/src/services/api.service.ts | 6 +- .../src/services/group/group.service.ts | 65 ----- .../organization-group-bulk-request.ts | 7 - .../async-actions/form-button.directive.ts | 3 +- .../src/badge-list/badge-list.component.ts | 4 +- libs/components/src/badge/badge.directive.ts | 8 +- libs/components/src/badge/badge.stories.ts | 8 +- libs/components/src/badge/index.ts | 2 +- .../src/callout/callout.component.spec.ts | 2 +- .../src/form-field/bit-validators.stories.ts | 56 ---- .../forbidden-characters.validator.spec.ts | 45 --- .../forbidden-characters.validator.ts | 23 -- .../src/form-field/bit-validators/index.ts | 1 - .../src/form-field/error.component.ts | 2 - libs/components/src/form-field/index.ts | 1 - libs/components/src/multi-select/index.ts | 1 - 57 files changed, 420 insertions(+), 1225 deletions(-) rename apps/web/src/app/organizations/{shared => }/components/access-selector/access-selector.component.html (100%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/access-selector.component.spec.ts (98%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/access-selector.component.ts (100%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/access-selector.models.ts (97%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/access-selector.module.ts (83%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/access-selector.stories.ts (98%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/index.ts (100%) rename apps/web/src/app/organizations/{shared => }/components/access-selector/user-type.pipe.ts (100%) delete mode 100644 apps/web/src/app/organizations/core/core-organization.module.ts delete mode 100644 apps/web/src/app/organizations/core/index.ts delete mode 100644 apps/web/src/app/organizations/core/services/collection-admin.service.ts delete mode 100644 apps/web/src/app/organizations/core/views/collection-access-selection-view.ts delete mode 100644 apps/web/src/app/organizations/core/views/collection-admin-view.ts create mode 100644 apps/web/src/app/organizations/manage/collection-add-edit.component.html create mode 100644 apps/web/src/app/organizations/manage/collection-add-edit.component.ts delete mode 100644 apps/web/src/app/organizations/shared/components/collection-dialog/collection-dialog.component.html delete mode 100644 apps/web/src/app/organizations/shared/components/collection-dialog/collection-dialog.component.ts delete mode 100644 apps/web/src/app/organizations/shared/components/collection-dialog/collection-dialog.module.ts delete mode 100644 apps/web/src/app/organizations/shared/components/collection-dialog/collection-dialog.stories.ts delete mode 100644 apps/web/src/app/organizations/shared/components/collection-dialog/index.ts delete mode 100644 apps/web/src/app/organizations/shared/index.ts delete mode 100644 apps/web/src/app/organizations/shared/shared-organization.module.ts delete mode 100644 libs/common/src/abstractions/group/group.service.abstraction.ts delete mode 100644 libs/common/src/abstractions/group/index.ts delete mode 100644 libs/common/src/abstractions/group/responses/group-response.ts delete mode 100644 libs/common/src/models/view/group-view.ts delete mode 100644 libs/common/src/services/group/group.service.ts delete mode 100644 libs/common/src/services/group/requests/organization-group-bulk-request.ts delete mode 100644 libs/components/src/form-field/bit-validators.stories.ts delete mode 100644 libs/components/src/form-field/bit-validators/forbidden-characters.validator.spec.ts delete mode 100644 libs/components/src/form-field/bit-validators/forbidden-characters.validator.ts delete mode 100644 libs/components/src/form-field/bit-validators/index.ts diff --git a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.html b/apps/web/src/app/organizations/components/access-selector/access-selector.component.html similarity index 100% rename from apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.html rename to apps/web/src/app/organizations/components/access-selector/access-selector.component.html diff --git a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.spec.ts b/apps/web/src/app/organizations/components/access-selector/access-selector.component.spec.ts similarity index 98% rename from apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.spec.ts rename to apps/web/src/app/organizations/components/access-selector/access-selector.component.spec.ts index 145f1ad11e1..3b2ba911aac 100644 --- a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.spec.ts +++ b/apps/web/src/app/organizations/components/access-selector/access-selector.component.spec.ts @@ -15,7 +15,7 @@ import { } from "@bitwarden/components"; import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view"; -import { PreloadedEnglishI18nModule } from "../../../../tests/preloaded-english-i18n.module"; +import { PreloadedEnglishI18nModule } from "../../../tests/preloaded-english-i18n.module"; import { AccessSelectorComponent, PermissionMode } from "./access-selector.component"; import { AccessItemType, CollectionPermission } from "./access-selector.models"; diff --git a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/organizations/components/access-selector/access-selector.component.ts similarity index 100% rename from apps/web/src/app/organizations/shared/components/access-selector/access-selector.component.ts rename to apps/web/src/app/organizations/components/access-selector/access-selector.component.ts diff --git a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.models.ts b/apps/web/src/app/organizations/components/access-selector/access-selector.models.ts similarity index 97% rename from apps/web/src/app/organizations/shared/components/access-selector/access-selector.models.ts rename to apps/web/src/app/organizations/components/access-selector/access-selector.models.ts index ba457b9409f..5a9f878e55a 100644 --- a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.models.ts +++ b/apps/web/src/app/organizations/components/access-selector/access-selector.models.ts @@ -2,7 +2,7 @@ import { OrganizationUserStatusType } from "@bitwarden/common/enums/organization import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType"; import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view"; -import { CollectionAccessSelectionView } from "../../../core"; +import { CollectionAccessSelectionView } from "../../views/collection-access-selection.view"; /** * Permission options that replace/correspond with readOnly and hidePassword server fields. diff --git a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.module.ts b/apps/web/src/app/organizations/components/access-selector/access-selector.module.ts similarity index 83% rename from apps/web/src/app/organizations/shared/components/access-selector/access-selector.module.ts rename to apps/web/src/app/organizations/components/access-selector/access-selector.module.ts index 9bd0139f329..cbb01137b4d 100644 --- a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.module.ts +++ b/apps/web/src/app/organizations/components/access-selector/access-selector.module.ts @@ -1,6 +1,6 @@ import { NgModule } from "@angular/core"; -import { SharedModule } from "../../../../shared/shared.module"; +import { SharedModule } from "../../../shared"; import { AccessSelectorComponent } from "./access-selector.component"; import { UserTypePipe } from "./user-type.pipe"; diff --git a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.stories.ts b/apps/web/src/app/organizations/components/access-selector/access-selector.stories.ts similarity index 98% rename from apps/web/src/app/organizations/shared/components/access-selector/access-selector.stories.ts rename to apps/web/src/app/organizations/components/access-selector/access-selector.stories.ts index 63249f67a0b..059fb1c430c 100644 --- a/apps/web/src/app/organizations/shared/components/access-selector/access-selector.stories.ts +++ b/apps/web/src/app/organizations/components/access-selector/access-selector.stories.ts @@ -15,7 +15,7 @@ import { TabsModule, } from "@bitwarden/components"; -import { PreloadedEnglishI18nModule } from "../../../../tests/preloaded-english-i18n.module"; +import { PreloadedEnglishI18nModule } from "../../../tests/preloaded-english-i18n.module"; import { AccessSelectorComponent } from "./access-selector.component"; import { AccessItemType, AccessItemView, CollectionPermission } from "./access-selector.models"; diff --git a/apps/web/src/app/organizations/shared/components/access-selector/index.ts b/apps/web/src/app/organizations/components/access-selector/index.ts similarity index 100% rename from apps/web/src/app/organizations/shared/components/access-selector/index.ts rename to apps/web/src/app/organizations/components/access-selector/index.ts diff --git a/apps/web/src/app/organizations/shared/components/access-selector/user-type.pipe.ts b/apps/web/src/app/organizations/components/access-selector/user-type.pipe.ts similarity index 100% rename from apps/web/src/app/organizations/shared/components/access-selector/user-type.pipe.ts rename to apps/web/src/app/organizations/components/access-selector/user-type.pipe.ts diff --git a/apps/web/src/app/organizations/core/core-organization.module.ts b/apps/web/src/app/organizations/core/core-organization.module.ts deleted file mode 100644 index 57362e01d7c..00000000000 --- a/apps/web/src/app/organizations/core/core-organization.module.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { NgModule } from "@angular/core"; - -@NgModule({}) -export class CoreOrganizationModule {} diff --git a/apps/web/src/app/organizations/core/index.ts b/apps/web/src/app/organizations/core/index.ts deleted file mode 100644 index e68991103c9..00000000000 --- a/apps/web/src/app/organizations/core/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./core-organization.module"; -export * from "./services/collection-admin.service"; -export * from "./views/collection-access-selection-view"; -export * from "./views/collection-admin-view"; diff --git a/apps/web/src/app/organizations/core/services/collection-admin.service.ts b/apps/web/src/app/organizations/core/services/collection-admin.service.ts deleted file mode 100644 index 039751eb754..00000000000 --- a/apps/web/src/app/organizations/core/services/collection-admin.service.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { CollectionRequest } from "@bitwarden/common/models/request/collection.request"; -import { SelectionReadOnlyRequest } from "@bitwarden/common/models/request/selection-read-only.request"; -import { - CollectionAccessDetailsResponse, - CollectionResponse, -} from "@bitwarden/common/models/response/collection.response"; -import { CollectionView } from "@bitwarden/common/models/view/collection.view"; - -import { CoreOrganizationModule } from "../core-organization.module"; -import { CollectionAdminView } from "../views/collection-admin-view"; - -@Injectable({ providedIn: CoreOrganizationModule }) -export class CollectionAdminService { - constructor(private apiService: ApiService, private cryptoService: CryptoService) {} - - async getAll(organizationId: string): Promise { - const collectionResponse = await this.apiService.getCollections(organizationId); - if (collectionResponse?.data == null || collectionResponse.data.length === 0) { - return []; - } - - return await this.decryptMany(organizationId, collectionResponse.data); - } - - async get( - organizationId: string, - collectionId: string - ): Promise { - const collectionResponse = await this.apiService.getCollectionDetails( - organizationId, - collectionId - ); - - if (collectionResponse == null) { - return undefined; - } - - const [view] = await this.decryptMany(organizationId, [collectionResponse]); - - return view; - } - - async save(collection: CollectionAdminView): Promise { - const request = await this.encrypt(collection); - - let response: CollectionResponse; - if (collection.id == null) { - response = await this.apiService.postCollection(collection.organizationId, request); - collection.id = response.id; - } else { - response = await this.apiService.putCollection( - collection.organizationId, - collection.id, - request - ); - } - - // TODO: Implement upsert when in PS-1083: Collection Service refactors - // await this.collectionService.upsert(data); - return; - } - - async delete(organizationId: string, collectionId: string): Promise { - await this.apiService.deleteCollection(organizationId, collectionId); - } - - private async decryptMany( - organizationId: string, - collections: CollectionResponse[] | CollectionAccessDetailsResponse[] - ): Promise { - const orgKey = await this.cryptoService.getOrgKey(organizationId); - - const promises = collections.map(async (c) => { - const view = new CollectionAdminView(); - view.id = c.id; - view.name = await this.cryptoService.decryptToUtf8(new EncString(c.name), orgKey); - view.externalId = c.externalId; - view.organizationId = c.organizationId; - - if (isCollectionAccessDetailsResponse(c)) { - view.groups = c.groups; - view.users = c.users; - } - - return view; - }); - - return await Promise.all(promises); - } - - private async encrypt(model: CollectionAdminView): Promise { - if (model.organizationId == null) { - throw new Error("Collection has no organization id."); - } - const key = await this.cryptoService.getOrgKey(model.organizationId); - if (key == null) { - throw new Error("No key for this collection's organization."); - } - const collection = new CollectionRequest(); - collection.externalId = model.externalId; - collection.name = (await this.cryptoService.encrypt(model.name, key)).encryptedString; - collection.groups = model.groups.map( - (group) => new SelectionReadOnlyRequest(group.id, group.readOnly, group.hidePasswords) - ); - collection.users = model.users.map( - (user) => new SelectionReadOnlyRequest(user.id, user.readOnly, user.hidePasswords) - ); - return collection; - } -} - -function isCollectionAccessDetailsResponse( - response: CollectionResponse | CollectionAccessDetailsResponse -): response is CollectionAccessDetailsResponse { - const anyResponse = response as any; - - return anyResponse?.groups instanceof Array && anyResponse?.users instanceof Array; -} diff --git a/apps/web/src/app/organizations/core/views/collection-access-selection-view.ts b/apps/web/src/app/organizations/core/views/collection-access-selection-view.ts deleted file mode 100644 index 38191605fd1..00000000000 --- a/apps/web/src/app/organizations/core/views/collection-access-selection-view.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { View } from "@bitwarden/common/models/view/view"; - -interface SelectionResponseLike { - id: string; - readOnly: boolean; - hidePasswords: boolean; -} - -export class CollectionAccessSelectionView extends View { - readonly id: string; - readonly readOnly: boolean; - readonly hidePasswords: boolean; - - constructor(response?: SelectionResponseLike) { - super(); - - if (!response) { - return; - } - - this.id = response.id; - this.readOnly = response.readOnly; - this.hidePasswords = response.hidePasswords; - } -} diff --git a/apps/web/src/app/organizations/core/views/collection-admin-view.ts b/apps/web/src/app/organizations/core/views/collection-admin-view.ts deleted file mode 100644 index e0ad35bf438..00000000000 --- a/apps/web/src/app/organizations/core/views/collection-admin-view.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CollectionView } from "@bitwarden/common/models/view/collection.view"; -import { CollectionAccessDetailsResponse } from "@bitwarden/common/src/models/response/collection.response"; - -import { CollectionAccessSelectionView } from "./collection-access-selection-view"; - -export class CollectionAdminView extends CollectionView { - groups: CollectionAccessSelectionView[] = []; - users: CollectionAccessSelectionView[] = []; - - constructor(response?: CollectionAccessDetailsResponse) { - super(response); - - if (!response) { - return; - } - - this.groups = response.groups - ? response.groups.map((g) => new CollectionAccessSelectionView(g)) - : []; - - this.users = response.users - ? response.users.map((g) => new CollectionAccessSelectionView(g)) - : []; - } -} diff --git a/apps/web/src/app/organizations/manage/collection-add-edit.component.html b/apps/web/src/app/organizations/manage/collection-add-edit.component.html new file mode 100644 index 00000000000..97c973a4195 --- /dev/null +++ b/apps/web/src/app/organizations/manage/collection-add-edit.component.html @@ -0,0 +1,162 @@ + diff --git a/apps/web/src/app/organizations/manage/collection-add-edit.component.ts b/apps/web/src/app/organizations/manage/collection-add-edit.component.ts new file mode 100644 index 00000000000..6f0ad082920 --- /dev/null +++ b/apps/web/src/app/organizations/manage/collection-add-edit.component.ts @@ -0,0 +1,181 @@ +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; +import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { Utils } from "@bitwarden/common/misc/utils"; +import { EncString } from "@bitwarden/common/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; +import { CollectionRequest } from "@bitwarden/common/models/request/collection.request"; +import { SelectionReadOnlyRequest } from "@bitwarden/common/models/request/selection-read-only.request"; + +import { GroupServiceAbstraction } from "../services/abstractions/group"; +import { GroupView } from "../views/group.view"; + +@Component({ + selector: "app-collection-add-edit", + templateUrl: "collection-add-edit.component.html", +}) +export class CollectionAddEditComponent implements OnInit { + @Input() collectionId: string; + @Input() organizationId: string; + @Input() canSave: boolean; + @Input() canDelete: boolean; + @Output() onSavedCollection = new EventEmitter(); + @Output() onDeletedCollection = new EventEmitter(); + + loading = true; + editMode = false; + accessGroups = false; + title: string; + name: string; + externalId: string; + groups: GroupView[] = []; + formPromise: Promise; + deletePromise: Promise; + + private orgKey: SymmetricCryptoKey; + + constructor( + private apiService: ApiService, + private groupApiService: GroupServiceAbstraction, + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, + private cryptoService: CryptoService, + private logService: LogService, + private organizationService: OrganizationService + ) {} + + async ngOnInit() { + const organization = await this.organizationService.get(this.organizationId); + this.accessGroups = organization.useGroups; + this.editMode = this.loading = this.collectionId != null; + if (this.accessGroups) { + const groupsResponse = await this.groupApiService.getAll(this.organizationId); + this.groups = groupsResponse.sort(Utils.getSortFunction(this.i18nService, "name")); + } + this.orgKey = await this.cryptoService.getOrgKey(this.organizationId); + + if (this.editMode) { + this.editMode = true; + this.title = this.i18nService.t("editCollection"); + try { + const collection = await this.apiService.getCollectionDetails( + this.organizationId, + this.collectionId + ); + this.name = await this.cryptoService.decryptToUtf8( + new EncString(collection.name), + this.orgKey + ); + this.externalId = collection.externalId; + if (collection.groups != null && this.groups.length > 0) { + collection.groups.forEach((s) => { + const group = this.groups.filter((g) => !g.accessAll && g.id === s.id); + if (group != null && group.length > 0) { + (group[0] as any).checked = true; + (group[0] as any).readOnly = s.readOnly; + (group[0] as any).hidePasswords = s.hidePasswords; + } + }); + } + } catch (e) { + this.logService.error(e); + } + } else { + this.title = this.i18nService.t("addCollection"); + } + + this.groups.forEach((g) => { + if (g.accessAll) { + (g as any).checked = true; + } + }); + + this.loading = false; + } + + check(g: GroupView, select?: boolean) { + if (g.accessAll) { + return; + } + (g as any).checked = select == null ? !(g as any).checked : select; + if (!(g as any).checked) { + (g as any).readOnly = false; + (g as any).hidePasswords = false; + } + } + + selectAll(select: boolean) { + this.groups.forEach((g) => this.check(g, select)); + } + + async submit() { + if (this.orgKey == null) { + throw new Error("No encryption key for this organization."); + } + + const request = new CollectionRequest(); + request.name = (await this.cryptoService.encrypt(this.name, this.orgKey)).encryptedString; + request.externalId = this.externalId; + request.groups = this.groups + .filter((g) => (g as any).checked && !g.accessAll) + .map( + (g) => new SelectionReadOnlyRequest(g.id, !!(g as any).readOnly, !!(g as any).hidePasswords) + ); + + try { + if (this.editMode) { + this.formPromise = this.apiService.putCollection( + this.organizationId, + this.collectionId, + request + ); + } else { + this.formPromise = this.apiService.postCollection(this.organizationId, request); + } + await this.formPromise; + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t(this.editMode ? "editedCollectionId" : "createdCollectionId", this.name) + ); + this.onSavedCollection.emit(); + } catch (e) { + this.logService.error(e); + } + } + + async delete() { + if (!this.editMode) { + return; + } + + const confirmed = await this.platformUtilsService.showDialog( + this.i18nService.t("deleteCollectionConfirmation"), + this.name, + this.i18nService.t("yes"), + this.i18nService.t("no"), + "warning" + ); + if (!confirmed) { + return false; + } + + try { + this.deletePromise = this.apiService.deleteCollection(this.organizationId, this.collectionId); + await this.deletePromise; + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("deletedCollectionId", this.name) + ); + this.onDeletedCollection.emit(); + } catch (e) { + this.logService.error(e); + } + } +} diff --git a/apps/web/src/app/organizations/manage/collections.component.html b/apps/web/src/app/organizations/manage/collections.component.html index 69fc9e9896e..976a63fe6d4 100644 --- a/apps/web/src/app/organizations/manage/collections.component.html +++ b/apps/web/src/app/organizations/manage/collections.component.html @@ -65,16 +65,6 @@