1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

Auth/PM-5712 - Extension & Desktop Account Switcher - Fix incorrect env showing when adding new accounts (#13362)

* PM-5712 - Refactor env service to require user id instead of having global and active user state fallbacks per working session with Justin.

* PM-5712 - AccountSwitcherService tests - fix tests and add env assertions.
This commit is contained in:
Jared Snider
2025-02-25 17:58:26 -05:00
committed by GitHub
parent cec117459b
commit 44d50a70c2
6 changed files with 65 additions and 91 deletions

View File

@@ -128,7 +128,7 @@ export abstract class EnvironmentService {
/**
* Get the environment from state. Useful if you need to get the environment for another user.
*/
abstract getEnvironment$(userId?: string): Observable<Environment | undefined>;
abstract getEnvironment$(userId: UserId): Observable<Environment | undefined>;
/**
* @deprecated Use {@link getEnvironment$} instead.

View File

@@ -304,85 +304,21 @@ describe("EnvironmentService", () => {
});
});
describe("getEnvironment", () => {
describe("getEnvironment$", () => {
it.each([
{ region: Region.US, expectedHost: "bitwarden.com" },
{ region: Region.EU, expectedHost: "bitwarden.eu" },
])("gets it from user data if there is an active user", async ({ region, expectedHost }) => {
setGlobalData(Region.US, new EnvironmentUrls());
setUserData(region, new EnvironmentUrls());
])("gets it from the passed in userId: %s", async ({ region, expectedHost }) => {
setUserData(Region.US, new EnvironmentUrls());
setUserData(region, new EnvironmentUrls(), alternateTestUser);
await switchUser(testUser);
const env = await firstValueFrom(sut.getEnvironment$());
expect(env.getHostname()).toBe(expectedHost);
const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser));
expect(env?.getHostname()).toBe(expectedHost);
});
it.each([
{ region: Region.US, expectedHost: "bitwarden.com" },
{ region: Region.EU, expectedHost: "bitwarden.eu" },
])("gets it from global data if there is no active user", async ({ region, expectedHost }) => {
setGlobalData(region, new EnvironmentUrls());
setUserData(Region.US, new EnvironmentUrls());
const env = await firstValueFrom(sut.getEnvironment$());
expect(env.getHostname()).toBe(expectedHost);
});
it.each([
{ region: Region.US, expectedHost: "bitwarden.com" },
{ region: Region.EU, expectedHost: "bitwarden.eu" },
])(
"gets it from global state if there is no active user even if a user id is passed in.",
async ({ region, expectedHost }) => {
setGlobalData(region, new EnvironmentUrls());
setUserData(Region.US, new EnvironmentUrls());
const env = await firstValueFrom(sut.getEnvironment$(testUser));
expect(env.getHostname()).toBe(expectedHost);
},
);
it.each([
{ region: Region.US, expectedHost: "bitwarden.com" },
{ region: Region.EU, expectedHost: "bitwarden.eu" },
])(
"gets it from the passed in userId if there is any active user: %s",
async ({ region, expectedHost }) => {
setGlobalData(Region.US, new EnvironmentUrls());
setUserData(Region.US, new EnvironmentUrls());
setUserData(region, new EnvironmentUrls(), alternateTestUser);
await switchUser(testUser);
const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser));
expect(env.getHostname()).toBe(expectedHost);
},
);
it("gets it from base url saved in self host config", async () => {
const globalSelfHostUrls = new EnvironmentUrls();
globalSelfHostUrls.base = "https://base.example.com";
setGlobalData(Region.SelfHosted, globalSelfHostUrls);
setUserData(Region.EU, new EnvironmentUrls());
const env = await firstValueFrom(sut.getEnvironment$());
expect(env.getHostname()).toBe("base.example.com");
});
it("gets it from webVault url saved in self host config", async () => {
const globalSelfHostUrls = new EnvironmentUrls();
globalSelfHostUrls.webVault = "https://vault.example.com";
globalSelfHostUrls.base = "https://base.example.com";
setGlobalData(Region.SelfHosted, globalSelfHostUrls);
setUserData(Region.EU, new EnvironmentUrls());
const env = await firstValueFrom(sut.getEnvironment$());
expect(env.getHostname()).toBe("vault.example.com");
});
it("gets it from saved self host config from passed in user when there is an active user", async () => {
setGlobalData(Region.US, new EnvironmentUrls());
it("gets env from saved self host config from passed in user when there is a different active user", async () => {
setUserData(Region.EU, new EnvironmentUrls());
const selfHostUserUrls = new EnvironmentUrls();
@@ -392,7 +328,31 @@ describe("EnvironmentService", () => {
await switchUser(testUser);
const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser));
expect(env.getHostname()).toBe("base.example.com");
expect(env?.getHostname()).toBe("base.example.com");
});
});
describe("getEnvironment (deprecated)", () => {
it("gets self hosted env from active user when no user passed in", async () => {
const selfHostUserUrls = new EnvironmentUrls();
selfHostUserUrls.base = "https://base.example.com";
setUserData(Region.SelfHosted, selfHostUserUrls);
await switchUser(testUser);
const env = await sut.getEnvironment();
expect(env?.getHostname()).toBe("base.example.com");
});
it("gets self hosted env from passed in user", async () => {
const selfHostUserUrls = new EnvironmentUrls();
selfHostUserUrls.base = "https://base.example.com";
setUserData(Region.SelfHosted, selfHostUserUrls);
await switchUser(testUser);
const env = await sut.getEnvironment(testUser);
expect(env?.getHostname()).toBe("base.example.com");
});
});

View File

@@ -271,19 +271,8 @@ export class DefaultEnvironmentService implements EnvironmentService {
}
}
getEnvironment$(userId?: UserId): Observable<Environment | undefined> {
if (userId == null) {
return this.environment$;
}
return this.activeAccountId$.pipe(
switchMap((activeUserId) => {
// Previous rules dictated that we only get from user scoped state if there is an active user.
if (activeUserId == null) {
return this.globalState.state$;
}
return this.stateProvider.getUser(userId ?? activeUserId, USER_ENVIRONMENT_KEY).state$;
}),
getEnvironment$(userId: UserId): Observable<Environment | undefined> {
return this.stateProvider.getUser(userId, USER_ENVIRONMENT_KEY).state$.pipe(
map((state) => {
return this.buildEnvironment(state?.region, state?.urls);
}),
@@ -294,7 +283,10 @@ export class DefaultEnvironmentService implements EnvironmentService {
* @deprecated Use getEnvironment$ instead.
*/
async getEnvironment(userId?: UserId): Promise<Environment | undefined> {
return firstValueFrom(this.getEnvironment$(userId));
// Add backwards compatibility support for null userId
const definedUserId = userId ?? (await firstValueFrom(this.activeAccountId$));
return firstValueFrom(this.getEnvironment$(definedUserId));
}
async seedUserEnvironment(userId: UserId) {