1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

[PM-20633] rename personal ownership (#15228)

* sensible renames

* renames

* clean up comments
This commit is contained in:
Brandon Treston
2025-06-24 09:31:40 -04:00
committed by GitHub
parent fa23a905e0
commit 1c237a3753
37 changed files with 170 additions and 162 deletions

View File

@@ -77,7 +77,7 @@ type BaseCipherFormConfig = {
* Flag to indicate if the user is allowed to create ciphers in their own Vault. If false, configuration must
* supply a list of organizations that the user can create ciphers in.
*/
allowPersonalOwnership: boolean;
organizationDataOwnershipDisabled: boolean;
/**
* The original cipher that is being edited or cloned. This can be undefined when creating a new cipher.
@@ -131,18 +131,18 @@ type CreateNewCipherConfig = BaseCipherFormConfig & {
type CombinedAddEditConfig = ExistingCipherConfig | CreateNewCipherConfig;
/**
* Configuration object for the cipher form when personal ownership is allowed.
* Configuration object for the cipher form when organization data ownership is not allowed.
*/
type PersonalOwnershipAllowed = CombinedAddEditConfig & {
allowPersonalOwnership: true;
type OrganizationDataOwnershipDisabled = CombinedAddEditConfig & {
organizationDataOwnershipDisabled: true;
};
/**
* Configuration object for the cipher form when personal ownership is not allowed.
* Configuration object for the cipher form when organization data ownership is allowed.
* Organizations must be provided.
*/
type PersonalOwnershipNotAllowed = CombinedAddEditConfig & {
allowPersonalOwnership: false;
type OrganizationDataOwnershipEnabled = CombinedAddEditConfig & {
organizationDataOwnershipDisabled: false;
organizations: Organization[];
};
@@ -150,7 +150,7 @@ type PersonalOwnershipNotAllowed = CombinedAddEditConfig & {
* Configuration object for the cipher form.
* Determines the behavior of the form and the controls that are displayed/enabled.
*/
export type CipherFormConfig = PersonalOwnershipAllowed | PersonalOwnershipNotAllowed;
export type CipherFormConfig = OrganizationDataOwnershipDisabled | OrganizationDataOwnershipEnabled;
/**
* Service responsible for building the configuration object for the cipher form.

View File

@@ -57,7 +57,7 @@ const defaultConfig: CipherFormConfig = {
mode: "add",
cipherType: CipherType.Login,
admin: false,
allowPersonalOwnership: true,
organizationDataOwnershipDisabled: true,
collections: [
{
id: "col1",
@@ -354,13 +354,13 @@ export const WithSubmitButton: Story = {
},
};
export const NoPersonalOwnership: Story = {
export const OrganizationDataOwnershipEnabled: Story = {
...Add,
args: {
config: {
...defaultConfig,
mode: "add",
allowPersonalOwnership: false,
organizationDataOwnershipDisabled: false,
originalCipher: defaultConfig.originalCipher,
organizations: defaultConfig.organizations!,
},

View File

@@ -22,7 +22,7 @@
<bit-label>{{ "owner" | i18n }}</bit-label>
<bit-select formControlName="organizationId">
<bit-option
*ngIf="showPersonalOwnerOption"
*ngIf="showOrganizationDataOwnershipOption"
[value]="null"
[label]="userEmail$ | async"
></bit-option>

View File

@@ -93,8 +93,8 @@ describe("ItemDetailsSectionComponent", () => {
});
describe("ngOnInit", () => {
it("should throw an error if no organizations are available for ownership and personal ownership is not allowed", async () => {
component.config.allowPersonalOwnership = false;
it("should throw an error if no organizations are available for ownership and organization data ownership is enabled", async () => {
component.config.organizationDataOwnershipDisabled = false;
component.config.organizations = [];
await expect(component.ngOnInit()).rejects.toThrow(
"No organizations available for ownership.",
@@ -102,7 +102,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("should initialize form with default values if no originalCipher is provided", fakeAsync(async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.config.organizations = [{ id: "org1" } as Organization];
await component.ngOnInit();
tick();
@@ -120,7 +120,7 @@ describe("ItemDetailsSectionComponent", () => {
}));
it("should initialize form with values from originalCipher if provided", fakeAsync(async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.config.organizations = [{ id: "org1" } as Organization];
component.config.collections = [
createMockCollection("col1", "Collection 1", "org1") as CollectionView,
@@ -150,7 +150,7 @@ describe("ItemDetailsSectionComponent", () => {
}));
it("should disable organizationId control if ownership change is not allowed", async () => {
component.config.allowPersonalOwnership = false;
component.config.organizationDataOwnershipDisabled = false;
component.config.organizations = [{ id: "org1" } as Organization];
jest.spyOn(component, "allowOwnershipChange", "get").mockReturnValue(false);
@@ -188,15 +188,15 @@ describe("ItemDetailsSectionComponent", () => {
expect(component.allowOwnershipChange).toBe(false);
});
it("should allow ownership change if personal ownership is allowed and there is at least one organization", () => {
component.config.allowPersonalOwnership = true;
it("should allow ownership change if organization data ownership is disabled and there is at least one organization", () => {
component.config.organizationDataOwnershipDisabled = true;
component.config.organizations = [{ id: "org1", name: "org1" } as Organization];
fixture.detectChanges();
expect(component.allowOwnershipChange).toBe(true);
});
it("should allow ownership change if personal ownership is not allowed but there is more than one organization", () => {
component.config.allowPersonalOwnership = false;
it("should allow ownership change if organization data ownership is enabled but there is more than one organization", () => {
component.config.organizationDataOwnershipDisabled = false;
component.config.organizations = [
{ id: "org1", name: "org1" } as Organization,
{ id: "org2", name: "org2" } as Organization,
@@ -207,23 +207,23 @@ describe("ItemDetailsSectionComponent", () => {
});
describe("defaultOwner", () => {
it("should return null if personal ownership is allowed", () => {
component.config.allowPersonalOwnership = true;
it("should return null if organization data ownership is disabled", () => {
component.config.organizationDataOwnershipDisabled = true;
expect(component.defaultOwner).toBeNull();
});
it("should return the first organization id if personal ownership is not allowed", () => {
component.config.allowPersonalOwnership = false;
it("should return the first organization id if organization data ownership is enabled", () => {
component.config.organizationDataOwnershipDisabled = false;
component.config.organizations = [{ id: "org1", name: "Organization 1" } as Organization];
fixture.detectChanges();
expect(component.defaultOwner).toBe("org1");
});
});
describe("showPersonalOwnerOption", () => {
it("should show personal ownership when the configuration allows", () => {
describe("showOrganizationDataOwnershipOption", () => {
it("should show organization data ownership when the configuration allows", () => {
component.config.mode = "edit";
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.originalCipherView = {} as CipherView;
component.config.organizations = [{ id: "134-433-22" } as Organization];
fixture.detectChanges();
@@ -235,9 +235,9 @@ describe("ItemDetailsSectionComponent", () => {
expect(label).toBe("test@example.com");
});
it("should show personal ownership when the control is disabled", async () => {
it("should show organization data ownership when the control is disabled", async () => {
component.config.mode = "edit";
component.config.allowPersonalOwnership = false;
component.config.organizationDataOwnershipDisabled = false;
component.originalCipherView = {} as CipherView;
component.config.organizations = [{ id: "134-433-22" } as Organization];
await component.ngOnInit();
@@ -253,7 +253,7 @@ describe("ItemDetailsSectionComponent", () => {
describe("showOwnership", () => {
it("should return true if ownership change is allowed or in edit mode with at least one organization", () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
jest.spyOn(component, "allowOwnershipChange", "get").mockReturnValue(true);
expect(component.showOwnership).toBe(true);
@@ -265,7 +265,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("should hide the ownership control if showOwnership is false", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
jest.spyOn(component, "showOwnership", "get").mockReturnValue(false);
fixture.detectChanges();
await fixture.whenStable();
@@ -276,7 +276,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("should show the ownership control if showOwnership is true", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
jest.spyOn(component, "allowOwnershipChange", "get").mockReturnValue(true);
fixture.detectChanges();
await fixture.whenStable();
@@ -293,7 +293,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("should append '- Clone' to the title if in clone mode", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
const cipher = {
name: "cipher1",
organizationId: null,
@@ -312,7 +312,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("does not append clone when the cipher was populated from the cache", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
const cipher = {
name: "from cache cipher",
organizationId: null,
@@ -332,8 +332,8 @@ describe("ItemDetailsSectionComponent", () => {
expect(component.itemDetailsForm.controls.name.value).toBe("from cache cipher");
});
it("should select the first organization if personal ownership is not allowed", async () => {
component.config.allowPersonalOwnership = false;
it("should select the first organization if organization data ownership is enabled", async () => {
component.config.organizationDataOwnershipDisabled = false;
component.config.organizations = [
{ id: "org1", name: "org1" } as Organization,
{ id: "org2", name: "org2" } as Organization,
@@ -354,7 +354,7 @@ describe("ItemDetailsSectionComponent", () => {
describe("collectionOptions", () => {
it("should reset and disable/hide collections control when no organization is selected", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.itemDetailsForm.controls.organizationId.setValue(null);
fixture.detectChanges();
@@ -370,7 +370,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("should enable/show collection control when an organization is selected", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.config.organizations = [{ id: "org1" } as Organization];
component.config.collections = [
createMockCollection("col1", "Collection 1", "org1") as CollectionView,
@@ -421,7 +421,7 @@ describe("ItemDetailsSectionComponent", () => {
});
it("should automatically select the first collection if only one is available", async () => {
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.config.organizations = [{ id: "org1" } as Organization];
component.config.collections = [
createMockCollection("col1", "Collection 1", "org1") as CollectionView,
@@ -475,7 +475,7 @@ describe("ItemDetailsSectionComponent", () => {
it("should allow all collections to be altered when `config.admin` is true", async () => {
component.config.admin = true;
component.config.allowPersonalOwnership = true;
component.config.organizationDataOwnershipDisabled = true;
component.config.organizations = [{ id: "org1" } as Organization];
component.config.collections = [
createMockCollection("col1", "Collection 1", "org1", true, false) as CollectionView,
@@ -491,7 +491,6 @@ describe("ItemDetailsSectionComponent", () => {
expect(component["collectionOptions"].map((c) => c.id)).toEqual(["col1", "col2", "col3"]);
});
});
describe("readonlyCollections", () => {
beforeEach(() => {
component.config.mode = "edit";

View File

@@ -92,8 +92,8 @@ export class ItemDetailsSectionComponent implements OnInit {
return this.config.mode === "partial-edit";
}
get allowPersonalOwnership() {
return this.config.allowPersonalOwnership;
get organizationDataOwnershipDisabled() {
return this.config.organizationDataOwnershipDisabled;
}
get collections(): CollectionView[] {
@@ -105,14 +105,17 @@ export class ItemDetailsSectionComponent implements OnInit {
}
/**
* Show the personal ownership option in the Owner dropdown when:
* - Personal ownership is allowed
* Show the organization data ownership option in the Owner dropdown when:
* - organization data ownership is disabled
* - The `organizationId` control is disabled. This avoids the scenario
* where a the dropdown is empty because the user personally owns the cipher
* but cannot edit the ownership.
*/
get showPersonalOwnerOption() {
return this.allowPersonalOwnership || !this.itemDetailsForm.controls.organizationId.enabled;
get showOrganizationDataOwnershipOption() {
return (
this.organizationDataOwnershipDisabled ||
!this.itemDetailsForm.controls.organizationId.enabled
);
}
constructor(
@@ -161,7 +164,7 @@ export class ItemDetailsSectionComponent implements OnInit {
}
// If personal ownership is allowed and there is at least one organization, allow ownership change.
if (this.allowPersonalOwnership) {
if (this.organizationDataOwnershipDisabled) {
return this.organizations.length > 0;
}
@@ -180,7 +183,7 @@ export class ItemDetailsSectionComponent implements OnInit {
}
get defaultOwner() {
return this.allowPersonalOwnership ? null : this.organizations[0].id;
return this.organizationDataOwnershipDisabled ? null : this.organizations[0].id;
}
async ngOnInit() {
@@ -188,7 +191,7 @@ export class ItemDetailsSectionComponent implements OnInit {
Utils.getSortFunction(this.i18nService, "name"),
);
if (!this.allowPersonalOwnership && this.organizations.length === 0) {
if (!this.organizationDataOwnershipDisabled && this.organizations.length === 0) {
throw new Error("No organizations available for ownership.");
}
@@ -244,7 +247,7 @@ export class ItemDetailsSectionComponent implements OnInit {
);
}
if (!this.allowPersonalOwnership && prefillCipher.organizationId == null) {
if (!this.organizationDataOwnershipDisabled && prefillCipher.organizationId == null) {
this.itemDetailsForm.controls.organizationId.setValue(this.defaultOwner);
}
}

View File

@@ -44,7 +44,7 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
): Promise<CipherFormConfig> {
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const [organizations, collections, allowPersonalOwnership, folders, cipher] =
const [organizations, collections, organizationDataOwnershipDisabled, folders, cipher] =
await firstValueFrom(
combineLatest([
this.organizations$(activeUserId),
@@ -55,7 +55,7 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
),
),
),
this.allowPersonalOwnership$,
this.organizationDataOwnershipDisabled$,
this.folderService.folders$(activeUserId).pipe(
switchMap((f) =>
this.folderService.folderViews$(activeUserId).pipe(
@@ -71,7 +71,7 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
mode,
cipherType: cipher?.type ?? cipherType ?? CipherType.Login,
admin: false,
allowPersonalOwnership,
organizationDataOwnershipDisabled,
originalCipher: cipher,
collections,
organizations,
@@ -91,10 +91,10 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
);
}
private allowPersonalOwnership$ = this.accountService.activeAccount$.pipe(
private organizationDataOwnershipDisabled$ = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
),
map((p) => !p),
);