1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[AC-1707] Restrict provider access to items (#8265)

* [AC-1707] Add feature flag

* [AC-1707] Prevent loading ciphers for provider users in the org vault when the feature flag is enabled

* [AC-1707] Ensure new canEditAllCiphers logic only applies to organizations that have FC enabled

* [AC-1707] Update editAllCiphers helper to check for restrictProviderAccess feature flag

* [AC-1707] Remove un-used vaultFilterComponent reference

* [AC-1707] Hide vault filter for providers

* [AC-1707] Add search to vault header for provider users

* [AC-1707] Hide New Item button for Providers when restrict provider access feature flag is enabled

* [AC-1707] Remove leftover debug statement

* [AC-1707] Update canEditAllCiphers references to consider the restrictProviderAccessFlag

* [AC-1707] Fix collections component changes from main

* [AC-1707] Fix some feature flag issues from merge with main

* [AC-1707] Avoid 'readonly' collection dialog for providers

* [AC-1707] Fix broken Browser component

* [AC-1707] Fix broken Desktop component

* [AC-1707] Add restrict provider flag to add access badge logic
This commit is contained in:
Shane Melton
2024-05-07 12:35:28 -07:00
committed by GitHub
parent 27d4178287
commit 3a71322510
18 changed files with 273 additions and 57 deletions

View File

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

View File

@@ -91,6 +91,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
private previousCipherId: string;
protected flexibleCollectionsV1Enabled = false;
protected restrictProviderAccess = false;
get fido2CredentialCreationDateValue(): string {
const dateCreated = this.i18nService.t("dateCreated");
@@ -183,6 +184,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.flexibleCollectionsV1Enabled = await this.configService.getFeatureFlag(
FeatureFlag.FlexibleCollectionsV1,
);
this.restrictProviderAccess = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess,
);
this.policyService
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
@@ -668,11 +672,14 @@ export class AddEditComponent implements OnInit, OnDestroy {
protected saveCipher(cipher: Cipher) {
const isNotClone = this.editMode && !this.cloneMode;
let orgAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled);
let orgAdmin = this.organization?.canEditAllCiphers(
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 (!cipher.collectionIds) {
orgAdmin = this.organization?.canEditUnassignedCiphers();
orgAdmin = this.organization?.canEditUnassignedCiphers(this.restrictProviderAccess);
}
return this.cipher.id == null
@@ -681,14 +688,20 @@ export class AddEditComponent implements OnInit, OnDestroy {
}
protected deleteCipher() {
const asAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled);
const asAdmin = this.organization?.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
);
return this.cipher.isDeleted
? this.cipherService.deleteWithServer(this.cipher.id, asAdmin)
: this.cipherService.softDeleteWithServer(this.cipher.id, asAdmin);
}
protected restoreCipher() {
const asAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled);
const asAdmin = this.organization?.canEditAllCiphers(
this.flexibleCollectionsV1Enabled,
this.restrictProviderAccess,
);
return this.cipherService.restoreWithServer(this.cipher.id, asAdmin);
}

View File

@@ -203,22 +203,32 @@ export class Organization {
);
}
canEditUnassignedCiphers() {
// TODO: Update this to exclude Providers if provider access is restricted in AC-1707
canEditUnassignedCiphers(restrictProviderAccessFlagEnabled: boolean) {
if (this.isProviderUser) {
return !restrictProviderAccessFlagEnabled;
}
return this.isAdmin || this.permissions.editAnyCollection;
}
canEditAllCiphers(flexibleCollectionsV1Enabled: boolean) {
canEditAllCiphers(
flexibleCollectionsV1Enabled: boolean,
restrictProviderAccessFlagEnabled: boolean,
) {
// Before Flexible Collections, any admin or anyone with editAnyCollection permission could edit all ciphers
if (!this.flexibleCollections || !flexibleCollectionsV1Enabled) {
if (!this.flexibleCollections || !flexibleCollectionsV1Enabled || !this.flexibleCollections) {
return this.isAdmin || this.permissions.editAnyCollection;
}
if (this.isProviderUser) {
return !restrictProviderAccessFlagEnabled;
}
// Post Flexible Collections V1, the allowAdminAccessToAllCollectionItems flag can restrict admins
// Providers and custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag
// Custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag
return (
this.isProviderUser ||
(this.type === OrganizationUserType.Custom && this.permissions.editAnyCollection) ||
(this.allowAdminAccessToAllCollectionItems && this.isAdmin)
(this.allowAdminAccessToAllCollectionItems &&
(this.type === OrganizationUserType.Admin || this.type === OrganizationUserType.Owner))
);
}

View File

@@ -17,6 +17,7 @@ export enum FeatureFlag {
UnassignedItemsBanner = "unassigned-items-banner",
EnableDeleteProvider = "AC-1218-delete-provider",
ExtensionRefresh = "extension-refresh",
RestrictProviderAccess = "restrict-provider-access",
}
export type AllowedFeatureFlagTypes = boolean | number | string;
@@ -44,6 +45,7 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.UnassignedItemsBanner]: FALSE,
[FeatureFlag.EnableDeleteProvider]: FALSE,
[FeatureFlag.ExtensionRefresh]: FALSE,
[FeatureFlag.RestrictProviderAccess]: FALSE,
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;

View File

@@ -39,7 +39,11 @@ export class CollectionView implements View, ITreeNodeObject {
}
}
canEditItems(org: Organization, v1FlexibleCollections: boolean): boolean {
canEditItems(
org: Organization,
v1FlexibleCollections: boolean,
restrictProviderAccess: boolean,
): boolean {
if (org != null && org.id !== this.organizationId) {
throw new Error(
"Id of the organization provided does not match the org id of the collection.",
@@ -48,7 +52,7 @@ export class CollectionView implements View, ITreeNodeObject {
if (org?.flexibleCollections) {
return (
org?.canEditAllCiphers(v1FlexibleCollections) ||
org?.canEditAllCiphers(v1FlexibleCollections, restrictProviderAccess) ||
this.manage ||
(this.assigned && !this.readOnly)
);