1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-14 10:26:19 +01:00

[PM-10294] Remove FC v1 from Clients (#10422)

* chore: remove fc v1 from org.canEditAnyCollection and update callers, refs PM-10294

* chore: remove fc v1 from collectionView.canEdit and update callers, refs PM-10294

* chore: remove fc v1 from organization.canEditAllCiphers and update callers, refs PM-10294

* chore: remove fc v1 from canDeleteAnyCollection, collection views, update callers, refs PM-10294

* chore: remove fc v1 from canEditUser/GroupAccess, refs PM-10294

* chore: remove fc v1 from canViewCollectionInfo, refs PM-10294

* chore: remove fc v1 from account component, refs PM-10294

* fix: remove fc v1 from collections component, refs PM-10294

* fix: update vault-items component, refs PM-10294

* fix: remove fc v1 from collection-dialog and collections components, refs PM-10294

* chore: remove ConfigService from group-add-edit and account components, refs PM-10294

* chore: change canEditAnyCollection to getter and update callers, refs PM-10294

* chore: change canEditUnmanagedCollections to getter and update callers, refs PM-10294

* chore: change canDeleteAnyCollection to getter and update callers, refs PM-10294

* chore: remove deprecated observable and update comments with v1, refs PM-10294

* chore: remove ununsed ConfigService from collection-dialog component, refs PM-10294

* chore: remove final fc v1 ref for vault-collection-row, refs PM-10294
This commit is contained in:
Vincent Salucci 2024-08-13 10:45:41 -05:00 committed by GitHub
parent 43da67ee51
commit 471dd3bd7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 254 additions and 717 deletions

View File

@ -19,9 +19,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -96,9 +94,6 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
private organization$ = this.organizationService private organization$ = this.organizationService
.get$(this.organizationId) .get$(this.organizationId)
.pipe(shareReplay({ refCount: true })); .pipe(shareReplay({ refCount: true }));
private flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
FeatureFlag.FlexibleCollectionsV1,
);
protected PermissionMode = PermissionMode; protected PermissionMode = PermissionMode;
protected ResultType = GroupAddEditDialogResultType; protected ResultType = GroupAddEditDialogResultType;
@ -179,27 +174,19 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
shareReplay({ refCount: true, bufferSize: 1 }), shareReplay({ refCount: true, bufferSize: 1 }),
); );
protected allowAdminAccessToAllCollectionItems$ = combineLatest([ protected allowAdminAccessToAllCollectionItems$ = this.organization$.pipe(
this.organization$, map((organization) => {
this.flexibleCollectionsV1Enabled$,
]).pipe(
map(([organization, flexibleCollectionsV1Enabled]) => {
if (!flexibleCollectionsV1Enabled) {
return true;
}
return organization.allowAdminAccessToAllCollectionItems; return organization.allowAdminAccessToAllCollectionItems;
}), }),
); );
protected canAssignAccessToAnyCollection$ = combineLatest([ protected canAssignAccessToAnyCollection$ = combineLatest([
this.organization$, this.organization$,
this.flexibleCollectionsV1Enabled$,
this.allowAdminAccessToAllCollectionItems$, this.allowAdminAccessToAllCollectionItems$,
]).pipe( ]).pipe(
map( map(
([org, flexibleCollectionsV1Enabled, allowAdminAccessToAllCollectionItems]) => ([org, allowAdminAccessToAllCollectionItems]) =>
org.canEditAnyCollection(flexibleCollectionsV1Enabled) || org.canEditAnyCollection ||
// Manage Groups custom permission cannot edit any collection but they can assign access from this dialog // Manage Groups custom permission cannot edit any collection but they can assign access from this dialog
// if permitted by collection management settings // if permitted by collection management settings
(org.permissions.manageGroups && allowAdminAccessToAllCollectionItems), (org.permissions.manageGroups && allowAdminAccessToAllCollectionItems),
@ -224,7 +211,6 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dialogService: DialogService, private dialogService: DialogService,
private organizationService: OrganizationService, private organizationService: OrganizationService,
private configService: ConfigService,
private accountService: AccountService, private accountService: AccountService,
private collectionAdminService: CollectionAdminService, private collectionAdminService: CollectionAdminService,
) { ) {
@ -242,27 +228,13 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
this.cannotAddSelfToGroup$, this.cannotAddSelfToGroup$,
this.accountService.activeAccount$, this.accountService.activeAccount$,
this.organization$, this.organization$,
this.flexibleCollectionsV1Enabled$,
]) ])
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe( .subscribe(
([ ([collections, members, group, restrictGroupAccess, activeAccount, organization]) => {
collections,
members,
group,
restrictGroupAccess,
activeAccount,
organization,
flexibleCollectionsV1Enabled,
]) => {
this.members = members; this.members = members;
this.group = group; this.group = group;
this.collections = mapToAccessItemViews( this.collections = mapToAccessItemViews(collections, organization, group);
collections,
organization,
flexibleCollectionsV1Enabled,
group,
);
if (this.group != undefined) { if (this.group != undefined) {
// Must detect changes so that AccessSelector @Inputs() are aware of the latest // Must detect changes so that AccessSelector @Inputs() are aware of the latest
@ -384,7 +356,6 @@ function mapToAccessSelections(group: GroupView, items: AccessItemView[]): Acces
function mapToAccessItemViews( function mapToAccessItemViews(
collections: CollectionAdminView[], collections: CollectionAdminView[],
organization: Organization, organization: Organization,
flexibleCollectionsV1Enabled: boolean,
group?: GroupView, group?: GroupView,
): AccessItemView[] { ): AccessItemView[] {
return ( return (
@ -396,7 +367,7 @@ function mapToAccessItemViews(
type: AccessItemType.Collection, type: AccessItemType.Collection,
labelName: c.name, labelName: c.name,
listName: c.name, listName: c.name,
readonly: !c.canEditGroupAccess(organization, flexibleCollectionsV1Enabled), readonly: !c.canEditGroupAccess(organization),
readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined, readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined,
}; };
}) })

View File

@ -23,8 +23,6 @@ import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permi
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ProductTierType } from "@bitwarden/common/billing/enums"; import { ProductTierType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
@ -143,7 +141,6 @@ export class MemberDialogComponent implements OnDestroy {
private userService: UserAdminService, private userService: UserAdminService,
private organizationUserService: OrganizationUserService, private organizationUserService: OrganizationUserService,
private dialogService: DialogService, private dialogService: DialogService,
private configService: ConfigService,
private accountService: AccountService, private accountService: AccountService,
organizationService: OrganizationService, organizationService: OrganizationService,
) { ) {
@ -174,15 +171,8 @@ export class MemberDialogComponent implements OnDestroy {
? this.userService.get(this.params.organizationId, this.params.organizationUserId) ? this.userService.get(this.params.organizationId, this.params.organizationUserId)
: of(null); : of(null);
this.allowAdminAccessToAllCollectionItems$ = combineLatest([ this.allowAdminAccessToAllCollectionItems$ = this.organization$.pipe(
this.organization$, map((organization) => {
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
]).pipe(
map(([organization, flexibleCollectionsV1Enabled]) => {
if (!flexibleCollectionsV1Enabled) {
return true;
}
return organization.allowAdminAccessToAllCollectionItems; return organization.allowAdminAccessToAllCollectionItems;
}), }),
); );
@ -208,18 +198,13 @@ export class MemberDialogComponent implements OnDestroy {
} }
}); });
const flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
FeatureFlag.FlexibleCollectionsV1,
);
this.canAssignAccessToAnyCollection$ = combineLatest([ this.canAssignAccessToAnyCollection$ = combineLatest([
this.organization$, this.organization$,
flexibleCollectionsV1Enabled$,
this.allowAdminAccessToAllCollectionItems$, this.allowAdminAccessToAllCollectionItems$,
]).pipe( ]).pipe(
map( map(
([org, flexibleCollectionsV1Enabled, allowAdminAccessToAllCollectionItems]) => ([org, allowAdminAccessToAllCollectionItems]) =>
org.canEditAnyCollection(flexibleCollectionsV1Enabled) || org.canEditAnyCollection ||
// Manage Users custom permission cannot edit any collection but they can assign access from this dialog // Manage Users custom permission cannot edit any collection but they can assign access from this dialog
// if permitted by collection management settings // if permitted by collection management settings
(org.permissions.manageUsers && allowAdminAccessToAllCollectionItems), (org.permissions.manageUsers && allowAdminAccessToAllCollectionItems),
@ -231,49 +216,39 @@ export class MemberDialogComponent implements OnDestroy {
collections: this.collectionAdminService.getAll(this.params.organizationId), collections: this.collectionAdminService.getAll(this.params.organizationId),
userDetails: userDetails$, userDetails: userDetails$,
groups: groups$, groups: groups$,
flexibleCollectionsV1Enabled: flexibleCollectionsV1Enabled$,
}) })
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe( .subscribe(({ organization, collections, userDetails, groups }) => {
({ organization, collections, userDetails, groups, flexibleCollectionsV1Enabled }) => { this.setFormValidators(organization);
this.setFormValidators(organization);
// Groups tab: populate available groups // Groups tab: populate available groups
this.groupAccessItems = [].concat( this.groupAccessItems = [].concat(
groups.map<AccessItemView>((g) => mapGroupToAccessItemView(g)), groups.map<AccessItemView>((g) => mapGroupToAccessItemView(g)),
);
// Collections tab: Populate all available collections (including current user access where applicable)
this.collectionAccessItems = collections
.map((c) =>
mapCollectionToAccessItemView(
c,
organization,
userDetails == null
? undefined
: c.users.find((access) => access.id === userDetails.id),
),
)
// But remove collections that we can't assign access to, unless the user is already assigned
.filter(
(item) =>
!item.readonly || userDetails?.collections.some((access) => access.id == item.id),
); );
// Collections tab: Populate all available collections (including current user access where applicable) if (userDetails != null) {
this.collectionAccessItems = collections this.loadOrganizationUser(userDetails, groups, collections, organization);
.map((c) => }
mapCollectionToAccessItemView(
c,
organization,
flexibleCollectionsV1Enabled,
userDetails == null
? undefined
: c.users.find((access) => access.id === userDetails.id),
),
)
// But remove collections that we can't assign access to, unless the user is already assigned
.filter(
(item) =>
!item.readonly || userDetails?.collections.some((access) => access.id == item.id),
);
if (userDetails != null) { this.loading = false;
this.loadOrganizationUser( });
userDetails,
groups,
collections,
organization,
flexibleCollectionsV1Enabled,
);
}
this.loading = false;
},
);
} }
private setFormValidators(organization: Organization) { private setFormValidators(organization: Organization) {
@ -297,7 +272,6 @@ export class MemberDialogComponent implements OnDestroy {
groups: GroupView[], groups: GroupView[],
collections: CollectionAdminView[], collections: CollectionAdminView[],
organization: Organization, organization: Organization,
flexibleCollectionsV1Enabled: boolean,
) { ) {
if (!userDetails) { if (!userDetails) {
throw new Error("Could not find user to edit."); throw new Error("Could not find user to edit.");
@ -341,13 +315,7 @@ export class MemberDialogComponent implements OnDestroy {
// Populate additional collection access via groups (rendered as separate rows from user access) // Populate additional collection access via groups (rendered as separate rows from user access)
this.collectionAccessItems = this.collectionAccessItems.concat( this.collectionAccessItems = this.collectionAccessItems.concat(
collectionsFromGroups.map(({ collection, accessSelection, group }) => collectionsFromGroups.map(({ collection, accessSelection, group }) =>
mapCollectionToAccessItemView( mapCollectionToAccessItemView(collection, organization, accessSelection, group),
collection,
organization,
flexibleCollectionsV1Enabled,
accessSelection,
group,
),
), ),
); );
@ -621,7 +589,6 @@ export class MemberDialogComponent implements OnDestroy {
function mapCollectionToAccessItemView( function mapCollectionToAccessItemView(
collection: CollectionAdminView, collection: CollectionAdminView,
organization: Organization, organization: Organization,
flexibleCollectionsV1Enabled: boolean,
accessSelection?: CollectionAccessSelectionView, accessSelection?: CollectionAccessSelectionView,
group?: GroupView, group?: GroupView,
): AccessItemView { ): AccessItemView {
@ -630,9 +597,7 @@ function mapCollectionToAccessItemView(
id: group ? `${collection.id}-${group.id}` : collection.id, id: group ? `${collection.id}-${group.id}` : collection.id,
labelName: collection.name, labelName: collection.name,
listName: collection.name, listName: collection.name,
readonly: readonly: group !== undefined || !collection.canEditUserAccess(organization),
group !== undefined ||
!collection.canEditUserAccess(organization, flexibleCollectionsV1Enabled),
readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined, readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined,
viaGroupName: group?.name, viaGroupName: group?.name,
}; };

View File

@ -56,7 +56,7 @@
> >
<h1 bitTypography="h1" class="tw-mt-16 tw-pb-2.5">{{ "collectionManagement" | i18n }}</h1> <h1 bitTypography="h1" class="tw-mt-16 tw-pb-2.5">{{ "collectionManagement" | i18n }}</h1>
<p bitTypography="body1">{{ "collectionManagementDesc" | i18n }}</p> <p bitTypography="body1">{{ "collectionManagementDesc" | i18n }}</p>
<bit-form-control *ngIf="flexibleCollectionsV1Enabled$ | async"> <bit-form-control>
<bit-label>{{ "allowAdminAccessToAllCollectionItemsDesc" | i18n }}</bit-label> <bit-label>{{ "allowAdminAccessToAllCollectionItemsDesc" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox formControlName="allowAdminAccessToAllCollectionItems" /> <input type="checkbox" bitCheckbox formControlName="allowAdminAccessToAllCollectionItems" />
</bit-form-control> </bit-form-control>

View File

@ -10,8 +10,6 @@ import { OrganizationCollectionManagementUpdateRequest } from "@bitwarden/common
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "@bitwarden/common/admin-console/models/request/organization-update.request"; import { OrganizationUpdateRequest } from "@bitwarden/common/admin-console/models/request/organization-update.request";
import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response"; import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -40,10 +38,6 @@ export class AccountComponent implements OnInit, OnDestroy {
org: OrganizationResponse; org: OrganizationResponse;
taxFormPromise: Promise<unknown>; taxFormPromise: Promise<unknown>;
flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
FeatureFlag.FlexibleCollectionsV1,
);
// FormGroup validators taken from server Organization domain object // FormGroup validators taken from server Organization domain object
protected formGroup = this.formBuilder.group({ protected formGroup = this.formBuilder.group({
orgName: this.formBuilder.control( orgName: this.formBuilder.control(
@ -83,7 +77,6 @@ export class AccountComponent implements OnInit, OnDestroy {
private organizationApiService: OrganizationApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction,
private dialogService: DialogService, private dialogService: DialogService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private configService: ConfigService,
) {} ) {}
async ngOnInit() { async ngOnInit() {

View File

@ -80,13 +80,9 @@
<span *ngIf="!organization.useGroups">{{ <span *ngIf="!organization.useGroups">{{
"grantCollectionAccessMembersOnly" | i18n "grantCollectionAccessMembersOnly" | i18n
}}</span> }}</span>
<span <span *ngIf="organization.allowAdminAccessToAllCollectionItems">
*ngIf=" {{ " " + ("adminCollectionAccess" | i18n) }}
(flexibleCollectionsV1Enabled$ | async) && </span>
organization.allowAdminAccessToAllCollectionItems
"
>{{ " " + ("adminCollectionAccess" | i18n) }}</span
>
</ng-container> </ng-container>
</div> </div>
<div <div

View File

@ -17,8 +17,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses/organization-user.response"; import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses/organization-user.response";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
@ -77,10 +75,6 @@ export enum CollectionDialogAction {
templateUrl: "collection-dialog.component.html", templateUrl: "collection-dialog.component.html",
}) })
export class CollectionDialogComponent implements OnInit, OnDestroy { export class CollectionDialogComponent implements OnInit, OnDestroy {
protected flexibleCollectionsV1Enabled$ = this.configService
.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1)
.pipe(first());
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
protected organizations$: Observable<Organization[]>; protected organizations$: Observable<Organization[]>;
@ -113,7 +107,6 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
private i18nService: I18nService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private organizationUserService: OrganizationUserService, private organizationUserService: OrganizationUserService,
private configService: ConfigService,
private dialogService: DialogService, private dialogService: DialogService,
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
) { ) {
@ -163,95 +156,90 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
groups: groups$, groups: groups$,
// Collection(s) needed to map readonlypermission for (potential) access selector disabled state // Collection(s) needed to map readonlypermission for (potential) access selector disabled state
users: this.organizationUserService.getAllUsers(orgId, { includeCollections: true }), users: this.organizationUserService.getAllUsers(orgId, { includeCollections: true }),
flexibleCollectionsV1: this.flexibleCollectionsV1Enabled$,
}) })
.pipe(takeUntil(this.formGroup.controls.selectedOrg.valueChanges), takeUntil(this.destroy$)) .pipe(takeUntil(this.formGroup.controls.selectedOrg.valueChanges), takeUntil(this.destroy$))
.subscribe( .subscribe(({ organization, collections: allCollections, groups, users }) => {
({ organization, collections: allCollections, groups, users, flexibleCollectionsV1 }) => { this.organization = organization;
this.organization = organization; this.accessItems = [].concat(
this.accessItems = [].concat( groups.map((group) => mapGroupToAccessItemView(group, this.collectionId)),
groups.map((group) => mapGroupToAccessItemView(group, this.collectionId)), users.data.map((user) => mapUserToAccessItemView(user, this.collectionId)),
users.data.map((user) => mapUserToAccessItemView(user, this.collectionId)), );
);
// Force change detection to update the access selector's items // Force change detection to update the access selector's items
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
this.nestOptions = this.params.limitNestedCollections this.nestOptions = this.params.limitNestedCollections
? allCollections.filter((c) => c.manage) ? allCollections.filter((c) => c.manage)
: allCollections; : allCollections;
if (this.params.collectionId) { if (this.params.collectionId) {
this.collection = allCollections.find((c) => c.id === this.collectionId); this.collection = allCollections.find((c) => c.id === this.collectionId);
// Ensure we don't allow nesting the current collection within itself // Ensure we don't allow nesting the current collection within itself
this.nestOptions = this.nestOptions.filter((c) => c.id !== this.collectionId); this.nestOptions = this.nestOptions.filter((c) => c.id !== this.collectionId);
if (!this.collection) { if (!this.collection) {
throw new Error("Could not find collection to edit."); throw new Error("Could not find collection to edit.");
}
// Parse the name to find its parent name
const { name, parent: parentName } = parseName(this.collection);
// Determine if the user can see/select the parent collection
if (parentName !== undefined) {
if (
this.organization.canViewAllCollections &&
!allCollections.find((c) => c.name === parentName)
) {
// The user can view all collections, but the parent was not found -> assume it has been deleted
this.deletedParentName = parentName;
} else if (!this.nestOptions.find((c) => c.name === parentName)) {
// We cannot find the current parent collection in our list of options, so add a placeholder
this.nestOptions.unshift({ name: parentName } as CollectionView);
}
}
const accessSelections = mapToAccessSelections(this.collection);
this.formGroup.patchValue({
name,
externalId: this.collection.externalId,
parent: parentName,
access: accessSelections,
});
this.showDeleteButton =
!this.dialogReadonly &&
this.collection.canDelete(organization, flexibleCollectionsV1);
} else {
const parent = this.nestOptions.find((c) => c.id === this.params.parentCollectionId);
const currentOrgUserId = users.data.find(
(u) => u.userId === this.organization?.userId,
)?.id;
const initialSelection: AccessItemValue[] =
currentOrgUserId !== undefined
? [
{
id: currentOrgUserId,
type: AccessItemType.Member,
permission: CollectionPermission.Manage,
},
]
: [];
this.formGroup.patchValue({
parent: parent?.name ?? undefined,
access: initialSelection,
});
} }
if (flexibleCollectionsV1 && !organization.allowAdminAccessToAllCollectionItems) { // Parse the name to find its parent name
this.formGroup.controls.access.addValidators(validateCanManagePermission); const { name, parent: parentName } = parseName(this.collection);
} else {
this.formGroup.controls.access.removeValidators(validateCanManagePermission); // Determine if the user can see/select the parent collection
if (parentName !== undefined) {
if (
this.organization.canViewAllCollections &&
!allCollections.find((c) => c.name === parentName)
) {
// The user can view all collections, but the parent was not found -> assume it has been deleted
this.deletedParentName = parentName;
} else if (!this.nestOptions.find((c) => c.name === parentName)) {
// We cannot find the current parent collection in our list of options, so add a placeholder
this.nestOptions.unshift({ name: parentName } as CollectionView);
}
} }
this.formGroup.controls.access.updateValueAndValidity();
this.handleFormGroupReadonly(this.dialogReadonly); const accessSelections = mapToAccessSelections(this.collection);
this.formGroup.patchValue({
name,
externalId: this.collection.externalId,
parent: parentName,
access: accessSelections,
});
this.showDeleteButton = !this.dialogReadonly && this.collection.canDelete(organization);
} else {
const parent = this.nestOptions.find((c) => c.id === this.params.parentCollectionId);
const currentOrgUserId = users.data.find(
(u) => u.userId === this.organization?.userId,
)?.id;
const initialSelection: AccessItemValue[] =
currentOrgUserId !== undefined
? [
{
id: currentOrgUserId,
type: AccessItemType.Member,
permission: CollectionPermission.Manage,
},
]
: [];
this.loading = false; this.formGroup.patchValue({
this.showAddAccessWarning = this.handleAddAccessWarning(flexibleCollectionsV1); parent: parent?.name ?? undefined,
}, access: initialSelection,
); });
}
if (!organization.allowAdminAccessToAllCollectionItems) {
this.formGroup.controls.access.addValidators(validateCanManagePermission);
} else {
this.formGroup.controls.access.removeValidators(validateCanManagePermission);
}
this.formGroup.controls.access.updateValueAndValidity();
this.handleFormGroupReadonly(this.dialogReadonly);
this.loading = false;
this.showAddAccessWarning = this.handleAddAccessWarning();
});
} }
protected get collectionId() { protected get collectionId() {
@ -361,9 +349,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.destroy$.complete(); this.destroy$.complete();
} }
private handleAddAccessWarning(flexibleCollectionsV1: boolean): boolean { private handleAddAccessWarning(): boolean {
if ( if (
flexibleCollectionsV1 &&
!this.organization?.allowAdminAccessToAllCollectionItems && !this.organization?.allowAdminAccessToAllCollectionItems &&
this.params.isAddAccessCollection this.params.isAddAccessCollection
) { ) {

View File

@ -34,7 +34,6 @@ export class VaultCollectionRowComponent {
@Input() organizations: Organization[]; @Input() organizations: Organization[];
@Input() groups: GroupView[]; @Input() groups: GroupView[];
@Input() showPermissionsColumn: boolean; @Input() showPermissionsColumn: boolean;
@Input() flexibleCollectionsV1Enabled: boolean;
@Input() restrictProviderAccess: boolean; @Input() restrictProviderAccess: boolean;
@Output() onEvent = new EventEmitter<VaultItemEvent>(); @Output() onEvent = new EventEmitter<VaultItemEvent>();
@ -57,10 +56,6 @@ export class VaultCollectionRowComponent {
} }
get showAddAccess() { get showAddAccess() {
if (!this.flexibleCollectionsV1Enabled) {
return false;
}
if (this.collection.id == Unassigned) { if (this.collection.id == Unassigned) {
return false; return false;
} }
@ -71,7 +66,7 @@ export class VaultCollectionRowComponent {
return ( return (
!this.organization?.allowAdminAccessToAllCollectionItems && !this.organization?.allowAdminAccessToAllCollectionItems &&
this.collection.unmanaged && this.collection.unmanaged &&
this.organization?.canEditUnmanagedCollections() this.organization?.canEditUnmanagedCollections
); );
} }
@ -114,10 +109,6 @@ export class VaultCollectionRowComponent {
} }
protected get showCheckbox() { protected get showCheckbox() {
if (this.flexibleCollectionsV1Enabled) { return this.collection?.id !== Unassigned;
return this.collection?.id !== Unassigned;
}
return this.canDeleteCollection;
} }
} }

View File

@ -113,7 +113,6 @@
[canDeleteCollection]="canDeleteCollection(item.collection)" [canDeleteCollection]="canDeleteCollection(item.collection)"
[canEditCollection]="canEditCollection(item.collection)" [canEditCollection]="canEditCollection(item.collection)"
[canViewCollectionInfo]="canViewCollectionInfo(item.collection)" [canViewCollectionInfo]="canViewCollectionInfo(item.collection)"
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled"
[restrictProviderAccess]="restrictProviderAccess" [restrictProviderAccess]="restrictProviderAccess"
[checked]="selection.isSelected(item)" [checked]="selection.isSelected(item)"
(checkedToggled)="selection.toggle(item)" (checkedToggled)="selection.toggle(item)"

View File

@ -44,7 +44,6 @@ export class VaultItemsComponent {
@Input() showBulkAddToCollections = false; @Input() showBulkAddToCollections = false;
@Input() showPermissionsColumn = false; @Input() showPermissionsColumn = false;
@Input() viewingOrgVault: boolean; @Input() viewingOrgVault: boolean;
@Input({ required: true }) flexibleCollectionsV1Enabled = false;
@Input() addAccessStatus: number; @Input() addAccessStatus: number;
@Input() addAccessToggle: boolean; @Input() addAccessToggle: boolean;
@Input() restrictProviderAccess: boolean; @Input() restrictProviderAccess: boolean;
@ -120,7 +119,7 @@ export class VaultItemsComponent {
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId); const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
return collection.canEdit(organization, this.flexibleCollectionsV1Enabled); return collection.canEdit(organization);
} }
protected canDeleteCollection(collection: CollectionView): boolean { protected canDeleteCollection(collection: CollectionView): boolean {
@ -131,12 +130,12 @@ export class VaultItemsComponent {
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId); const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
return collection.canDelete(organization, this.flexibleCollectionsV1Enabled); return collection.canDelete(organization);
} }
protected canViewCollectionInfo(collection: CollectionView) { protected canViewCollectionInfo(collection: CollectionView) {
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId); const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
return collection.canViewCollectionInfo(organization, this.flexibleCollectionsV1Enabled); return collection.canViewCollectionInfo(organization);
} }
protected toggleAll() { protected toggleAll() {
@ -214,11 +213,7 @@ export class VaultItemsComponent {
const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId); const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId);
return ( return (
(organization.canEditAllCiphers( (organization.canEditAllCiphers(this.restrictProviderAccess) && this.viewingOrgVault) ||
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
) &&
this.viewingOrgVault) ||
cipher.edit cipher.edit
); );
} }
@ -230,21 +225,12 @@ export class VaultItemsComponent {
this.selection.clear(); this.selection.clear();
if (this.flexibleCollectionsV1Enabled) { // Every item except for the Unassigned collection is selectable, individual bulk actions check the user's permission
// Every item except for the Unassigned collection is selectable, individual bulk actions check the user's permission this.editableItems = items.filter(
this.editableItems = items.filter( (item) =>
(item) => item.cipher !== undefined ||
item.cipher !== undefined || (item.collection !== undefined && item.collection.id !== Unassigned),
(item.collection !== undefined && item.collection.id !== Unassigned), );
);
} else {
// only collections the user can delete are selectable
this.editableItems = items.filter(
(item) =>
item.cipher !== undefined ||
(item.collection !== undefined && this.canDeleteCollection(item.collection)),
);
}
this.dataSource.data = items; this.dataSource.data = items;
} }
@ -293,10 +279,7 @@ export class VaultItemsComponent {
const organization = this.allOrganizations.find((o) => o.id === orgId); const organization = this.allOrganizations.find((o) => o.id === orgId);
const canEditOrManageAllCiphers = const canEditOrManageAllCiphers =
organization?.canEditAllCiphers( organization?.canEditAllCiphers(this.restrictProviderAccess) && this.viewingOrgVault;
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
) && this.viewingOrgVault;
const collectionNotSelected = const collectionNotSelected =
this.selection.selected.filter((item) => item.collection).length === 0; this.selection.selected.filter((item) => item.collection).length === 0;
@ -317,9 +300,7 @@ export class VaultItemsComponent {
const canEditOrManageAllCiphers = const canEditOrManageAllCiphers =
organizations.length > 0 && organizations.length > 0 &&
organizations.every((org) => organizations.every((org) => org?.canEditAllCiphers(this.restrictProviderAccess));
org?.canEditAllCiphers(this.flexibleCollectionsV1Enabled, this.restrictProviderAccess),
);
const canDeleteCollections = this.selection.selected const canDeleteCollections = this.selection.selected
.filter((item) => item.collection) .filter((item) => item.collection)

View File

@ -41,61 +41,44 @@ export class CollectionAdminView extends CollectionView {
/** /**
* Returns true if the user can edit a collection (including user and group access) from the Admin Console. * Returns true if the user can edit a collection (including user and group access) from the Admin Console.
*/ */
override canEdit(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { override canEdit(org: Organization): boolean {
return ( return (
org?.canEditAnyCollection(flexibleCollectionsV1Enabled) || org?.canEditAnyCollection ||
(flexibleCollectionsV1Enabled && this.unmanaged && org?.canEditUnmanagedCollections()) || (this.unmanaged && org?.canEditUnmanagedCollections) ||
super.canEdit(org, flexibleCollectionsV1Enabled) super.canEdit(org)
); );
} }
/** /**
* Returns true if the user can delete a collection from the Admin Console. * Returns true if the user can delete a collection from the Admin Console.
*/ */
override canDelete(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { override canDelete(org: Organization): boolean {
return ( return org?.canDeleteAnyCollection || super.canDelete(org);
org?.canDeleteAnyCollection(flexibleCollectionsV1Enabled) ||
super.canDelete(org, flexibleCollectionsV1Enabled)
);
} }
/** /**
* Whether the user can modify user access to this collection * Whether the user can modify user access to this collection
*/ */
canEditUserAccess(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { canEditUserAccess(org: Organization): boolean {
const allowAdminAccessToAllCollectionItems =
!flexibleCollectionsV1Enabled || org.allowAdminAccessToAllCollectionItems;
return ( return (
(org.permissions.manageUsers && allowAdminAccessToAllCollectionItems) || (org.permissions.manageUsers && org.allowAdminAccessToAllCollectionItems) || this.canEdit(org)
this.canEdit(org, flexibleCollectionsV1Enabled)
); );
} }
/** /**
* Whether the user can modify group access to this collection * Whether the user can modify group access to this collection
*/ */
canEditGroupAccess(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { canEditGroupAccess(org: Organization): boolean {
const allowAdminAccessToAllCollectionItems =
!flexibleCollectionsV1Enabled || org.allowAdminAccessToAllCollectionItems;
return ( return (
(org.permissions.manageGroups && allowAdminAccessToAllCollectionItems) || (org.permissions.manageGroups && org.allowAdminAccessToAllCollectionItems) ||
this.canEdit(org, flexibleCollectionsV1Enabled) this.canEdit(org)
); );
} }
/** /**
* Returns true if the user can view collection info and access in a read-only state from the Admin Console * Returns true if the user can view collection info and access in a read-only state from the Admin Console
*/ */
override canViewCollectionInfo( override canViewCollectionInfo(org: Organization | undefined): boolean {
org: Organization | undefined,
flexibleCollectionsV1Enabled: boolean,
): boolean {
if (!flexibleCollectionsV1Enabled) {
return false;
}
if (this.isUnassignedCollection) { if (this.isUnassignedCollection) {
return false; return false;
} }

View File

@ -54,10 +54,6 @@ export class BulkDeleteDialogComponent {
collections: CollectionView[]; collections: CollectionView[];
unassignedCiphers: string[]; unassignedCiphers: string[];
private flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
FeatureFlag.FlexibleCollectionsV1,
);
private restrictProviderAccess$ = this.configService.getFeatureFlag$( private restrictProviderAccess$ = this.configService.getFeatureFlag$(
FeatureFlag.RestrictProviderAccess, FeatureFlag.RestrictProviderAccess,
); );
@ -96,13 +92,9 @@ export class BulkDeleteDialogComponent {
deletePromises.push(this.deleteCiphersAdmin(this.unassignedCiphers)); deletePromises.push(this.deleteCiphersAdmin(this.unassignedCiphers));
} }
if (this.cipherIds.length) { if (this.cipherIds.length) {
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
const restrictProviderAccess = await firstValueFrom(this.restrictProviderAccess$); const restrictProviderAccess = await firstValueFrom(this.restrictProviderAccess$);
if ( if (!this.organization || !this.organization.canEditAllCiphers(restrictProviderAccess)) {
!this.organization ||
!this.organization.canEditAllCiphers(flexibleCollectionsV1Enabled, restrictProviderAccess)
) {
deletePromises.push(this.deleteCiphers()); deletePromises.push(this.deleteCiphers());
} else { } else {
deletePromises.push(this.deleteCiphersAdmin(this.cipherIds)); deletePromises.push(this.deleteCiphersAdmin(this.cipherIds));
@ -134,12 +126,8 @@ export class BulkDeleteDialogComponent {
}; };
private async deleteCiphers(): Promise<any> { private async deleteCiphers(): Promise<any> {
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
const restrictProviderAccess = await firstValueFrom(this.restrictProviderAccess$); const restrictProviderAccess = await firstValueFrom(this.restrictProviderAccess$);
const asAdmin = this.organization?.canEditAllCiphers( const asAdmin = this.organization?.canEditAllCiphers(restrictProviderAccess);
flexibleCollectionsV1Enabled,
restrictProviderAccess,
);
if (this.permanent) { if (this.permanent) {
await this.cipherService.deleteManyWithServer(this.cipherIds, asAdmin); await this.cipherService.deleteManyWithServer(this.cipherIds, asAdmin);
} else { } else {
@ -157,12 +145,9 @@ export class BulkDeleteDialogComponent {
} }
private async deleteCollections(): Promise<any> { private async deleteCollections(): Promise<any> {
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
// From org vault // From org vault
if (this.organization) { if (this.organization) {
if ( if (this.collections.some((c) => !c.canDelete(this.organization))) {
this.collections.some((c) => !c.canDelete(this.organization, flexibleCollectionsV1Enabled))
) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),
@ -179,7 +164,7 @@ export class BulkDeleteDialogComponent {
const deletePromises: Promise<any>[] = []; const deletePromises: Promise<any>[] = [];
for (const organization of this.organizations) { for (const organization of this.organizations) {
const orgCollections = this.collections.filter((o) => o.organizationId === organization.id); const orgCollections = this.collections.filter((o) => o.organizationId === organization.id);
if (orgCollections.some((c) => !c.canDelete(organization, flexibleCollectionsV1Enabled))) { if (orgCollections.some((c) => !c.canDelete(organization))) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),

View File

@ -32,13 +32,7 @@
[(ngModel)]="$any(c).checked" [(ngModel)]="$any(c).checked"
name="Collection[{{ i }}].Checked" name="Collection[{{ i }}].Checked"
appStopProp appStopProp
[disabled]=" [disabled]="!c.canEditItems(this.organization, this.restrictProviderAccess)"
!c.canEditItems(
this.organization,
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess
)
"
/> />
{{ c.name }} {{ c.name }}
</td> </td>

View File

@ -50,13 +50,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
} }
check(c: CollectionView, select?: boolean) { check(c: CollectionView, select?: boolean) {
if ( if (!c.canEditItems(this.organization, this.restrictProviderAccess)) {
!c.canEditItems(
this.organization,
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return; return;
} }
(c as any).checked = select == null ? !(c as any).checked : select; (c as any).checked = select == null ? !(c as any).checked : select;

View File

@ -84,17 +84,12 @@ export class VaultHeaderComponent implements OnInit {
/** Emits an event when the delete collection button is clicked in the header */ /** Emits an event when the delete collection button is clicked in the header */
@Output() onDeleteCollection = new EventEmitter<void>(); @Output() onDeleteCollection = new EventEmitter<void>();
private flexibleCollectionsV1Enabled = false;
constructor( constructor(
private i18nService: I18nService, private i18nService: I18nService,
private configService: ConfigService, private configService: ConfigService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
this.flexibleCollectionsV1Enabled = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
);
this.extensionRefreshEnabled = await firstValueFrom( this.extensionRefreshEnabled = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh), this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
); );
@ -180,7 +175,7 @@ export class VaultHeaderComponent implements OnInit {
const organization = this.organizations.find( const organization = this.organizations.find(
(o) => o.id === this.collection?.node.organizationId, (o) => o.id === this.collection?.node.organizationId,
); );
return this.collection.node.canEdit(organization, this.flexibleCollectionsV1Enabled); return this.collection.node.canEdit(organization);
} }
async editCollection(tab: CollectionDialogTabType): Promise<void> { async editCollection(tab: CollectionDialogTabType): Promise<void> {
@ -198,7 +193,7 @@ export class VaultHeaderComponent implements OnInit {
(o) => o.id === this.collection?.node.organizationId, (o) => o.id === this.collection?.node.organizationId,
); );
return this.collection.node.canDelete(organization, this.flexibleCollectionsV1Enabled); return this.collection.node.canDelete(organization);
} }
deleteCollection() { deleteCollection() {

View File

@ -56,7 +56,6 @@
[showAdminActions]="false" [showAdminActions]="false"
[showBulkAddToCollections]="vaultBulkManagementActionEnabled$ | async" [showBulkAddToCollections]="vaultBulkManagementActionEnabled$ | async"
(onEvent)="onVaultItemsEvent($event)" (onEvent)="onVaultItemsEvent($event)"
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled$ | async"
[vaultBulkManagementActionEnabled]="vaultBulkManagementActionEnabled$ | async" [vaultBulkManagementActionEnabled]="vaultBulkManagementActionEnabled$ | async"
> >
</app-vault-items> </app-vault-items>

View File

@ -158,9 +158,6 @@ export class VaultComponent implements OnInit, OnDestroy {
protected selectedCollection: TreeNode<CollectionView> | undefined; protected selectedCollection: TreeNode<CollectionView> | undefined;
protected canCreateCollections = false; protected canCreateCollections = false;
protected currentSearchText$: Observable<string>; protected currentSearchText$: Observable<string>;
protected flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
FeatureFlag.FlexibleCollectionsV1,
);
protected vaultBulkManagementActionEnabled$ = this.configService.getFeatureFlag$( protected vaultBulkManagementActionEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.VaultBulkManagementAction, FeatureFlag.VaultBulkManagementAction,
); );
@ -552,7 +549,7 @@ export class VaultComponent implements OnInit, OnDestroy {
} }
async shareCipher(cipher: CipherView) { async shareCipher(cipher: CipherView) {
if ((await this.flexibleCollectionsV1Enabled()) && cipher.organizationId != null) { if (cipher.organizationId != null) {
// You cannot move ciphers between organizations // You cannot move ciphers between organizations
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
@ -712,8 +709,7 @@ export class VaultComponent implements OnInit, OnDestroy {
async deleteCollection(collection: CollectionView): Promise<void> { async deleteCollection(collection: CollectionView): Promise<void> {
const organization = await this.organizationService.get(collection.organizationId); const organization = await this.organizationService.get(collection.organizationId);
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$); if (!collection.canDelete(organization)) {
if (!collection.canDelete(organization, flexibleCollectionsV1Enabled)) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -811,7 +807,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
if ((await this.flexibleCollectionsV1Enabled()) && !c.edit) { if (!c.edit) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -834,7 +830,7 @@ export class VaultComponent implements OnInit, OnDestroy {
} }
async bulkRestore(ciphers: CipherView[]) { async bulkRestore(ciphers: CipherView[]) {
if ((await this.flexibleCollectionsV1Enabled()) && ciphers.some((c) => !c.edit)) { if (ciphers.some((c) => !c.edit)) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -887,7 +883,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
if ((await this.flexibleCollectionsV1Enabled()) && !c.edit) { if (!c.edit) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -936,19 +932,12 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
const flexibleCollectionsV1Enabled = await this.flexibleCollectionsV1Enabled();
const canDeleteCollections = const canDeleteCollections =
collections == null || collections == null ||
collections.every((c) => collections.every((c) => c.canDelete(organizations.find((o) => o.id == c.organizationId)));
c.canDelete(
organizations.find((o) => o.id == c.organizationId),
flexibleCollectionsV1Enabled,
),
);
const canDeleteCiphers = ciphers == null || ciphers.every((c) => c.edit); const canDeleteCiphers = ciphers == null || ciphers.every((c) => c.edit);
if (flexibleCollectionsV1Enabled && (!canDeleteCollections || !canDeleteCiphers)) { if (!canDeleteCollections || !canDeleteCiphers) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -1052,10 +1041,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
if ( if (ciphers.some((c) => c.organizationId != null)) {
(await this.flexibleCollectionsV1Enabled()) &&
ciphers.some((c) => c.organizationId != null)
) {
// You cannot move ciphers between organizations // You cannot move ciphers between organizations
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
@ -1099,10 +1085,8 @@ export class VaultComponent implements OnInit, OnDestroy {
return true; return true;
} }
const flexibleCollectionsV1Enabled = await this.flexibleCollectionsV1Enabled();
const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId); const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId);
return organization.canEditAllCiphers(flexibleCollectionsV1Enabled, false); return organization.canEditAllCiphers(false);
} }
private go(queryParams: any = null) { private go(queryParams: any = null) {
@ -1131,10 +1115,6 @@ export class VaultComponent implements OnInit, OnDestroy {
message: this.i18nService.t("missingPermissions"), message: this.i18nService.t("missingPermissions"),
}); });
} }
private flexibleCollectionsV1Enabled() {
return firstValueFrom(this.flexibleCollectionsV1Enabled$);
}
} }
/** /**

View File

@ -82,12 +82,7 @@ export class AddEditComponent extends BaseAddEditComponent {
} }
protected loadCollections() { protected loadCollections() {
if ( if (!this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
!this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return super.loadCollections(); return super.loadCollections();
} }
return Promise.resolve(this.collections); return Promise.resolve(this.collections);
@ -98,10 +93,7 @@ export class AddEditComponent extends BaseAddEditComponent {
const firstCipherCheck = await super.loadCipher(); const firstCipherCheck = await super.loadCipher();
if ( if (
!this.organization.canEditAllCiphers( !this.organization.canEditAllCiphers(this.restrictProviderAccess) &&
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
) &&
firstCipherCheck != null firstCipherCheck != null
) { ) {
return firstCipherCheck; return firstCipherCheck;
@ -116,24 +108,14 @@ export class AddEditComponent extends BaseAddEditComponent {
} }
protected encryptCipher() { protected encryptCipher() {
if ( if (!this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
!this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return super.encryptCipher(); return super.encryptCipher();
} }
return this.cipherService.encrypt(this.cipher, null, null, this.originalCipher); return this.cipherService.encrypt(this.cipher, null, null, this.originalCipher);
} }
protected async deleteCipher() { protected async deleteCipher() {
if ( if (!this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
!this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return super.deleteCipher(); return super.deleteCipher();
} }
return this.cipher.isDeleted return this.cipher.isDeleted

View File

@ -28,7 +28,6 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
viewOnly = false; viewOnly = false;
organization: Organization; organization: Organization;
private flexibleCollectionsV1Enabled = false;
private restrictProviderAccess = false; private restrictProviderAccess = false;
constructor( constructor(
@ -60,9 +59,6 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
async ngOnInit() { async ngOnInit() {
await super.ngOnInit(); await super.ngOnInit();
this.flexibleCollectionsV1Enabled = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
);
this.restrictProviderAccess = await firstValueFrom( this.restrictProviderAccess = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.RestrictProviderAccess), this.configService.getFeatureFlag$(FeatureFlag.RestrictProviderAccess),
); );
@ -70,10 +66,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
protected async reupload(attachment: AttachmentView) { protected async reupload(attachment: AttachmentView) {
if ( if (
this.organization.canEditAllCiphers( this.organization.canEditAllCiphers(this.restrictProviderAccess) &&
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
) &&
this.showFixOldAttachments(attachment) this.showFixOldAttachments(attachment)
) { ) {
await super.reuploadCipherAttachment(attachment, true); await super.reuploadCipherAttachment(attachment, true);
@ -81,12 +74,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
} }
protected async loadCipher() { protected async loadCipher() {
if ( if (!this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
!this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return await super.loadCipher(); return await super.loadCipher();
} }
const response = await this.apiService.getCipherAdmin(this.cipherId); const response = await this.apiService.getCipherAdmin(this.cipherId);
@ -97,20 +85,12 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
return this.cipherService.saveAttachmentWithServer( return this.cipherService.saveAttachmentWithServer(
this.cipherDomain, this.cipherDomain,
file, file,
this.organization.canEditAllCiphers( this.organization.canEditAllCiphers(this.restrictProviderAccess),
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
),
); );
} }
protected deleteCipherAttachment(attachmentId: string) { protected deleteCipherAttachment(attachmentId: string) {
if ( if (!this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
!this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return super.deleteCipherAttachment(attachmentId); return super.deleteCipherAttachment(attachmentId);
} }
return this.apiService.deleteCipherAttachmentAdmin(this.cipherId, attachmentId); return this.apiService.deleteCipherAttachmentAdmin(this.cipherId, attachmentId);
@ -118,11 +98,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
protected showFixOldAttachments(attachment: AttachmentView) { protected showFixOldAttachments(attachment: AttachmentView) {
return ( return (
attachment.key == null && attachment.key == null && this.organization.canEditAllCiphers(this.restrictProviderAccess)
this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
); );
} }
} }

View File

@ -61,10 +61,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
protected async loadCipher() { protected async loadCipher() {
// if cipher is unassigned use apiService. We can see this by looking at this.collectionIds // if cipher is unassigned use apiService. We can see this by looking at this.collectionIds
if ( if (
!this.organization.canEditAllCiphers( !this.organization.canEditAllCiphers(this.restrictProviderAccess) &&
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
) &&
this.collectionIds.length !== 0 this.collectionIds.length !== 0
) { ) {
return await super.loadCipher(); return await super.loadCipher();
@ -89,10 +86,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
protected saveCollections() { protected saveCollections() {
if ( if (
this.organization.canEditAllCiphers( this.organization.canEditAllCiphers(this.restrictProviderAccess) ||
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
) ||
this.collectionIds.length === 0 this.collectionIds.length === 0
) { ) {
const request = new CipherCollectionsRequest(this.cipherDomain.collectionIds); const request = new CipherCollectionsRequest(this.cipherDomain.collectionIds);

View File

@ -87,7 +87,6 @@ export class VaultHeaderComponent implements OnInit {
protected CollectionDialogTabType = CollectionDialogTabType; protected CollectionDialogTabType = CollectionDialogTabType;
protected organizations$ = this.organizationService.organizations$; protected organizations$ = this.organizationService.organizations$;
protected flexibleCollectionsV1Enabled = false;
protected restrictProviderAccessFlag = false; protected restrictProviderAccessFlag = false;
constructor( constructor(
@ -100,9 +99,6 @@ export class VaultHeaderComponent implements OnInit {
) {} ) {}
async ngOnInit() { async ngOnInit() {
this.flexibleCollectionsV1Enabled = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
);
this.restrictProviderAccessFlag = await this.configService.getFeatureFlag( this.restrictProviderAccessFlag = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess, FeatureFlag.RestrictProviderAccess,
); );
@ -195,7 +191,7 @@ export class VaultHeaderComponent implements OnInit {
} }
// Otherwise, check if we can edit the specified collection // Otherwise, check if we can edit the specified collection
return this.collection.node.canEdit(this.organization, this.flexibleCollectionsV1Enabled); return this.collection.node.canEdit(this.organization);
} }
addCipher() { addCipher() {
@ -225,14 +221,11 @@ export class VaultHeaderComponent implements OnInit {
} }
// Otherwise, check if we can delete the specified collection // Otherwise, check if we can delete the specified collection
return this.collection.node.canDelete(this.organization, this.flexibleCollectionsV1Enabled); return this.collection.node.canDelete(this.organization);
} }
get canViewCollectionInfo(): boolean { get canViewCollectionInfo(): boolean {
return this.collection.node.canViewCollectionInfo( return this.collection.node.canViewCollectionInfo(this.organization);
this.organization,
this.flexibleCollectionsV1Enabled,
);
} }
get canCreateCollection(): boolean { get canCreateCollection(): boolean {

View File

@ -68,39 +68,12 @@
[showBulkEditCollectionAccess]="true" [showBulkEditCollectionAccess]="true"
[showBulkAddToCollections]="true" [showBulkAddToCollections]="true"
[viewingOrgVault]="true" [viewingOrgVault]="true"
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled"
[addAccessStatus]="addAccessStatus$ | async" [addAccessStatus]="addAccessStatus$ | async"
[addAccessToggle]="showAddAccessToggle" [addAccessToggle]="showAddAccessToggle"
[restrictProviderAccess]="restrictProviderAccessEnabled" [restrictProviderAccess]="restrictProviderAccessEnabled"
> >
</app-vault-items> </app-vault-items>
<ng-container *ngIf="!flexibleCollectionsV1Enabled"> <ng-container *ngIf="!performingInitialLoad && isEmpty">
<div
class="tw-mt-6 tw-flex tw-h-full tw-flex-col tw-items-center tw-justify-start"
*ngIf="showMissingCollectionPermissionMessage"
>
<bit-icon [icon]="noItemIcon" aria-hidden="true"></bit-icon>
<p>{{ "noPermissionToViewAllCollectionItems" | i18n }}</p>
</div>
<div
class="tw-mt-6 tw-flex tw-h-full tw-flex-col tw-items-center tw-justify-start"
*ngIf="isEmpty && !showMissingCollectionPermissionMessage && !performingInitialLoad"
>
<bit-icon [icon]="noItemIcon" aria-hidden="true"></bit-icon>
<p>{{ "noItemsInList" | i18n }}</p>
<button
type="button"
buttonType="primary"
bitButton
(click)="addCipher()"
*ngIf="filter.type !== 'trash' && filter.collectionId !== Unassigned"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "newItem" | i18n }}
</button>
</div></ng-container
>
<ng-container *ngIf="flexibleCollectionsV1Enabled && !performingInitialLoad && isEmpty">
<bit-no-items *ngIf="!showCollectionAccessRestricted"> <bit-no-items *ngIf="!showCollectionAccessRestricted">
<span slot="title" class="tw-mt-4 tw-block">{{ "noItemsInList" | i18n }}</span> <span slot="title" class="tw-mt-4 tw-block">{{ "noItemsInList" | i18n }}</span>
<button <button
@ -116,15 +89,8 @@
</bit-no-items> </bit-no-items>
<collection-access-restricted <collection-access-restricted
*ngIf="showCollectionAccessRestricted" *ngIf="showCollectionAccessRestricted"
[canEditCollection]=" [canEditCollection]="selectedCollection?.node?.canEdit(organization)"
selectedCollection?.node?.canEdit(organization, flexibleCollectionsV1Enabled) [canViewCollectionInfo]="selectedCollection?.node?.canViewCollectionInfo(organization)"
"
[canViewCollectionInfo]="
selectedCollection?.node?.canViewCollectionInfo(
organization,
flexibleCollectionsV1Enabled
)
"
(viewCollectionClicked)=" (viewCollectionClicked)="
editCollection(selectedCollection.node, $event.tab, $event.readonly) editCollection(selectedCollection.node, $event.tab, $event.readonly)
" "

View File

@ -150,12 +150,6 @@ export class VaultComponent implements OnInit, OnDestroy {
protected collections: CollectionAdminView[]; protected collections: CollectionAdminView[];
protected selectedCollection: TreeNode<CollectionAdminView> | undefined; protected selectedCollection: TreeNode<CollectionAdminView> | undefined;
protected isEmpty: boolean; protected isEmpty: boolean;
/**
* Used to show an old missing permission message for custom users with DeleteAnyCollection
* @deprecated Replaced with showCollectionAccessRestricted$ and this should be removed after flexible collections V1
* is released
*/
protected showMissingCollectionPermissionMessage: boolean;
protected showCollectionAccessRestricted: boolean; protected showCollectionAccessRestricted: boolean;
protected currentSearchText$: Observable<string>; protected currentSearchText$: Observable<string>;
/** /**
@ -164,16 +158,12 @@ export class VaultComponent implements OnInit, OnDestroy {
*/ */
protected editableCollections$: Observable<CollectionAdminView[]>; protected editableCollections$: Observable<CollectionAdminView[]>;
protected allCollectionsWithoutUnassigned$: Observable<CollectionAdminView[]>; protected allCollectionsWithoutUnassigned$: Observable<CollectionAdminView[]>;
private _flexibleCollectionsV1FlagEnabled: boolean;
protected get flexibleCollectionsV1Enabled(): boolean {
return this._flexibleCollectionsV1FlagEnabled;
}
protected orgRevokedUsers: OrganizationUserUserDetailsResponse[]; protected orgRevokedUsers: OrganizationUserUserDetailsResponse[];
private _restrictProviderAccessFlagEnabled: boolean; private _restrictProviderAccessFlagEnabled: boolean;
protected get restrictProviderAccessEnabled(): boolean { protected get restrictProviderAccessEnabled(): boolean {
return this._restrictProviderAccessFlagEnabled && this.flexibleCollectionsV1Enabled; return this._restrictProviderAccessFlagEnabled;
} }
protected get hideVaultFilters(): boolean { protected get hideVaultFilters(): boolean {
@ -228,10 +218,6 @@ export class VaultComponent implements OnInit, OnDestroy {
: "trashCleanupWarning", : "trashCleanupWarning",
); );
this._flexibleCollectionsV1FlagEnabled = await this.configService.getFeatureFlag(
FeatureFlag.FlexibleCollectionsV1,
);
this._restrictProviderAccessFlagEnabled = await this.configService.getFeatureFlag( this._restrictProviderAccessFlagEnabled = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess, FeatureFlag.RestrictProviderAccess,
); );
@ -254,7 +240,7 @@ export class VaultComponent implements OnInit, OnDestroy {
switchMap(async ([organization]) => { switchMap(async ([organization]) => {
this.organization = organization; this.organization = organization;
if (!organization.canEditAnyCollection(this.flexibleCollectionsV1Enabled)) { if (!organization.canEditAnyCollection) {
await this.syncService.fullSync(false); await this.syncService.fullSync(false);
} }
@ -327,12 +313,7 @@ export class VaultComponent implements OnInit, OnDestroy {
this.editableCollections$ = this.allCollectionsWithoutUnassigned$.pipe( this.editableCollections$ = this.allCollectionsWithoutUnassigned$.pipe(
map((collections) => { map((collections) => {
// Users that can edit all ciphers can implicitly add to / edit within any collection // Users that can edit all ciphers can implicitly add to / edit within any collection
if ( if (this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled)) {
this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
)
) {
return collections; return collections;
} }
// The user is only allowed to add/edit items to assigned collections that are not readonly // The user is only allowed to add/edit items to assigned collections that are not readonly
@ -377,34 +358,12 @@ export class VaultComponent implements OnInit, OnDestroy {
return []; return [];
} }
if (this.flexibleCollectionsV1Enabled) { // If the user can edit all ciphers for the organization then fetch them ALL.
// Flexible collections V1 logic. if (organization.canEditAllCiphers(this.restrictProviderAccessEnabled)) {
// If the user can edit all ciphers for the organization then fetch them ALL. ciphers = await this.cipherService.getAllFromApiForOrganization(organization.id);
if (
organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
)
) {
ciphers = await this.cipherService.getAllFromApiForOrganization(organization.id);
} else {
// Otherwise, only fetch ciphers they have access to (includes unassigned for admins).
ciphers = await this.cipherService.getManyFromApiForOrganization(organization.id);
}
} else { } else {
// Pre-flexible collections logic, to be removed after flexible collections is fully released // Otherwise, only fetch ciphers they have access to (includes unassigned for admins).
if ( ciphers = await this.cipherService.getManyFromApiForOrganization(organization.id);
organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
)
) {
ciphers = await this.cipherService.getAllFromApiForOrganization(organization.id);
} else {
ciphers = (await this.cipherService.getAllDecrypted()).filter(
(c) => c.organizationId === organization.id,
);
}
} }
await this.searchService.indexCiphers(ciphers, organization.id); await this.searchService.indexCiphers(ciphers, organization.id);
@ -469,9 +428,8 @@ export class VaultComponent implements OnInit, OnDestroy {
// Add access toggle is only shown if allowAdminAccessToAllCollectionItems is false and there are unmanaged collections the user can edit // Add access toggle is only shown if allowAdminAccessToAllCollectionItems is false and there are unmanaged collections the user can edit
this.showAddAccessToggle = this.showAddAccessToggle =
this.flexibleCollectionsV1Enabled &&
!this.organization.allowAdminAccessToAllCollectionItems && !this.organization.allowAdminAccessToAllCollectionItems &&
this.organization.canEditUnmanagedCollections() && this.organization.canEditUnmanagedCollections &&
collectionsToReturn.some((c) => c.unmanaged); collectionsToReturn.some((c) => c.unmanaged);
if (addAccessStatus === 1 && this.showAddAccessToggle) { if (addAccessStatus === 1 && this.showAddAccessToggle) {
@ -508,10 +466,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return ( return (
(filter.collectionId === Unassigned && (filter.collectionId === Unassigned &&
!organization.canEditUnassignedCiphers(this.restrictProviderAccessEnabled)) || !organization.canEditUnassignedCiphers(this.restrictProviderAccessEnabled)) ||
(!organization.canEditAllCiphers( (!organization.canEditAllCiphers(this.restrictProviderAccessEnabled) &&
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
) &&
collection != undefined && collection != undefined &&
!collection.node.assigned) !collection.node.assigned)
); );
@ -531,7 +486,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return []; return [];
} }
if (this.flexibleCollectionsV1Enabled && showCollectionAccessRestricted) { if (showCollectionAccessRestricted) {
// Do not show ciphers for restricted collections // Do not show ciphers for restricted collections
// Ciphers belonging to multiple collections may still be present in $allCiphers and shouldn't be visible // Ciphers belonging to multiple collections may still be present in $allCiphers and shouldn't be visible
return []; return [];
@ -548,25 +503,6 @@ export class VaultComponent implements OnInit, OnDestroy {
shareReplay({ refCount: true, bufferSize: 1 }), shareReplay({ refCount: true, bufferSize: 1 }),
); );
const showMissingCollectionPermissionMessage$ = combineLatest([
filter$,
selectedCollection$,
organization$,
]).pipe(
map(([filter, collection, organization]) => {
return (
// Filtering by unassigned, show message if not admin
(filter.collectionId === Unassigned &&
!organization.canEditUnassignedCiphers(this.restrictProviderAccessEnabled)) ||
// Filtering by a collection, so show message if user is not assigned
(collection != undefined &&
!collection.node.assigned &&
!organization.canEditAnyCollection(this.flexibleCollectionsV1Enabled))
);
}),
shareReplay({ refCount: true, bufferSize: 1 }),
);
firstSetup$ firstSetup$
.pipe( .pipe(
switchMap(() => combineLatest([this.route.queryParams, organization$])), switchMap(() => combineLatest([this.route.queryParams, organization$])),
@ -576,17 +512,9 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
let canEditCipher: boolean; const canEditCipher =
organization.canEditAllCiphers(this.restrictProviderAccessEnabled) ||
if (this.flexibleCollectionsV1Enabled) { (await firstValueFrom(allCipherMap$))[cipherId] != undefined;
canEditCipher =
organization.canEditAllCiphers(true, this.restrictProviderAccessEnabled) ||
(await firstValueFrom(allCipherMap$))[cipherId] != undefined;
} else {
canEditCipher =
organization.canEditAnyCollection(this.flexibleCollectionsV1Enabled) ||
(await this.cipherService.get(cipherId)) != null;
}
if (canEditCipher) { if (canEditCipher) {
await this.editCipherId(cipherId); await this.editCipherId(cipherId);
@ -646,7 +574,6 @@ export class VaultComponent implements OnInit, OnDestroy {
ciphers$, ciphers$,
collections$, collections$,
selectedCollection$, selectedCollection$,
showMissingCollectionPermissionMessage$,
showCollectionAccessRestricted$, showCollectionAccessRestricted$,
]), ]),
), ),
@ -661,7 +588,6 @@ export class VaultComponent implements OnInit, OnDestroy {
ciphers, ciphers,
collections, collections,
selectedCollection, selectedCollection,
showMissingCollectionPermissionMessage,
showCollectionAccessRestricted, showCollectionAccessRestricted,
]) => { ]) => {
this.organization = organization; this.organization = organization;
@ -671,7 +597,6 @@ export class VaultComponent implements OnInit, OnDestroy {
this.ciphers = ciphers; this.ciphers = ciphers;
this.collections = collections; this.collections = collections;
this.selectedCollection = selectedCollection; this.selectedCollection = selectedCollection;
this.showMissingCollectionPermissionMessage = showMissingCollectionPermissionMessage;
this.showCollectionAccessRestricted = showCollectionAccessRestricted; this.showCollectionAccessRestricted = showCollectionAccessRestricted;
this.isEmpty = collections?.length === 0 && ciphers?.length === 0; this.isEmpty = collections?.length === 0 && ciphers?.length === 0;
@ -812,32 +737,28 @@ export class VaultComponent implements OnInit, OnDestroy {
async editCipherCollections(cipher: CipherView) { async editCipherCollections(cipher: CipherView) {
let collections: CollectionAdminView[] = []; let collections: CollectionAdminView[] = [];
if (this.flexibleCollectionsV1Enabled) { // Admins limited to only adding items to collections they have access to.
// V1 limits admins to only adding items to collections they have access to. collections = await firstValueFrom(
collections = await firstValueFrom( this.allCollectionsWithoutUnassigned$.pipe(
this.allCollectionsWithoutUnassigned$.pipe( map((c) => {
map((c) => { return c.sort((a, b) => {
return c.sort((a, b) => { if (
if ( a.canEditItems(this.organization, this.restrictProviderAccessEnabled) &&
a.canEditItems(this.organization, true, this.restrictProviderAccessEnabled) && !b.canEditItems(this.organization, this.restrictProviderAccessEnabled)
!b.canEditItems(this.organization, true, this.restrictProviderAccessEnabled) ) {
) { return -1;
return -1; } else if (
} else if ( !a.canEditItems(this.organization, this.restrictProviderAccessEnabled) &&
!a.canEditItems(this.organization, true, this.restrictProviderAccessEnabled) && b.canEditItems(this.organization, this.restrictProviderAccessEnabled)
b.canEditItems(this.organization, true, this.restrictProviderAccessEnabled) ) {
) { return 1;
return 1; } else {
} else { return a.name.localeCompare(b.name);
return a.name.localeCompare(b.name); }
} });
}); }),
}), ),
), );
);
} else {
collections = await firstValueFrom(this.allCollectionsWithoutUnassigned$);
}
const dialog = openOrgVaultCollectionsDialog(this.dialogService, { const dialog = openOrgVaultCollectionsDialog(this.dialogService, {
data: { data: {
collectionIds: cipher.collectionIds, collectionIds: cipher.collectionIds,
@ -855,14 +776,8 @@ export class VaultComponent implements OnInit, OnDestroy {
async addCipher() { async addCipher() {
let collections: CollectionView[] = []; let collections: CollectionView[] = [];
if (this.flexibleCollectionsV1Enabled) { // Admins limited to only adding items to collections they have access to.
// V1 limits admins to only adding items to collections they have access to. collections = await firstValueFrom(this.editableCollections$);
collections = await firstValueFrom(this.editableCollections$);
} else {
collections = (await firstValueFrom(this.vaultFilterService.filteredCollections$)).filter(
(c) => !c.readOnly && c.id != Unassigned,
);
}
await this.editCipher(null, (comp) => { await this.editCipher(null, (comp) => {
comp.type = this.activeFilter.cipherType; comp.type = this.activeFilter.cipherType;
@ -954,14 +869,8 @@ export class VaultComponent implements OnInit, OnDestroy {
let collections: CollectionView[] = []; let collections: CollectionView[] = [];
if (this.flexibleCollectionsV1Enabled) { // Admins limited to only adding items to collections they have access to.
// V1 limits admins to only adding items to collections they have access to. collections = await firstValueFrom(this.editableCollections$);
collections = await firstValueFrom(this.editableCollections$);
} else {
collections = (await firstValueFrom(this.vaultFilterService.filteredCollections$)).filter(
(c) => !c.readOnly && c.id != Unassigned,
);
}
await this.editCipher(cipher, (comp) => { await this.editCipher(cipher, (comp) => {
comp.cloneMode = true; comp.cloneMode = true;
@ -977,7 +886,6 @@ export class VaultComponent implements OnInit, OnDestroy {
if ( if (
!this.organization.permissions.editAnyCollection && !this.organization.permissions.editAnyCollection &&
this.flexibleCollectionsV1Enabled &&
!c.edit && !c.edit &&
!this.organization.allowAdminAccessToAllCollectionItems !this.organization.allowAdminAccessToAllCollectionItems
) { ) {
@ -991,9 +899,7 @@ export class VaultComponent implements OnInit, OnDestroy {
// Allow restore of an Unassigned Item // Allow restore of an Unassigned Item
try { try {
const asAdmin = const asAdmin = this.organization?.canEditAnyCollection || c.isUnassigned;
this.organization?.canEditAnyCollection(this.flexibleCollectionsV1Enabled) ||
c.isUnassigned;
await this.cipherService.restoreWithServer(c.id, asAdmin); await this.cipherService.restoreWithServer(c.id, asAdmin);
this.toastService.showToast({ this.toastService.showToast({
variant: "success", variant: "success",
@ -1009,7 +915,6 @@ export class VaultComponent implements OnInit, OnDestroy {
async bulkRestore(ciphers: CipherView[]) { async bulkRestore(ciphers: CipherView[]) {
if ( if (
!this.organization.permissions.editAnyCollection && !this.organization.permissions.editAnyCollection &&
this.flexibleCollectionsV1Enabled &&
ciphers.some((c) => !c.edit && !this.organization.allowAdminAccessToAllCollectionItems) ciphers.some((c) => !c.edit && !this.organization.allowAdminAccessToAllCollectionItems)
) { ) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
@ -1025,10 +930,7 @@ export class VaultComponent implements OnInit, OnDestroy {
const unassignedCiphers: string[] = []; const unassignedCiphers: string[] = [];
// If user has edit all Access no need to check for unassigned ciphers // If user has edit all Access no need to check for unassigned ciphers
const canEditAll = this.organization.canEditAllCiphers( const canEditAll = this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled);
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
);
if (canEditAll) { if (canEditAll) {
ciphers.map((cipher) => { ciphers.map((cipher) => {
@ -1069,14 +971,7 @@ export class VaultComponent implements OnInit, OnDestroy {
} }
async deleteCipher(c: CipherView): Promise<boolean> { async deleteCipher(c: CipherView): Promise<boolean> {
if ( if (!c.edit && !this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled)) {
this.flexibleCollectionsV1Enabled &&
!c.edit &&
!this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
)
) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -1111,7 +1006,7 @@ export class VaultComponent implements OnInit, OnDestroy {
} }
async deleteCollection(collection: CollectionAdminView): Promise<void> { async deleteCollection(collection: CollectionAdminView): Promise<void> {
if (!collection.canDelete(this.organization, this.flexibleCollectionsV1Enabled)) { if (!collection.canDelete(this.organization)) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -1178,17 +1073,13 @@ export class VaultComponent implements OnInit, OnDestroy {
} }
const canDeleteCollections = const canDeleteCollections =
collections == null || collections == null || collections.every((c) => c.canDelete(organization));
collections.every((c) => c.canDelete(organization, this.flexibleCollectionsV1Enabled));
const canDeleteCiphers = const canDeleteCiphers =
ciphers == null || ciphers == null ||
ciphers.every((c) => c.edit) || ciphers.every((c) => c.edit) ||
this.organization.canEditAllCiphers( this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled);
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
);
if (this.flexibleCollectionsV1Enabled && (!canDeleteCiphers || !canDeleteCollections)) { if (!canDeleteCiphers || !canDeleteCollections) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -1268,9 +1159,7 @@ export class VaultComponent implements OnInit, OnDestroy {
data: { data: {
organizationId: this.organization?.id, organizationId: this.organization?.id,
parentCollectionId: this.selectedCollection?.node.id, parentCollectionId: this.selectedCollection?.node.id,
limitNestedCollections: !this.organization.canEditAnyCollection( limitNestedCollections: !this.organization.canEditAnyCollection,
this.flexibleCollectionsV1Enabled,
),
}, },
}); });
@ -1295,9 +1184,7 @@ export class VaultComponent implements OnInit, OnDestroy {
initialTab: tab, initialTab: tab,
readonly: readonly, readonly: readonly,
isAddAccessCollection: c.unmanaged, isAddAccessCollection: c.unmanaged,
limitNestedCollections: !this.organization.canEditAnyCollection( limitNestedCollections: !this.organization.canEditAnyCollection,
this.flexibleCollectionsV1Enabled,
),
}, },
}); });
@ -1335,10 +1222,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
if ( if (collections.some((c) => !c.canEdit(organization))) {
this.flexibleCollectionsV1Enabled &&
collections.some((c) => !c.canEdit(organization, this.flexibleCollectionsV1Enabled))
) {
this.showMissingPermissionsError(); this.showMissingPermissionsError();
return; return;
} }
@ -1366,15 +1250,7 @@ export class VaultComponent implements OnInit, OnDestroy {
return; return;
} }
let availableCollections: CollectionView[]; const availableCollections = await firstValueFrom(this.editableCollections$);
if (this.flexibleCollectionsV1Enabled) {
availableCollections = await firstValueFrom(this.editableCollections$);
} else {
availableCollections = (
await firstValueFrom(this.vaultFilterService.filteredCollections$)
).filter((c) => c.id != Unassigned);
}
const dialog = AssignCollectionsWebComponent.open(this.dialogService, { const dialog = AssignCollectionsWebComponent.open(this.dialogService, {
data: { data: {
@ -1405,10 +1281,7 @@ export class VaultComponent implements OnInit, OnDestroy {
protected deleteCipherWithServer(id: string, permanent: boolean, isUnassigned: boolean) { protected deleteCipherWithServer(id: string, permanent: boolean, isUnassigned: boolean) {
const asAdmin = const asAdmin =
this.organization?.canEditAllCiphers( this.organization?.canEditAllCiphers(this.restrictProviderAccessEnabled) || isUnassigned;
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccessEnabled,
) || isUnassigned;
return permanent return permanent
? this.cipherService.deleteWithServer(id, asAdmin) ? this.cipherService.deleteWithServer(id, asAdmin)
: this.cipherService.softDeleteWithServer(id, asAdmin); : this.cipherService.softDeleteWithServer(id, asAdmin);

View File

@ -24,7 +24,6 @@ export class CollectionsComponent implements OnInit {
collectionIds: string[]; collectionIds: string[];
collections: CollectionView[] = []; collections: CollectionView[] = [];
organization: Organization; organization: Organization;
flexibleCollectionsV1Enabled: boolean;
restrictProviderAccess: boolean; restrictProviderAccess: boolean;
protected cipherDomain: Cipher; protected cipherDomain: Cipher;
@ -40,9 +39,6 @@ export class CollectionsComponent implements OnInit {
) {} ) {}
async ngOnInit() { async ngOnInit() {
this.flexibleCollectionsV1Enabled = await this.configService.getFeatureFlag(
FeatureFlag.FlexibleCollectionsV1,
);
this.restrictProviderAccess = await this.configService.getFeatureFlag( this.restrictProviderAccess = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess, FeatureFlag.RestrictProviderAccess,
); );
@ -72,12 +68,7 @@ export class CollectionsComponent implements OnInit {
async submit(): Promise<boolean> { async submit(): Promise<boolean> {
const selectedCollectionIds = this.collections const selectedCollectionIds = this.collections
.filter((c) => { .filter((c) => {
if ( if (this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
this.organization.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
)
) {
return !!(c as any).checked; return !!(c as any).checked;
} else { } else {
return !!(c as any).checked && c.readOnly == null; return !!(c as any).checked && c.readOnly == null;

View File

@ -90,7 +90,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
private personalOwnershipPolicyAppliesToActiveUser: boolean; private personalOwnershipPolicyAppliesToActiveUser: boolean;
private previousCipherId: string; private previousCipherId: string;
protected flexibleCollectionsV1Enabled = false;
protected restrictProviderAccess = false; protected restrictProviderAccess = false;
get fido2CredentialCreationDateValue(): string { get fido2CredentialCreationDateValue(): string {
@ -181,9 +180,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
} }
async ngOnInit() { async ngOnInit() {
this.flexibleCollectionsV1Enabled = await this.configService.getFeatureFlag(
FeatureFlag.FlexibleCollectionsV1,
);
this.restrictProviderAccess = await this.configService.getFeatureFlag( this.restrictProviderAccess = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess, FeatureFlag.RestrictProviderAccess,
); );
@ -674,10 +670,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
protected saveCipher(cipher: Cipher) { protected saveCipher(cipher: Cipher) {
const isNotClone = this.editMode && !this.cloneMode; const isNotClone = this.editMode && !this.cloneMode;
let orgAdmin = this.organization?.canEditAllCiphers( let orgAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess);
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
);
// if a cipher is unassigned we want to check if they are an admin or have permission to edit any collection // if a cipher is unassigned we want to check if they are an admin or have permission to edit any collection
if (!cipher.collectionIds) { if (!cipher.collectionIds) {
@ -690,20 +683,14 @@ export class AddEditComponent implements OnInit, OnDestroy {
} }
protected deleteCipher() { protected deleteCipher() {
const asAdmin = this.organization?.canEditAllCiphers( const asAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess);
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
);
return this.cipher.isDeleted return this.cipher.isDeleted
? this.cipherService.deleteWithServer(this.cipher.id, asAdmin) ? this.cipherService.deleteWithServer(this.cipher.id, asAdmin)
: this.cipherService.softDeleteWithServer(this.cipher.id, asAdmin); : this.cipherService.softDeleteWithServer(this.cipher.id, asAdmin);
} }
protected restoreCipher() { protected restoreCipher() {
const asAdmin = this.organization?.canEditAllCiphers( const asAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess);
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
);
return this.cipherService.restoreWithServer(this.cipher.id, asAdmin); return this.cipherService.restoreWithServer(this.cipher.id, asAdmin);
} }

View File

@ -168,13 +168,8 @@ export class Organization {
); );
} }
canEditAnyCollection(flexibleCollectionsV1Enabled: boolean) { get canEditAnyCollection() {
if (!flexibleCollectionsV1Enabled) { // The allowAdminAccessToAllCollectionItems flag can restrict admins
// Pre-Flexible Collections v1 logic
return this.isAdmin || this.permissions.editAnyCollection;
}
// Post Flexible Collections V1, the allowAdminAccessToAllCollectionItems flag can restrict admins
// Providers and custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag // Providers and custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag
return ( return (
this.isProviderUser || this.isProviderUser ||
@ -183,7 +178,7 @@ export class Organization {
); );
} }
canEditUnmanagedCollections() { get canEditUnmanagedCollections() {
// Any admin or custom user with editAnyCollection permission can edit unmanaged collections // Any admin or custom user with editAnyCollection permission can edit unmanaged collections
return this.isAdmin || this.permissions.editAnyCollection; return this.isAdmin || this.permissions.editAnyCollection;
} }
@ -203,15 +198,7 @@ export class Organization {
); );
} }
canEditAllCiphers( canEditAllCiphers(restrictProviderAccessFlagEnabled: boolean) {
flexibleCollectionsV1Enabled: boolean,
restrictProviderAccessFlagEnabled: boolean,
) {
// Before Flexible Collections V1, any admin or anyone with editAnyCollection permission could edit all ciphers
if (!flexibleCollectionsV1Enabled) {
return this.isAdmin || this.permissions.editAnyCollection;
}
// Providers can access items until the restrictProviderAccess flag is enabled // Providers can access items until the restrictProviderAccess flag is enabled
// After the flag is enabled and removed, this block will be deleted // After the flag is enabled and removed, this block will be deleted
// so that they permanently lose access to items // so that they permanently lose access to items
@ -219,7 +206,7 @@ export class Organization {
return true; return true;
} }
// Post Flexible Collections V1, the allowAdminAccessToAllCollectionItems flag can restrict admins // The allowAdminAccessToAllCollectionItems flag can restrict admins
// Custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag // Custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag
return ( return (
(this.type === OrganizationUserType.Custom && this.permissions.editAnyCollection) || (this.type === OrganizationUserType.Custom && this.permissions.editAnyCollection) ||
@ -229,10 +216,9 @@ export class Organization {
} }
/** /**
* @param flexibleCollectionsV1Enabled - Whether or not the V1 Flexible Collection feature flag is enabled
* @returns True if the user can delete any collection * @returns True if the user can delete any collection
*/ */
canDeleteAnyCollection(flexibleCollectionsV1Enabled: boolean) { get canDeleteAnyCollection() {
// Providers and Users with DeleteAnyCollection permission can always delete collections // Providers and Users with DeleteAnyCollection permission can always delete collections
if (this.isProviderUser || this.permissions.deleteAnyCollection) { if (this.isProviderUser || this.permissions.deleteAnyCollection) {
return true; return true;
@ -240,7 +226,7 @@ export class Organization {
// If AllowAdminAccessToAllCollectionItems is true, Owners and Admins can delete any collection, regardless of LimitCollectionCreationDeletion setting // If AllowAdminAccessToAllCollectionItems is true, Owners and Admins can delete any collection, regardless of LimitCollectionCreationDeletion setting
// Using explicit type checks because provider users are handled above and this mimics the server's permission checks closely // Using explicit type checks because provider users are handled above and this mimics the server's permission checks closely
if (!flexibleCollectionsV1Enabled || this.allowAdminAccessToAllCollectionItems) { if (this.allowAdminAccessToAllCollectionItems) {
return this.type == OrganizationUserType.Owner || this.type == OrganizationUserType.Admin; return this.type == OrganizationUserType.Owner || this.type == OrganizationUserType.Admin;
} }

View File

@ -38,11 +38,7 @@ export class CollectionView implements View, ITreeNodeObject {
} }
} }
canEditItems( canEditItems(org: Organization, restrictProviderAccess: boolean): boolean {
org: Organization,
v1FlexibleCollections: boolean,
restrictProviderAccess: boolean,
): boolean {
if (org != null && org.id !== this.organizationId) { if (org != null && org.id !== this.organizationId) {
throw new Error( throw new Error(
"Id of the organization provided does not match the org id of the collection.", "Id of the organization provided does not match the org id of the collection.",
@ -50,7 +46,7 @@ export class CollectionView implements View, ITreeNodeObject {
} }
return ( return (
org?.canEditAllCiphers(v1FlexibleCollections, restrictProviderAccess) || org?.canEditAllCiphers(restrictProviderAccess) ||
this.manage || this.manage ||
(this.assigned && !this.readOnly) (this.assigned && !this.readOnly)
); );
@ -58,28 +54,23 @@ export class CollectionView implements View, ITreeNodeObject {
/** /**
* Returns true if the user can edit a collection (including user and group access) from the individual vault. * Returns true if the user can edit a collection (including user and group access) from the individual vault.
* After FCv1, does not include admin permissions - see {@link CollectionAdminView.canEdit}. * Does not include admin permissions - see {@link CollectionAdminView.canEdit}.
*/ */
canEdit(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { canEdit(org: Organization): boolean {
if (org != null && org.id !== this.organizationId) { if (org != null && org.id !== this.organizationId) {
throw new Error( throw new Error(
"Id of the organization provided does not match the org id of the collection.", "Id of the organization provided does not match the org id of the collection.",
); );
} }
if (flexibleCollectionsV1Enabled) { return this.manage;
// Only use individual permissions, not admin permissions
return this.manage;
}
return org?.canEditAnyCollection(flexibleCollectionsV1Enabled) || this.manage;
} }
/** /**
* Returns true if the user can delete a collection from the individual vault. * Returns true if the user can delete a collection from the individual vault.
* After FCv1, does not include admin permissions - see {@link CollectionAdminView.canDelete}. * Does not include admin permissions - see {@link CollectionAdminView.canDelete}.
*/ */
canDelete(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { canDelete(org: Organization): boolean {
if (org != null && org.id !== this.organizationId) { if (org != null && org.id !== this.organizationId) {
throw new Error( throw new Error(
"Id of the organization provided does not match the org id of the collection.", "Id of the organization provided does not match the org id of the collection.",
@ -88,24 +79,14 @@ export class CollectionView implements View, ITreeNodeObject {
const canDeleteManagedCollections = !org?.limitCollectionCreationDeletion || org.isAdmin; const canDeleteManagedCollections = !org?.limitCollectionCreationDeletion || org.isAdmin;
if (flexibleCollectionsV1Enabled) { // Only use individual permissions, not admin permissions
// Only use individual permissions, not admin permissions return canDeleteManagedCollections && this.manage;
return canDeleteManagedCollections && this.manage;
}
return (
org?.canDeleteAnyCollection(flexibleCollectionsV1Enabled) ||
(canDeleteManagedCollections && this.manage)
);
} }
/** /**
* Returns true if the user can view collection info and access in a read-only state from the individual vault * Returns true if the user can view collection info and access in a read-only state from the individual vault
*/ */
canViewCollectionInfo( canViewCollectionInfo(org: Organization | undefined): boolean {
org: Organization | undefined,
flexibleCollectionsV1Enabled: boolean,
): boolean {
return false; return false;
} }

View File

@ -175,7 +175,6 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
) {} ) {}
async ngOnInit() { async ngOnInit() {
const v1FCEnabled = await this.configService.getFeatureFlag(FeatureFlag.FlexibleCollectionsV1);
const restrictProviderAccess = await this.configService.getFeatureFlag( const restrictProviderAccess = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess, FeatureFlag.RestrictProviderAccess,
); );
@ -186,7 +185,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
this.showOrgSelector = true; this.showOrgSelector = true;
} }
await this.initializeItems(this.selectedOrgId, v1FCEnabled, restrictProviderAccess); await this.initializeItems(this.selectedOrgId, restrictProviderAccess);
if (this.selectedOrgId && this.selectedOrgId !== MY_VAULT_ID) { if (this.selectedOrgId && this.selectedOrgId !== MY_VAULT_ID) {
await this.handleOrganizationCiphers(); await this.handleOrganizationCiphers();
@ -332,11 +331,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
} }
} }
private async initializeItems( private async initializeItems(organizationId: OrganizationId, restrictProviderAccess: boolean) {
organizationId: OrganizationId,
v1FCEnabled: boolean,
restrictProviderAccess: boolean,
) {
this.totalItemCount = this.params.ciphers.length; this.totalItemCount = this.params.ciphers.length;
// If organizationId is not present or organizationId is MyVault, then all ciphers are considered personal items // If organizationId is not present or organizationId is MyVault, then all ciphers are considered personal items
@ -351,7 +346,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
const org = await this.organizationService.get(organizationId); const org = await this.organizationService.get(organizationId);
this.orgName = org.name; this.orgName = org.name;
this.editableItems = org.canEditAllCiphers(v1FCEnabled, restrictProviderAccess) this.editableItems = org.canEditAllCiphers(restrictProviderAccess)
? this.params.ciphers ? this.params.ciphers
: this.params.ciphers.filter((c) => c.edit); : this.params.ciphers.filter((c) => c.edit);