[PM-9118] fix duplicate website name (#9782)

This commit is contained in:
✨ Audrey ✨ 2024-07-22 15:22:08 -04:00 committed by GitHub
parent b9cb1644ec
commit 52f6b24020
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 112 additions and 22 deletions

View File

@ -151,9 +151,6 @@ export class GeneratorComponent implements OnInit, OnDestroy {
this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random"; this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random";
} else { } else {
this.usernameOptions.website = this.usernameWebsite; this.usernameOptions.website = this.usernameWebsite;
const websiteOption = { name: this.i18nService.t("websiteName"), value: "website-name" };
this.subaddressOptions.push(websiteOption);
this.catchallOptions.push(websiteOption);
} }
} }
@ -201,6 +198,12 @@ export class GeneratorComponent implements OnInit, OnDestroy {
takeUntil(this.destroy$), takeUntil(this.destroy$),
), ),
); );
if (this.usernameWebsite !== null) {
const websiteOption = { name: this.i18nService.t("websiteName"), value: "website-name" };
this.subaddressOptions.push(websiteOption);
this.catchallOptions.push(websiteOption);
}
} }
ngOnDestroy() { ngOnDestroy() {

View File

@ -638,6 +638,10 @@ describe("LegacyUsernameGenerationService", () => {
}); });
describe("saveOptions", () => { describe("saveOptions", () => {
// this test is awful, but the coupling of the legacy username generator
// would cause the test file's size to bloat to ~2000 loc. Since the legacy
// generators are actively being rewritten, this heinous test seemed the lesser
// of two evils.
it("saves option sets to its inner generators", async () => { it("saves option sets to its inner generators", async () => {
const account = mockAccountServiceWith(SomeUser); const account = mockAccountServiceWith(SomeUser);
const navigation = createNavigationGenerator({ type: "password" }); const navigation = createNavigationGenerator({ type: "password" });
@ -665,7 +669,7 @@ describe("LegacyUsernameGenerationService", () => {
simpleLogin, simpleLogin,
); );
await generator.saveOptions({ const options: UsernameGeneratorOptions = {
type: "catchall", type: "catchall",
wordCapitalize: true, wordCapitalize: true,
wordIncludeNumber: false, wordIncludeNumber: false,
@ -685,7 +689,9 @@ describe("LegacyUsernameGenerationService", () => {
forwardedSimpleLoginApiKey: "simpleLoginToken", forwardedSimpleLoginApiKey: "simpleLoginToken",
forwardedSimpleLoginBaseUrl: "https://simplelogin.api.example.com", forwardedSimpleLoginBaseUrl: "https://simplelogin.api.example.com",
website: null, website: null,
}); };
await generator.saveOptions(options);
expect(navigation.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(navigation.saveOptions).toHaveBeenCalledWith(SomeUser, {
type: "password", type: "password",
@ -699,18 +705,28 @@ describe("LegacyUsernameGenerationService", () => {
website: null, website: null,
}); });
options.type = "word";
await generator.saveOptions(options);
expect(effUsername.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(effUsername.saveOptions).toHaveBeenCalledWith(SomeUser, {
wordCapitalize: true, wordCapitalize: true,
wordIncludeNumber: false, wordIncludeNumber: false,
website: null, website: null,
}); });
options.type = "subaddress";
await generator.saveOptions(options);
expect(subaddress.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(subaddress.saveOptions).toHaveBeenCalledWith(SomeUser, {
subaddressType: "random", subaddressType: "random",
subaddressEmail: "foo@example.com", subaddressEmail: "foo@example.com",
website: null, website: null,
}); });
options.type = "forwarded";
options.forwardedService = "anonaddy";
await generator.saveOptions(options);
expect(addyIo.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(addyIo.saveOptions).toHaveBeenCalledWith(SomeUser, {
token: "addyIoToken", token: "addyIoToken",
domain: "addyio.example.com", domain: "addyio.example.com",
@ -718,27 +734,47 @@ describe("LegacyUsernameGenerationService", () => {
website: null, website: null,
}); });
options.type = "forwarded";
options.forwardedService = "duckduckgo";
await generator.saveOptions(options);
expect(duckDuckGo.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(duckDuckGo.saveOptions).toHaveBeenCalledWith(SomeUser, {
token: "ddgToken", token: "ddgToken",
website: null, website: null,
}); });
options.type = "forwarded";
options.forwardedService = "fastmail";
await generator.saveOptions(options);
expect(fastmail.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(fastmail.saveOptions).toHaveBeenCalledWith(SomeUser, {
token: "fastmailToken", token: "fastmailToken",
website: null, website: null,
}); });
options.type = "forwarded";
options.forwardedService = "firefoxrelay";
await generator.saveOptions(options);
expect(firefoxRelay.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(firefoxRelay.saveOptions).toHaveBeenCalledWith(SomeUser, {
token: "firefoxToken", token: "firefoxToken",
website: null, website: null,
}); });
options.type = "forwarded";
options.forwardedService = "forwardemail";
await generator.saveOptions(options);
expect(forwardEmail.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(forwardEmail.saveOptions).toHaveBeenCalledWith(SomeUser, {
token: "forwardEmailToken", token: "forwardEmailToken",
domain: "example.com", domain: "example.com",
website: null, website: null,
}); });
options.type = "forwarded";
options.forwardedService = "simplelogin";
await generator.saveOptions(options);
expect(simpleLogin.saveOptions).toHaveBeenCalledWith(SomeUser, { expect(simpleLogin.saveOptions).toHaveBeenCalledWith(SomeUser, {
token: "simpleLoginToken", token: "simpleLoginToken",
baseUrl: "https://simplelogin.api.example.com", baseUrl: "https://simplelogin.api.example.com",

View File

@ -1,6 +1,7 @@
import { zip, firstValueFrom, map, concatMap, combineLatest } from "rxjs"; import { zip, firstValueFrom, map, concatMap, combineLatest } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { UserId } from "@bitwarden/common/types/guid";
import { import {
ApiOptions, ApiOptions,
EmailDomainOptions, EmailDomainOptions,
@ -13,6 +14,8 @@ import {
EffUsernameGenerationOptions, EffUsernameGenerationOptions,
Forwarders, Forwarders,
SubaddressGenerationOptions, SubaddressGenerationOptions,
UsernameGeneratorType,
ForwarderId,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorNavigationService, GeneratorNavigation } from "@bitwarden/generator-navigation"; import { GeneratorNavigationService, GeneratorNavigation } from "@bitwarden/generator-navigation";
@ -178,28 +181,76 @@ export class LegacyUsernameGenerationService implements UsernameGenerationServic
const stored = this.toStoredOptions(options); const stored = this.toStoredOptions(options);
const activeAccount = await firstValueFrom(this.accountService.activeAccount$); const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
const saved = await this.saveGeneratorOptions(activeAccount.id, options.type, stored);
if (!saved) {
await this.saveForwarderOptions(activeAccount.id, options.forwardedService, stored);
}
// run navigation options 2nd so that navigation options update doesn't race the `saved options`
// update in Firefox.
await this.saveNavigationOptions(activeAccount.id, stored);
}
private async saveNavigationOptions(account: UserId, options: MappedOptions) {
// generator settings needs to preserve whether password or passphrase is selected, // generator settings needs to preserve whether password or passphrase is selected,
// so `navigationOptions` is mutated. // so `navigationOptions` is mutated.
const navigationOptions$ = zip( const navigationOptions$ = zip(
this.navigation.options$(activeAccount.id), this.navigation.options$(account),
this.navigation.defaults$(activeAccount.id), this.navigation.defaults$(account),
).pipe(map(([options, defaults]) => options ?? defaults)); ).pipe(map(([options, defaults]) => options ?? defaults));
let navigationOptions = await firstValueFrom(navigationOptions$);
navigationOptions = Object.assign(navigationOptions, stored.generator);
await this.navigation.saveOptions(activeAccount.id, navigationOptions);
// overwrite all other settings with latest values let navigationOptions = await firstValueFrom(navigationOptions$);
await Promise.all([ navigationOptions = Object.assign(navigationOptions, options.generator);
this.catchall.saveOptions(activeAccount.id, stored.algorithms.catchall), await this.navigation.saveOptions(account, navigationOptions);
this.effUsername.saveOptions(activeAccount.id, stored.algorithms.effUsername), }
this.subaddress.saveOptions(activeAccount.id, stored.algorithms.subaddress),
this.addyIo.saveOptions(activeAccount.id, stored.forwarders.addyIo), private async saveGeneratorOptions(
this.duckDuckGo.saveOptions(activeAccount.id, stored.forwarders.duckDuckGo), account: UserId,
this.fastmail.saveOptions(activeAccount.id, stored.forwarders.fastmail), type: UsernameGeneratorType,
this.firefoxRelay.saveOptions(activeAccount.id, stored.forwarders.firefoxRelay), options: MappedOptions,
this.forwardEmail.saveOptions(activeAccount.id, stored.forwarders.forwardEmail), ) {
this.simpleLogin.saveOptions(activeAccount.id, stored.forwarders.simpleLogin), switch (type) {
]); case "word":
await this.effUsername.saveOptions(account, options.algorithms.effUsername);
return true;
case "subaddress":
await this.subaddress.saveOptions(account, options.algorithms.subaddress);
return true;
case "catchall":
await this.catchall.saveOptions(account, options.algorithms.catchall);
return true;
default:
return false;
}
}
private async saveForwarderOptions(
account: UserId,
forwarder: ForwarderId | "",
options: MappedOptions,
) {
switch (forwarder) {
case "anonaddy":
await this.addyIo.saveOptions(account, options.forwarders.addyIo);
return true;
case "duckduckgo":
await this.duckDuckGo.saveOptions(account, options.forwarders.duckDuckGo);
return true;
case "fastmail":
await this.fastmail.saveOptions(account, options.forwarders.fastmail);
return true;
case "firefoxrelay":
await this.firefoxRelay.saveOptions(account, options.forwarders.firefoxRelay);
return true;
case "forwardemail":
await this.forwardEmail.saveOptions(account, options.forwarders.forwardEmail);
return true;
case "simplelogin":
await this.simpleLogin.saveOptions(account, options.forwarders.simpleLogin);
return true;
default:
return false;
}
} }
private toStoredOptions(options: UsernameGeneratorOptions) { private toStoredOptions(options: UsernameGeneratorOptions) {