1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 02:03:39 +00:00

[PM-22717] Expose DefaultUserCollectionEmail to clients (#15643)

* enforce restrictions based on collection type, set default collection type

* fix ts strict errors

* fix default collection enforcement in vault header

* enforce default collection restrictions in vault collection row

* enforce default collection restrictions in AC vault header

* enforce default collection restriction for select all

* fix ts strict error

* switch to signal, fix feature flag

* fix story

* clean up

* remove feature flag, move check for defaultCollecion to CollecitonView

* fix test

* remove unused configService

* fix test: coerce null to undefined for collection Id

* clean up leaky abstraction for default collection

* fix ts-strict error

* fix parens

* add new property to models, update logic, refactor for ts-strict

* fix type

* rename defaultCollection getter

* clean up

* clean up

* clean up, add comment, fix submit

* add comment

* add feature flag

* check model for name

* cleanup readonly logic, remove featureflag logic

* wip

* refactor CollectionRequest into Create and Update models

* fix readonly logic

* cleanup

* set defaultUserCollectionEmail in decryption from Collection

* split save into update/create methods

* fix readonly logic

* fix collections post and put requests

* add defaultUserCollection email to model when submitting collection dialog
This commit is contained in:
Brandon Treston
2025-08-26 11:42:52 -04:00
committed by GitHub
parent ad2dfe1e99
commit 28b5a2bb5e
20 changed files with 248 additions and 79 deletions

View File

@@ -45,9 +45,15 @@ export function cloneCollection(
let cloned;
if (collection instanceof CollectionAdminView) {
cloned = Object.assign(new CollectionAdminView({ ...collection }), collection);
cloned = Object.assign(
new CollectionAdminView({ ...collection, name: collection.name }),
collection,
);
} else {
cloned = Object.assign(new CollectionView({ ...collection }), collection);
cloned = Object.assign(
new CollectionView({ ...collection, name: collection.name }),
collection,
);
}
return cloned;
}

View File

@@ -398,6 +398,13 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
}
return;
}
if (
this.editMode &&
!this.collection.canEditName(this.organization) &&
this.formGroup.controls.name.dirty
) {
throw new Error("Cannot change readonly field: Name");
}
const parent = this.formGroup.controls.parent?.value;
const collectionView = new CollectionAdminView({
@@ -414,9 +421,13 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
collectionView.users = this.formGroup.controls.access.value
.filter((v) => v.type === AccessItemType.Member)
.map(convertToSelectionView);
collectionView.defaultUserCollectionEmail = this.collection.defaultUserCollectionEmail;
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const savedCollection = await this.collectionAdminService.save(collectionView, userId);
const collectionResponse = this.editMode
? await this.collectionAdminService.update(collectionView, userId)
: await this.collectionAdminService.create(collectionView, userId);
this.toastService.showToast({
variant: "success",
@@ -426,7 +437,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
),
});
this.close(CollectionDialogAction.Saved, savedCollection);
this.close(CollectionDialogAction.Saved, collectionResponse);
};
protected delete = async () => {
@@ -483,14 +494,23 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
private handleFormGroupReadonly(readonly: boolean) {
if (readonly) {
this.formGroup.controls.access.disable();
this.formGroup.controls.name.disable();
this.formGroup.controls.parent.disable();
this.formGroup.controls.access.disable();
} else {
return;
}
this.formGroup.controls.access.enable();
if (!this.editMode) {
this.formGroup.controls.name.enable();
this.formGroup.controls.parent.enable();
this.formGroup.controls.access.enable();
return;
}
const canEditName = this.collection.canEditName(this.organization);
this.formGroup.controls.name[canEditName ? "enable" : "disable"]();
this.formGroup.controls.parent[canEditName ? "enable" : "disable"]();
}
private close(action: CollectionDialogAction, collection?: CollectionResponse | CollectionView) {

View File

@@ -45,6 +45,7 @@ import {
import { OrganizationIntegrationApiService } from "@bitwarden/bit-common/dirt/integrations";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import {
InternalPolicyService,
@@ -317,7 +318,13 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: CollectionAdminService,
useClass: DefaultCollectionAdminService,
deps: [ApiService, KeyServiceAbstraction, EncryptService, CollectionService],
deps: [
ApiService,
KeyServiceAbstraction,
EncryptService,
CollectionService,
OrganizationService,
],
}),
safeProvider({
provide: SdkLoadService,

View File

@@ -250,7 +250,9 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
}
collections.forEach((c) => {
const collectionCopy = cloneCollection(new CollectionView({ ...c })) as CollectionFilter;
const collectionCopy = cloneCollection(
new CollectionView({ ...c, name: c.name }),
) as CollectionFilter;
collectionCopy.icon = "bwi-collection-shared";
const parts = c.name != null ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : [];
ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, null, NestingDelimiter);