mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 05:43:41 +00:00
[AC-2027] Update Flexible Collections logic to use organization property (#7445)
* Remove unused feature flag * Replace feature flag ref with org flag * Remove deprecated feature flag to discourage use * Add check to org.canCreateNewCollections * Adjust init logic of components to avoid race conditions * Make canCreateNewCollections logic more explicit * Resolve merge conflicts with vault changes * Update comments * Remove uses of old feature flag * Remove last of old feature flag * Clean up feature flag * Fix linting * Fix linting
This commit is contained in:
@@ -2,7 +2,6 @@ import { Injectable } from "@angular/core";
|
|||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||||
|
|
||||||
@@ -22,23 +21,6 @@ export class GroupService {
|
|||||||
protected configService: ConfigServiceAbstraction,
|
protected configService: ConfigServiceAbstraction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: This should be replaced with `GroupView.fromResponse` when `FeatureFlag.FlexibleCollections` is removed.
|
|
||||||
**/
|
|
||||||
protected async groupViewFromResponse(response: GroupResponse): Promise<GroupView> {
|
|
||||||
const view = GroupView.fromResponse(response);
|
|
||||||
|
|
||||||
const hasFlexibleCollections = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
if (hasFlexibleCollections) {
|
|
||||||
view.accessAll = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get(orgId: string, groupId: string): Promise<GroupView> {
|
async get(orgId: string, groupId: string): Promise<GroupView> {
|
||||||
const r = await this.apiService.send(
|
const r = await this.apiService.send(
|
||||||
"GET",
|
"GET",
|
||||||
@@ -48,7 +30,7 @@ export class GroupService {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.groupViewFromResponse(new GroupDetailsResponse(r));
|
return GroupView.fromResponse(new GroupDetailsResponse(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(orgId: string): Promise<GroupView[]> {
|
async getAll(orgId: string): Promise<GroupView[]> {
|
||||||
@@ -62,7 +44,7 @@ export class GroupService {
|
|||||||
|
|
||||||
const listResponse = new ListResponse(r, GroupDetailsResponse);
|
const listResponse = new ListResponse(r, GroupDetailsResponse);
|
||||||
|
|
||||||
return Promise.all(listResponse.data?.map((gr) => this.groupViewFromResponse(gr))) ?? [];
|
return Promise.all(listResponse.data?.map((gr) => GroupView.fromResponse(gr))) ?? [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +101,7 @@ export class InternalGroupService extends GroupService {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
return this.groupViewFromResponse(new GroupResponse(r));
|
return GroupView.fromResponse(new GroupResponse(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async putGroup(
|
private async putGroup(
|
||||||
@@ -134,6 +116,6 @@ export class InternalGroupService extends GroupService {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
return this.groupViewFromResponse(new GroupResponse(r));
|
return GroupView.fromResponse(new GroupResponse(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export class GroupResponse extends BaseResponse {
|
|||||||
name: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* To be removed alongside `FeatureFlag.FlexibleCollections`.
|
* To be removed after Flexible Collections.
|
||||||
**/
|
**/
|
||||||
accessAll: boolean;
|
accessAll: boolean;
|
||||||
externalId: string;
|
externalId: string;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
OrganizationUserUpdateRequest,
|
OrganizationUserUpdateRequest,
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
} from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
||||||
import { OrganizationUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
import { OrganizationUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||||
|
|
||||||
import { CoreOrganizationModule } from "../core-organization.module";
|
import { CoreOrganizationModule } from "../core-organization.module";
|
||||||
@@ -78,12 +77,7 @@ export class UserAdminService {
|
|||||||
view.type = u.type;
|
view.type = u.type;
|
||||||
view.status = u.status;
|
view.status = u.status;
|
||||||
view.externalId = u.externalId;
|
view.externalId = u.externalId;
|
||||||
view.accessAll = (await this.configService.getFeatureFlag(
|
view.accessAll = u.accessAll;
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
))
|
|
||||||
? false
|
|
||||||
: u.accessAll;
|
|
||||||
view.permissions = u.permissions;
|
view.permissions = u.permissions;
|
||||||
view.resetPasswordEnrolled = u.resetPasswordEnrolled;
|
view.resetPasswordEnrolled = u.resetPasswordEnrolled;
|
||||||
view.collections = u.collections.map((c) => ({
|
view.collections = u.collections.map((c) => ({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class GroupView implements View {
|
|||||||
name: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* To be removed alongside `FeatureFlag.FlexibleCollections`.
|
* To be removed after Flexible Collections.
|
||||||
* This will always return `false` if Flexible Collections is enabled.
|
* This will always return `false` if Flexible Collections is enabled.
|
||||||
**/
|
**/
|
||||||
accessAll: boolean;
|
accessAll: boolean;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class OrganizationUserAdminView {
|
|||||||
externalId: string;
|
externalId: string;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* To be removed alongside `FeatureFlag.FlexibleCollections`.
|
* To be removed after Flexible Collections.
|
||||||
* This will always return `false` if Flexible Collections is enabled.
|
* This will always return `false` if Flexible Collections is enabled.
|
||||||
**/
|
**/
|
||||||
accessAll: boolean;
|
accessAll: boolean;
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ export class OrganizationUserView {
|
|||||||
status: OrganizationUserStatusType;
|
status: OrganizationUserStatusType;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* To be removed alongside `FeatureFlag.FlexibleCollections`.
|
* To be removed after Flexible Collections.
|
||||||
*
|
|
||||||
* This will always return `false` if Flexible Collections is enabled.
|
* This will always return `false` if Flexible Collections is enabled.
|
||||||
**/
|
**/
|
||||||
accessAll: boolean;
|
accessAll: boolean;
|
||||||
|
|||||||
@@ -8,10 +8,7 @@
|
|||||||
></app-organization-switcher>
|
></app-organization-switcher>
|
||||||
<bit-tab-nav-bar class="-tw-mb-px">
|
<bit-tab-nav-bar class="-tw-mb-px">
|
||||||
<bit-tab-link
|
<bit-tab-link
|
||||||
*ngIf="
|
*ngIf="canShowVaultTab(organization) && organization.flexibleCollections; else vaultTab"
|
||||||
canShowVaultTab(organization) && (flexibleCollectionsEnabled$ | async);
|
|
||||||
else vaultTab
|
|
||||||
"
|
|
||||||
route="vault"
|
route="vault"
|
||||||
>{{ "collections" | i18n }}</bit-tab-link
|
>{{ "collections" | i18n }}</bit-tab-link
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import {
|
|||||||
OrganizationService,
|
OrganizationService,
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-organization-layout",
|
selector: "app-organization-layout",
|
||||||
@@ -25,15 +23,9 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private _destroy = new Subject<void>();
|
private _destroy = new Subject<void>();
|
||||||
|
|
||||||
protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import { FormBuilder, Validators } from "@angular/forms";
|
|||||||
import { catchError, combineLatest, from, map, of, Subject, switchMap, takeUntil } from "rxjs";
|
import { catchError, combineLatest, from, map, of, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.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 { 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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
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";
|
||||||
@@ -80,10 +79,9 @@ export const openGroupAddEditDialog = (
|
|||||||
templateUrl: "group-add-edit.component.html",
|
templateUrl: "group-add-edit.component.html",
|
||||||
})
|
})
|
||||||
export class GroupAddEditComponent implements OnInit, OnDestroy {
|
export class GroupAddEditComponent implements OnInit, OnDestroy {
|
||||||
protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
protected flexibleCollectionsEnabled$ = this.organizationService
|
||||||
FeatureFlag.FlexibleCollections,
|
.get$(this.organizationId)
|
||||||
false,
|
.pipe(map((o) => o?.flexibleCollections));
|
||||||
);
|
|
||||||
|
|
||||||
protected PermissionMode = PermissionMode;
|
protected PermissionMode = PermissionMode;
|
||||||
protected ResultType = GroupAddEditDialogResultType;
|
protected ResultType = GroupAddEditDialogResultType;
|
||||||
@@ -189,7 +187,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private configService: ConfigServiceAbstraction,
|
private organizationService: OrganizationService,
|
||||||
) {
|
) {
|
||||||
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
|
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,10 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div *ngIf="!flexibleCollectionsEnabled" class="tw-mb-2 tw-flex tw-items-baseline">
|
||||||
*ngIf="!(flexibleCollectionsEnabled$ | async)"
|
|
||||||
class="tw-mb-2 tw-flex tw-items-baseline"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
id="userTypeManager"
|
id="userTypeManager"
|
||||||
@@ -141,7 +138,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<ng-container *ngIf="customUserTypeSelected">
|
<ng-container *ngIf="customUserTypeSelected">
|
||||||
<ng-container *ngIf="!(flexibleCollectionsEnabled$ | async); else customPermissionsFC">
|
<ng-container *ngIf="!flexibleCollectionsEnabled; else customPermissionsFC">
|
||||||
<h3 class="mt-4 d-flex tw-font-semibold">
|
<h3 class="mt-4 d-flex tw-font-semibold">
|
||||||
{{ "permissions" | i18n }}
|
{{ "permissions" | i18n }}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -404,14 +401,14 @@
|
|||||||
[columnHeader]="'groups' | i18n"
|
[columnHeader]="'groups' | i18n"
|
||||||
[selectorLabelText]="'selectGroups' | i18n"
|
[selectorLabelText]="'selectGroups' | i18n"
|
||||||
[emptySelectionText]="'noGroupsAdded' | i18n"
|
[emptySelectionText]="'noGroupsAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled"
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
<bit-tab [label]="'collections' | i18n">
|
<bit-tab [label]="'collections' | i18n">
|
||||||
<div *ngIf="organization.useGroups" class="tw-mb-6">
|
<div *ngIf="organization.useGroups" class="tw-mb-6">
|
||||||
{{ "userPermissionOverrideHelper" | i18n }}
|
{{ "userPermissionOverrideHelper" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!(flexibleCollectionsEnabled$ | async)" class="tw-mb-6">
|
<div *ngIf="!flexibleCollectionsEnabled" class="tw-mb-6">
|
||||||
<bit-form-control>
|
<bit-form-control>
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessAllCollections" />
|
<input type="checkbox" bitCheckbox formControlName="accessAllCollections" />
|
||||||
<bit-label>
|
<bit-label>
|
||||||
@@ -437,7 +434,7 @@
|
|||||||
[columnHeader]="'collection' | i18n"
|
[columnHeader]="'collection' | i18n"
|
||||||
[selectorLabelText]="'selectCollections' | i18n"
|
[selectorLabelText]="'selectCollections' | i18n"
|
||||||
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled"
|
||||||
></bit-access-selector
|
></bit-access-selector
|
||||||
></bit-tab>
|
></bit-tab>
|
||||||
</bit-tab-group>
|
</bit-tab-group>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api";
|
import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { ProductType } from "@bitwarden/common/enums";
|
import { ProductType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||||
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";
|
||||||
@@ -68,11 +67,6 @@ export enum MemberDialogResult {
|
|||||||
templateUrl: "member-dialog.component.html",
|
templateUrl: "member-dialog.component.html",
|
||||||
})
|
})
|
||||||
export class MemberDialogComponent implements OnInit, OnDestroy {
|
export class MemberDialogComponent implements OnInit, OnDestroy {
|
||||||
protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
editMode = false;
|
editMode = false;
|
||||||
isRevoked = false;
|
isRevoked = false;
|
||||||
@@ -521,6 +515,10 @@ export class MemberDialogComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get flexibleCollectionsEnabled() {
|
||||||
|
return this.organization?.flexibleCollections;
|
||||||
|
}
|
||||||
|
|
||||||
protected readonly ProductType = ProductType;
|
protected readonly ProductType = ProductType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ import {
|
|||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
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 { ProductType } from "@bitwarden/common/enums";
|
import { ProductType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
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 { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@@ -126,7 +124,6 @@ export class PeopleComponent
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
private groupService: GroupService,
|
private groupService: GroupService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
apiService,
|
apiService,
|
||||||
@@ -244,18 +241,9 @@ export class PeopleComponent
|
|||||||
collectionsPromise,
|
collectionsPromise,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
return usersResponse.data?.map<OrganizationUserView>((r) => {
|
return usersResponse.data?.map<OrganizationUserView>((r) => {
|
||||||
const userView = OrganizationUserView.fromResponse(r);
|
const userView = OrganizationUserView.fromResponse(r);
|
||||||
|
|
||||||
if (flexibleCollectionsEnabled) {
|
|
||||||
userView.accessAll = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
userView.groupNames = userView.groups
|
userView.groupNames = userView.groups
|
||||||
.map((g) => groupNamesMap.get(g))
|
.map((g) => groupNamesMap.get(g))
|
||||||
.sort(this.i18nService.collator?.compare);
|
.sort(this.i18nService.collator?.compare);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<form
|
<form
|
||||||
*ngIf="org && !loading && (flexibleCollectionsEnabled$ | async)"
|
*ngIf="org && !loading && org.flexibleCollections"
|
||||||
[bitSubmit]="submitCollectionManagement"
|
[bitSubmit]="submitCollectionManagement"
|
||||||
[formGroup]="collectionManagementFormGroup"
|
[formGroup]="collectionManagementFormGroup"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ export class AccountComponent {
|
|||||||
canUseApi = false;
|
canUseApi = false;
|
||||||
org: OrganizationResponse;
|
org: OrganizationResponse;
|
||||||
taxFormPromise: Promise<unknown>;
|
taxFormPromise: Promise<unknown>;
|
||||||
flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.FlexibleCollectionsV1,
|
FeatureFlag.FlexibleCollectionsV1,
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { UntypedFormBuilder } from "@angular/forms";
|
import { UntypedFormBuilder } from "@angular/forms";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { EventType } from "@bitwarden/common/enums";
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.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";
|
||||||
@@ -36,7 +36,6 @@ export class OrganizationVaultExportComponent extends ExportComponent {
|
|||||||
fileDownloadService: FileDownloadService,
|
fileDownloadService: FileDownloadService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
organizationService: OrganizationService,
|
organizationService: OrganizationService,
|
||||||
configService: ConfigServiceAbstraction,
|
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
@@ -50,7 +49,6 @@ export class OrganizationVaultExportComponent extends ExportComponent {
|
|||||||
fileDownloadService,
|
fileDownloadService,
|
||||||
dialogService,
|
dialogService,
|
||||||
organizationService,
|
organizationService,
|
||||||
configService,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +61,12 @@ export class OrganizationVaultExportComponent extends ExportComponent {
|
|||||||
this.route.parent.parent.params.subscribe(async (params) => {
|
this.route.parent.parent.params.subscribe(async (params) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.flexibleCollectionsEnabled$ = this.route.parent.parent.params.pipe(
|
||||||
|
switchMap((params) => this.organizationService.get$(params.organizationId)),
|
||||||
|
map((organization) => organization.flexibleCollections),
|
||||||
|
);
|
||||||
|
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { UntypedFormBuilder } from "@angular/forms";
|
import { UntypedFormBuilder } from "@angular/forms";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { Observable, firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
|
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.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";
|
||||||
@@ -27,10 +25,8 @@ export class ExportComponent extends BaseExportComponent {
|
|||||||
encryptedExportType = EncryptedExportType;
|
encryptedExportType = EncryptedExportType;
|
||||||
protected showFilePassword: boolean;
|
protected showFilePassword: boolean;
|
||||||
|
|
||||||
protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
// Used in the OrganizationVaultExport subclass
|
||||||
FeatureFlag.FlexibleCollections,
|
protected flexibleCollectionsEnabled$ = new Observable<boolean>();
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
@@ -44,7 +40,6 @@ export class ExportComponent extends BaseExportComponent {
|
|||||||
fileDownloadService: FileDownloadService,
|
fileDownloadService: FileDownloadService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
organizationService: OrganizationService,
|
organizationService: OrganizationService,
|
||||||
protected configService: ConfigServiceAbstraction,
|
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
|
|||||||
@@ -69,10 +69,9 @@ 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 flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
protected flexibleCollectionsEnabled$ = this.organizationService
|
||||||
FeatureFlag.FlexibleCollections,
|
.get$(this.params.organizationId)
|
||||||
false,
|
.pipe(map((o) => o?.flexibleCollections));
|
||||||
);
|
|
||||||
|
|
||||||
protected flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
protected flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.FlexibleCollectionsV1,
|
FeatureFlag.FlexibleCollectionsV1,
|
||||||
@@ -110,9 +109,9 @@ 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: ConfigServiceAbstraction,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {
|
) {
|
||||||
this.tabIndex = params.initialTab ?? CollectionDialogTabType.Info;
|
this.tabIndex = params.initialTab ?? CollectionDialogTabType.Info;
|
||||||
}
|
}
|
||||||
@@ -209,7 +208,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
access: accessSelections,
|
access: accessSelections,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showDeleteButton = this.collection.canDelete(organization, flexibleCollections);
|
this.showDeleteButton = this.collection.canDelete(organization);
|
||||||
} else {
|
} else {
|
||||||
this.nestOptions = collections;
|
this.nestOptions = collections;
|
||||||
const parent = collections.find((c) => c.id === this.params.parentCollectionId);
|
const parent = collections.find((c) => c.id === this.params.parentCollectionId);
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { Component, EventEmitter, HostBinding, HostListener, Input, Output } from "@angular/core";
|
||||||
Component,
|
|
||||||
EventEmitter,
|
|
||||||
HostBinding,
|
|
||||||
HostListener,
|
|
||||||
Input,
|
|
||||||
OnInit,
|
|
||||||
Output,
|
|
||||||
} from "@angular/core";
|
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
@@ -19,7 +11,6 @@ import { CollectionAdminView } from "../../core/views/collection-admin.view";
|
|||||||
import {
|
import {
|
||||||
convertToPermission,
|
convertToPermission,
|
||||||
getPermissionList,
|
getPermissionList,
|
||||||
Permission,
|
|
||||||
} from "./../../../admin-console/organizations/shared/components/access-selector/access-selector.models";
|
} from "./../../../admin-console/organizations/shared/components/access-selector/access-selector.models";
|
||||||
import { VaultItemEvent } from "./vault-item-event";
|
import { VaultItemEvent } from "./vault-item-event";
|
||||||
import { RowHeightClass } from "./vault-items.component";
|
import { RowHeightClass } from "./vault-items.component";
|
||||||
@@ -28,7 +19,7 @@ import { RowHeightClass } from "./vault-items.component";
|
|||||||
selector: "tr[appVaultCollectionRow]",
|
selector: "tr[appVaultCollectionRow]",
|
||||||
templateUrl: "vault-collection-row.component.html",
|
templateUrl: "vault-collection-row.component.html",
|
||||||
})
|
})
|
||||||
export class VaultCollectionRowComponent implements OnInit {
|
export class VaultCollectionRowComponent {
|
||||||
protected RowHeightClass = RowHeightClass;
|
protected RowHeightClass = RowHeightClass;
|
||||||
|
|
||||||
@Input() disabled: boolean;
|
@Input() disabled: boolean;
|
||||||
@@ -41,24 +32,17 @@ export class VaultCollectionRowComponent implements OnInit {
|
|||||||
@Input() organizations: Organization[];
|
@Input() organizations: Organization[];
|
||||||
@Input() groups: GroupView[];
|
@Input() groups: GroupView[];
|
||||||
@Input() showPermissionsColumn: boolean;
|
@Input() showPermissionsColumn: boolean;
|
||||||
@Input() flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
@Output() onEvent = new EventEmitter<VaultItemEvent>();
|
@Output() onEvent = new EventEmitter<VaultItemEvent>();
|
||||||
|
|
||||||
@Input() checked: boolean;
|
@Input() checked: boolean;
|
||||||
@Output() checkedToggled = new EventEmitter<void>();
|
@Output() checkedToggled = new EventEmitter<void>();
|
||||||
|
|
||||||
private permissionList: Permission[];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.permissionList = getPermissionList(this.flexibleCollectionsEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostBinding("class")
|
@HostBinding("class")
|
||||||
get classes() {
|
get classes() {
|
||||||
return [].concat(this.disabled ? [] : ["tw-cursor-pointer"]);
|
return [].concat(this.disabled ? [] : ["tw-cursor-pointer"]);
|
||||||
@@ -80,8 +64,9 @@ export class VaultCollectionRowComponent implements OnInit {
|
|||||||
if (!(this.collection as CollectionAdminView).assigned) {
|
if (!(this.collection as CollectionAdminView).assigned) {
|
||||||
return "-";
|
return "-";
|
||||||
} else {
|
} else {
|
||||||
|
const permissionList = getPermissionList(this.organization?.flexibleCollections);
|
||||||
return this.i18nService.t(
|
return this.i18nService.t(
|
||||||
this.permissionList.find((p) => p.perm === convertToPermission(this.collection))?.labelId,
|
permissionList.find((p) => p.perm === convertToPermission(this.collection))?.labelId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,6 @@
|
|||||||
[canDeleteCollection]="canDeleteCollection(item.collection)"
|
[canDeleteCollection]="canDeleteCollection(item.collection)"
|
||||||
[canEditCollection]="canEditCollection(item.collection)"
|
[canEditCollection]="canEditCollection(item.collection)"
|
||||||
[checked]="selection.isSelected(item)"
|
[checked]="selection.isSelected(item)"
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled"
|
|
||||||
(checkedToggled)="selection.toggle(item)"
|
(checkedToggled)="selection.toggle(item)"
|
||||||
(onEvent)="event($event)"
|
(onEvent)="event($event)"
|
||||||
></tr>
|
></tr>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { SelectionModel } from "@angular/cdk/collections";
|
|||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
|
|
||||||
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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
import { TableDataSource } from "@bitwarden/components";
|
import { TableDataSource } from "@bitwarden/components";
|
||||||
@@ -29,8 +27,6 @@ const MaxSelectionCount = 500;
|
|||||||
export class VaultItemsComponent {
|
export class VaultItemsComponent {
|
||||||
protected RowHeight = RowHeight;
|
protected RowHeight = RowHeight;
|
||||||
|
|
||||||
protected flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
@Input() disabled: boolean;
|
@Input() disabled: boolean;
|
||||||
@Input() showOwner: boolean;
|
@Input() showOwner: boolean;
|
||||||
@Input() showCollections: boolean;
|
@Input() showCollections: boolean;
|
||||||
@@ -73,14 +69,6 @@ export class VaultItemsComponent {
|
|||||||
protected dataSource = new TableDataSource<VaultItem>();
|
protected dataSource = new TableDataSource<VaultItem>();
|
||||||
protected selection = new SelectionModel<VaultItem>(true, [], true);
|
protected selection = new SelectionModel<VaultItem>(true, [], true);
|
||||||
|
|
||||||
constructor(private configService: ConfigServiceAbstraction) {}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get showExtraColumn() {
|
get showExtraColumn() {
|
||||||
return this.showCollections || this.showGroups || this.showOwner;
|
return this.showCollections || this.showGroups || this.showOwner;
|
||||||
}
|
}
|
||||||
@@ -108,7 +96,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.flexibleCollectionsEnabled);
|
return collection.canEdit(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected canDeleteCollection(collection: CollectionView): boolean {
|
protected canDeleteCollection(collection: CollectionView): boolean {
|
||||||
@@ -118,7 +106,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.canDelete(organization, this.flexibleCollectionsEnabled);
|
return collection.canDelete(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected toggleAll() {
|
protected toggleAll() {
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ export class CollectionAdminView extends CollectionView {
|
|||||||
this.assigned = response.assigned;
|
this.assigned = response.assigned;
|
||||||
}
|
}
|
||||||
|
|
||||||
override canEdit(org: Organization, flexibleCollectionsEnabled: boolean): boolean {
|
override canEdit(org: Organization): boolean {
|
||||||
return flexibleCollectionsEnabled
|
return org?.flexibleCollections
|
||||||
? org?.canEditAnyCollection
|
? org?.canEditAnyCollection
|
||||||
: org?.canEditAnyCollection || (org?.canEditAssignedCollections && this.assigned);
|
: org?.canEditAnyCollection || (org?.canEditAssignedCollections && this.assigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
override canDelete(org: Organization, flexibleCollectionsEnabled: boolean): boolean {
|
override canDelete(org: Organization): boolean {
|
||||||
return flexibleCollectionsEnabled
|
return org?.flexibleCollections
|
||||||
? org?.canDeleteAnyCollection
|
? org?.canDeleteAnyCollection
|
||||||
: org?.canDeleteAnyCollection || (org?.canDeleteAssignedCollections && this.assigned);
|
: org?.canDeleteAnyCollection || (org?.canDeleteAssignedCollections && this.assigned);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { Component, Inject } from "@angular/core";
|
|||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
@@ -59,7 +57,6 @@ export class BulkDeleteDialogComponent {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {
|
) {
|
||||||
this.cipherIds = params.cipherIds ?? [];
|
this.cipherIds = params.cipherIds ?? [];
|
||||||
this.permanent = params.permanent;
|
this.permanent = params.permanent;
|
||||||
@@ -125,15 +122,9 @@ export class BulkDeleteDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async deleteCollections(): Promise<any> {
|
private async deleteCollections(): Promise<any> {
|
||||||
const flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
// 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, flexibleCollectionsEnabled))
|
|
||||||
) {
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"error",
|
"error",
|
||||||
this.i18nService.t("errorOccurred"),
|
this.i18nService.t("errorOccurred"),
|
||||||
@@ -150,7 +141,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, flexibleCollectionsEnabled))) {
|
if (orgCollections.some((c) => !c.canDelete(organization))) {
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"error",
|
"error",
|
||||||
this.i18nService.t("errorOccurred"),
|
this.i18nService.t("errorOccurred"),
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
|
|
||||||
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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
@@ -24,8 +22,6 @@ export class VaultHeaderComponent {
|
|||||||
protected All = All;
|
protected All = All;
|
||||||
protected CollectionDialogTabType = CollectionDialogTabType;
|
protected CollectionDialogTabType = CollectionDialogTabType;
|
||||||
|
|
||||||
private flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean to determine the loading state of the header.
|
* Boolean to determine the loading state of the header.
|
||||||
* Shows a loading spinner if set to true
|
* Shows a loading spinner if set to true
|
||||||
@@ -59,16 +55,7 @@ export class VaultHeaderComponent {
|
|||||||
/** 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>();
|
||||||
|
|
||||||
constructor(
|
constructor(private i18nService: I18nService) {}
|
||||||
private i18nService: I18nService,
|
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id of the organization that is currently being filtered on.
|
* The id of the organization that is currently being filtered on.
|
||||||
@@ -146,7 +133,7 @@ export class VaultHeaderComponent {
|
|||||||
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.flexibleCollectionsEnabled);
|
return this.collection.node.canEdit(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
async editCollection(tab: CollectionDialogTabType): Promise<void> {
|
async editCollection(tab: CollectionDialogTabType): Promise<void> {
|
||||||
@@ -164,7 +151,7 @@ export class VaultHeaderComponent {
|
|||||||
(o) => o.id === this.collection?.node.organizationId,
|
(o) => o.id === this.collection?.node.organizationId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.collection.node.canDelete(organization, this.flexibleCollectionsEnabled);
|
return this.collection.node.canDelete(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCollection() {
|
deleteCollection() {
|
||||||
|
|||||||
@@ -688,11 +688,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async deleteCollection(collection: CollectionView): Promise<void> {
|
async deleteCollection(collection: CollectionView): Promise<void> {
|
||||||
const organization = this.organizationService.get(collection.organizationId);
|
const organization = this.organizationService.get(collection.organizationId);
|
||||||
const flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
if (!collection.canDelete(organization)) {
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
if (!collection.canDelete(organization, flexibleCollectionsEnabled)) {
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"error",
|
"error",
|
||||||
this.i18nService.t("errorOccurred"),
|
this.i18nService.t("errorOccurred"),
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component, Inject, OnDestroy } from "@angular/core";
|
import { Component, Inject, OnDestroy } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { combineLatest, of, Subject, switchMap, takeUntil } from "rxjs";
|
import { combineLatest, map, of, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
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";
|
||||||
@@ -44,10 +42,9 @@ export enum BulkCollectionsDialogResult {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class BulkCollectionsDialogComponent implements OnDestroy {
|
export class BulkCollectionsDialogComponent implements OnDestroy {
|
||||||
protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$(
|
protected flexibleCollectionsEnabled$ = this.organizationService
|
||||||
FeatureFlag.FlexibleCollections,
|
.get$(this.params.organizationId)
|
||||||
false,
|
.pipe(map((o) => o?.flexibleCollections));
|
||||||
);
|
|
||||||
|
|
||||||
protected readonly PermissionMode = PermissionMode;
|
protected readonly PermissionMode = PermissionMode;
|
||||||
|
|
||||||
@@ -71,7 +68,6 @@ export class BulkCollectionsDialogComponent implements OnDestroy {
|
|||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private collectionAdminService: CollectionAdminService,
|
private collectionAdminService: CollectionAdminService,
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {
|
) {
|
||||||
this.numCollections = this.params.collections.length;
|
this.numCollections = this.params.collections.length;
|
||||||
const organization$ = this.organizationService.get$(this.params.organizationId);
|
const organization$ = this.organizationService.get$(this.params.organizationId);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { firstValueFrom, Subject } from "rxjs";
|
|||||||
|
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||||
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";
|
||||||
@@ -32,8 +31,6 @@ export class VaultFilterComponent extends BaseVaultFilterComponent implements On
|
|||||||
_organization: Organization;
|
_organization: Organization;
|
||||||
protected destroy$: Subject<void>;
|
protected destroy$: Subject<void>;
|
||||||
|
|
||||||
private flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected vaultFilterService: VaultFilterService,
|
protected vaultFilterService: VaultFilterService,
|
||||||
protected policyService: PolicyService,
|
protected policyService: PolicyService,
|
||||||
@@ -45,9 +42,6 @@ export class VaultFilterComponent extends BaseVaultFilterComponent implements On
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
);
|
|
||||||
this.filters = await this.buildAllFilters();
|
this.filters = await this.buildAllFilters();
|
||||||
if (!this.activeFilter.selectedCipherTypeNode) {
|
if (!this.activeFilter.selectedCipherTypeNode) {
|
||||||
this.activeFilter.resetFilter();
|
this.activeFilter.resetFilter();
|
||||||
@@ -103,7 +97,7 @@ export class VaultFilterComponent extends BaseVaultFilterComponent implements On
|
|||||||
async buildAllFilters(): Promise<VaultFilterList> {
|
async buildAllFilters(): Promise<VaultFilterList> {
|
||||||
const builderFilter = {} as VaultFilterList;
|
const builderFilter = {} as VaultFilterList;
|
||||||
builderFilter.typeFilter = await this.addTypeFilter(["favorites"]);
|
builderFilter.typeFilter = await this.addTypeFilter(["favorites"]);
|
||||||
if (this.flexibleCollectionsEnabled) {
|
if (this.organization?.flexibleCollections) {
|
||||||
builderFilter.collectionFilter = await this.addCollectionFilter();
|
builderFilter.collectionFilter = await this.addCollectionFilter();
|
||||||
} else {
|
} else {
|
||||||
builderFilter.collectionFilter = await super.addCollectionFilter();
|
builderFilter.collectionFilter = await super.addCollectionFilter();
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import { firstValueFrom } from "rxjs";
|
|||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { ProductType } from "@bitwarden/common/enums";
|
import { ProductType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||||
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
|
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
|
||||||
@@ -58,25 +56,16 @@ export class VaultHeaderComponent {
|
|||||||
protected CollectionDialogTabType = CollectionDialogTabType;
|
protected CollectionDialogTabType = CollectionDialogTabType;
|
||||||
protected organizations$ = this.organizationService.organizations$;
|
protected organizations$ = this.organizationService.organizations$;
|
||||||
|
|
||||||
private flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private collectionAdminService: CollectionAdminService,
|
private collectionAdminService: CollectionAdminService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private configService: ConfigServiceAbstraction,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
const headerType = this.flexibleCollectionsEnabled
|
const headerType = this.organization?.flexibleCollections
|
||||||
? this.i18nService.t("collections").toLowerCase()
|
? this.i18nService.t("collections").toLowerCase()
|
||||||
: this.i18nService.t("vault").toLowerCase();
|
: this.i18nService.t("vault").toLowerCase();
|
||||||
|
|
||||||
@@ -156,7 +145,7 @@ export class VaultHeaderComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.flexibleCollectionsEnabled);
|
return this.collection.node.canEdit(this.organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
addCipher() {
|
addCipher() {
|
||||||
@@ -186,7 +175,7 @@ export class VaultHeaderComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.flexibleCollectionsEnabled);
|
return this.collection.node.canDelete(this.organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCollection() {
|
deleteCollection() {
|
||||||
|
|||||||
@@ -770,11 +770,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteCollection(collection: CollectionView): Promise<void> {
|
async deleteCollection(collection: CollectionView): Promise<void> {
|
||||||
const flexibleCollectionsEnabled = await this.configService.getFeatureFlag(
|
if (!collection.canDelete(this.organization)) {
|
||||||
FeatureFlag.FlexibleCollections,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
if (!collection.canDelete(this.organization, flexibleCollectionsEnabled)) {
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"error",
|
"error",
|
||||||
this.i18nService.t("errorOccurred"),
|
this.i18nService.t("errorOccurred"),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class OrganizationUserResponse extends BaseResponse {
|
|||||||
externalId: string;
|
externalId: string;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* To be removed alongside `FeatureFlag.FlexibleCollections`.
|
* To be removed after Flexible Collections.
|
||||||
**/
|
**/
|
||||||
accessAll: boolean;
|
accessAll: boolean;
|
||||||
accessSecretsManager: boolean;
|
accessSecretsManager: boolean;
|
||||||
|
|||||||
@@ -177,11 +177,15 @@ export class Organization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get canCreateNewCollections() {
|
get canCreateNewCollections() {
|
||||||
return (
|
if (this.flexibleCollections) {
|
||||||
!this.limitCollectionCreationDeletion ||
|
return (
|
||||||
this.isManager ||
|
!this.limitCollectionCreationDeletion ||
|
||||||
this.permissions.createNewCollections
|
this.isAdmin ||
|
||||||
);
|
this.permissions.createNewCollections
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isManager || this.permissions.createNewCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canEditAnyCollection() {
|
get canEditAnyCollection() {
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ export enum FeatureFlag {
|
|||||||
AutofillOverlay = "autofill-overlay",
|
AutofillOverlay = "autofill-overlay",
|
||||||
BrowserFilelessImport = "browser-fileless-import",
|
BrowserFilelessImport = "browser-fileless-import",
|
||||||
ItemShare = "item-share",
|
ItemShare = "item-share",
|
||||||
FlexibleCollections = "flexible-collections",
|
|
||||||
FlexibleCollectionsV1 = "flexible-collections-v-1", // v-1 is intentional
|
FlexibleCollectionsV1 = "flexible-collections-v-1", // v-1 is intentional
|
||||||
BulkCollectionAccess = "bulk-collection-access",
|
BulkCollectionAccess = "bulk-collection-access",
|
||||||
KeyRotationImprovements = "key-rotation-improvements",
|
KeyRotationImprovements = "key-rotation-improvements",
|
||||||
|
FlexibleCollectionsMigration = "flexible-collections-migration",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace this with a type safe lookup of the feature flag values in PM-2282
|
// Replace this with a type safe lookup of the feature flag values in PM-2282
|
||||||
|
|||||||
@@ -32,27 +32,27 @@ export class CollectionView implements View, ITreeNodeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For editing collection details, not the items within it.
|
// For editing collection details, not the items within it.
|
||||||
canEdit(org: Organization, flexibleCollectionsEnabled: 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.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return flexibleCollectionsEnabled
|
return org?.flexibleCollections
|
||||||
? org?.canEditAnyCollection || this.manage
|
? org?.canEditAnyCollection || this.manage
|
||||||
: org?.canEditAnyCollection || org?.canEditAssignedCollections;
|
: org?.canEditAnyCollection || org?.canEditAssignedCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For deleting a collection, not the items within it.
|
// For deleting a collection, not the items within it.
|
||||||
canDelete(org: Organization, flexibleCollectionsEnabled: 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.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return flexibleCollectionsEnabled
|
return org?.flexibleCollections
|
||||||
? org?.canDeleteAnyCollection || (!org?.limitCollectionCreationDeletion && this.manage)
|
? org?.canDeleteAnyCollection || (!org?.limitCollectionCreationDeletion && this.manage)
|
||||||
: org?.canDeleteAnyCollection || org?.canDeleteAssignedCollections;
|
: org?.canDeleteAnyCollection || org?.canDeleteAssignedCollections;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user