mirror of
https://github.com/bitwarden/browser
synced 2025-12-21 18:53:29 +00:00
[AC-1041] My Vault filter on first login fix (#10301)
* [AC-1041] Ensure organizationTree$ updates whenever the policy observables emit * [AC-1041] Ensure enforcePersonalOwnership updates whenever the policy observable emits * [AC-1041] Do not attempt to pre-select null filter values or read-only collections
This commit is contained in:
@@ -36,6 +36,8 @@ describe("vault filter service", () => {
|
||||
let organizations: ReplaySubject<Organization[]>;
|
||||
let folderViews: ReplaySubject<FolderView[]>;
|
||||
let collectionViews: ReplaySubject<CollectionView[]>;
|
||||
let personalOwnershipPolicy: ReplaySubject<boolean>;
|
||||
let singleOrgPolicy: ReplaySubject<boolean>;
|
||||
let stateProvider: FakeStateProvider;
|
||||
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
@@ -56,10 +58,18 @@ describe("vault filter service", () => {
|
||||
organizations = new ReplaySubject<Organization[]>(1);
|
||||
folderViews = new ReplaySubject<FolderView[]>(1);
|
||||
collectionViews = new ReplaySubject<CollectionView[]>(1);
|
||||
personalOwnershipPolicy = new ReplaySubject<boolean>(1);
|
||||
singleOrgPolicy = new ReplaySubject<boolean>(1);
|
||||
|
||||
organizationService.memberOrganizations$ = organizations;
|
||||
folderService.folderViews$ = folderViews;
|
||||
collectionService.decryptedCollections$ = collectionViews;
|
||||
policyService.policyAppliesToActiveUser$
|
||||
.calledWith(PolicyType.PersonalOwnership)
|
||||
.mockReturnValue(personalOwnershipPolicy);
|
||||
policyService.policyAppliesToActiveUser$
|
||||
.calledWith(PolicyType.SingleOrg)
|
||||
.mockReturnValue(singleOrgPolicy);
|
||||
|
||||
vaultFilterService = new VaultFilterService(
|
||||
organizationService,
|
||||
@@ -100,6 +110,8 @@ describe("vault filter service", () => {
|
||||
beforeEach(() => {
|
||||
const storedOrgs = [createOrganization("1", "org1"), createOrganization("2", "org2")];
|
||||
organizations.next(storedOrgs);
|
||||
personalOwnershipPolicy.next(false);
|
||||
singleOrgPolicy.next(false);
|
||||
});
|
||||
|
||||
it("returns a nested tree", async () => {
|
||||
@@ -111,9 +123,7 @@ describe("vault filter service", () => {
|
||||
});
|
||||
|
||||
it("hides My Vault if personal ownership policy is enabled", async () => {
|
||||
policyService.policyAppliesToUser
|
||||
.calledWith(PolicyType.PersonalOwnership)
|
||||
.mockResolvedValue(true);
|
||||
personalOwnershipPolicy.next(true);
|
||||
|
||||
const tree = await firstValueFrom(vaultFilterService.organizationTree$);
|
||||
|
||||
@@ -122,7 +132,7 @@ describe("vault filter service", () => {
|
||||
});
|
||||
|
||||
it("returns 1 organization and My Vault if single organization policy is enabled", async () => {
|
||||
policyService.policyAppliesToUser.calledWith(PolicyType.SingleOrg).mockResolvedValue(true);
|
||||
singleOrgPolicy.next(true);
|
||||
|
||||
const tree = await firstValueFrom(vaultFilterService.organizationTree$);
|
||||
|
||||
@@ -132,10 +142,8 @@ describe("vault filter service", () => {
|
||||
});
|
||||
|
||||
it("returns 1 organization if both single organization and personal ownership policies are enabled", async () => {
|
||||
policyService.policyAppliesToUser.calledWith(PolicyType.SingleOrg).mockResolvedValue(true);
|
||||
policyService.policyAppliesToUser
|
||||
.calledWith(PolicyType.PersonalOwnership)
|
||||
.mockResolvedValue(true);
|
||||
singleOrgPolicy.next(true);
|
||||
personalOwnershipPolicy.next(true);
|
||||
|
||||
const tree = await firstValueFrom(vaultFilterService.organizationTree$);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
combineLatestWith,
|
||||
firstValueFrom,
|
||||
map,
|
||||
@@ -39,10 +40,15 @@ const NestingDelimiter = "/";
|
||||
|
||||
@Injectable()
|
||||
export class VaultFilterService implements VaultFilterServiceAbstraction {
|
||||
organizationTree$: Observable<TreeNode<OrganizationFilter>> =
|
||||
this.organizationService.memberOrganizations$.pipe(
|
||||
switchMap((orgs) => this.buildOrganizationTree(orgs)),
|
||||
);
|
||||
organizationTree$: Observable<TreeNode<OrganizationFilter>> = combineLatest([
|
||||
this.organizationService.memberOrganizations$,
|
||||
this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg),
|
||||
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
||||
]).pipe(
|
||||
switchMap(([orgs, singleOrgPolicy, personalOwnershipPolicy]) =>
|
||||
this.buildOrganizationTree(orgs, singleOrgPolicy, personalOwnershipPolicy),
|
||||
),
|
||||
);
|
||||
|
||||
protected _organizationFilter = new BehaviorSubject<Organization>(null);
|
||||
|
||||
@@ -125,14 +131,16 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
|
||||
}
|
||||
|
||||
protected async buildOrganizationTree(
|
||||
orgs?: Organization[],
|
||||
orgs: Organization[],
|
||||
singleOrgPolicy: boolean,
|
||||
personalOwnershipPolicy: boolean,
|
||||
): Promise<TreeNode<OrganizationFilter>> {
|
||||
const headNode = this.getOrganizationFilterHead();
|
||||
if (!(await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership))) {
|
||||
if (!personalOwnershipPolicy) {
|
||||
const myVaultNode = this.getOrganizationFilterMyVault();
|
||||
headNode.children.push(myVaultNode);
|
||||
}
|
||||
if (await this.policyService.policyAppliesToUser(PolicyType.SingleOrg)) {
|
||||
if (singleOrgPolicy) {
|
||||
orgs = orgs.slice(0, 1);
|
||||
}
|
||||
if (orgs) {
|
||||
|
||||
@@ -586,18 +586,24 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
async addCipher(cipherType?: CipherType) {
|
||||
const component = await this.editCipher(null);
|
||||
component.type = cipherType || this.activeFilter.cipherType;
|
||||
if (this.activeFilter.organizationId !== "MyVault") {
|
||||
if (
|
||||
this.activeFilter.organizationId !== "MyVault" &&
|
||||
this.activeFilter.organizationId != null
|
||||
) {
|
||||
component.organizationId = this.activeFilter.organizationId;
|
||||
component.collections = (
|
||||
await firstValueFrom(this.vaultFilterService.filteredCollections$)
|
||||
).filter((c) => !c.readOnly && c.id != null);
|
||||
}
|
||||
const selectedColId = this.activeFilter.collectionId;
|
||||
if (selectedColId !== "AllCollections") {
|
||||
component.organizationId = component.collections.find(
|
||||
(collection) => collection.id === selectedColId,
|
||||
)?.organizationId;
|
||||
component.collectionIds = [selectedColId];
|
||||
if (selectedColId !== "AllCollections" && selectedColId != null) {
|
||||
const selectedCollection = (
|
||||
await firstValueFrom(this.vaultFilterService.filteredCollections$)
|
||||
).find((c) => c.id === selectedColId);
|
||||
component.organizationId = selectedCollection?.organizationId;
|
||||
if (!selectedCollection.readOnly) {
|
||||
component.collectionIds = [selectedColId];
|
||||
}
|
||||
}
|
||||
component.folderId = this.activeFilter.folderId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user