mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-13 10:24:20 +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:
parent
43da67ee51
commit
471dd3bd7b
@ -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,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
) {
|
) {
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)"
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
|
@ -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>
|
||||||
|
@ -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$);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
"
|
"
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user