diff --git a/libs/tools/generator/components/src/catchall-settings.component.html b/libs/tools/generator/components/src/catchall-settings.component.html
index 0b2a9e69ef39..7438da6c468e 100644
--- a/libs/tools/generator/components/src/catchall-settings.component.html
+++ b/libs/tools/generator/components/src/catchall-settings.component.html
@@ -1,6 +1,6 @@
diff --git a/libs/tools/generator/components/src/catchall-settings.component.ts b/libs/tools/generator/components/src/catchall-settings.component.ts
index 55ddc1f8102c..379b6d4c0de8 100644
--- a/libs/tools/generator/components/src/catchall-settings.component.ts
+++ b/libs/tools/generator/components/src/catchall-settings.component.ts
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { FormBuilder } from "@angular/forms";
-import { BehaviorSubject, skip, Subject, takeUntil } from "rxjs";
+import { BehaviorSubject, map, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { UserId } from "@bitwarden/common/types/guid";
@@ -12,6 +12,11 @@ import {
import { completeOnAccountSwitch } from "./util";
+/** Splits an email into a username, subaddress, and domain named group.
+ * Subaddress is optional.
+ */
+export const DOMAIN_PARSER = new RegExp("[^@]+@(?.+)");
+
/** Options group for catchall emails */
@Component({
selector: "tools-catchall-settings",
@@ -53,14 +58,43 @@ export class CatchallSettingsComponent implements OnInit, OnDestroy {
const singleUserId$ = this.singleUserId$();
const settings = await this.generatorService.settings(Generators.catchall, { singleUserId$ });
- settings.pipe(takeUntil(this.destroyed$)).subscribe((s) => {
- this.settings.patchValue(s, { emitEvent: false });
- });
+ settings
+ .pipe(
+ withLatestFrom(this.accountService.activeAccount$),
+ map(([settings, activeAccount]) => {
+ // if the subaddress isn't specified, copy it from
+ // the user's settings
+ if ((settings.catchallDomain ?? "").trim().length < 1) {
+ const parsed = DOMAIN_PARSER.exec(activeAccount.email);
+ if (parsed) {
+ settings.catchallDomain = parsed.groups.domain;
+ }
+ }
+
+ return settings;
+ }),
+ takeUntil(this.destroyed$),
+ )
+ .subscribe((s) => {
+ this.settings.patchValue(s, { emitEvent: false });
+ });
// the first emission is the current value; subsequent emissions are updates
settings.pipe(skip(1), takeUntil(this.destroyed$)).subscribe(this.onUpdated);
- this.settings.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(settings);
+ // now that outputs are set up, connect inputs
+ this.saveSettings
+ .pipe(
+ withLatestFrom(this.settings.valueChanges),
+ map(([, settings]) => settings),
+ takeUntil(this.destroyed$),
+ )
+ .subscribe(settings);
+ }
+
+ private saveSettings = new Subject();
+ save(site: string = "component api call") {
+ this.saveSettings.next(site);
}
private singleUserId$() {
@@ -78,6 +112,7 @@ export class CatchallSettingsComponent implements OnInit, OnDestroy {
private readonly destroyed$ = new Subject();
ngOnDestroy(): void {
+ this.destroyed$.next();
this.destroyed$.complete();
}
}
diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts
index 2ccad318d756..211eef8301c6 100644
--- a/libs/tools/generator/components/src/credential-generator.component.ts
+++ b/libs/tools/generator/components/src/credential-generator.component.ts
@@ -538,6 +538,10 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
await this.passwordComponent.save("credential generator");
}
+ if (this.catchallComponent) {
+ await this.catchallComponent.save("credential generator");
+ }
+
this.generate$.next(requestor);
}
diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts
index dac3b061d8cf..c67c8d30c140 100644
--- a/libs/tools/generator/components/src/password-generator.component.ts
+++ b/libs/tools/generator/components/src/password-generator.component.ts
@@ -211,13 +211,15 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy {
});
// generate on load unless the generator prohibits it
- this.algorithm$
- .pipe(
- distinctUntilChanged((prev, next) => prev.id === next.id),
- filter((a) => !a.onlyOnRequest),
- takeUntil(this.destroyed),
- )
- .subscribe(() => this.generate("autogenerate"));
+ this.algorithm$.pipe(takeUntil(this.destroyed)).subscribe((a) => {
+ this.zone.run(() => {
+ if (!a || a.onlyOnRequest) {
+ this.value$.next("-");
+ } else {
+ this.generate("autogenerate").catch((e: unknown) => this.logService.error(e));
+ }
+ });
+ });
}
private typeToGenerator$(type: CredentialAlgorithm) {
diff --git a/libs/tools/generator/components/src/subaddress-settings.component.ts b/libs/tools/generator/components/src/subaddress-settings.component.ts
index bd6ca899db75..cefba063ceeb 100644
--- a/libs/tools/generator/components/src/subaddress-settings.component.ts
+++ b/libs/tools/generator/components/src/subaddress-settings.component.ts
@@ -59,7 +59,7 @@ export class SubaddressSettingsComponent implements OnInit, OnDestroy {
map(([settings, activeAccount]) => {
// if the subaddress isn't specified, copy it from
// the user's settings
- if ((settings.subaddressEmail ?? "").length < 1) {
+ if ((settings.subaddressEmail ?? "").trim().length < 1) {
settings.subaddressEmail = activeAccount.email;
}
diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts
index 8d67c00778fb..e9175bd9958d 100644
--- a/libs/tools/generator/components/src/username-generator.component.ts
+++ b/libs/tools/generator/components/src/username-generator.component.ts
@@ -348,7 +348,7 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
if (!a || a.onlyOnRequest) {
this.value$.next("-");
} else {
- this.generate("autogenerate");
+ this.generate("autogenerate").catch((e: unknown) => this.logService.error(e));
}
});
});
@@ -440,7 +440,11 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
* @param requestor a label used to trace generation request
* origin in the debugger.
*/
- protected generate(requestor: string) {
+ protected async generate(requestor: string) {
+ if (this.catchallComponent) {
+ await this.catchallComponent.save("credential generator");
+ }
+
this.generate$.next(requestor);
}
@@ -455,6 +459,7 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
private readonly destroyed = new Subject();
ngOnDestroy() {
+ this.destroyed.next();
this.destroyed.complete();
// finalize subjects