-
+
@@ -33,16 +34,19 @@
[appA11yTitle]="credentialTypeCopyLabel$ | async"
[appCopyClick]="value$ | async"
[valueLabel]="credentialTypeLabel$ | async"
+ [disabled]="!(algorithm$ | async)"
>
();
const activeIdentifier$ = new Subject();
@@ -385,7 +384,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
if (!a || a.onlyOnRequest) {
this.value$.next("-");
} else {
- this.generate("autogenerate");
+ this.generate("autogenerate").catch((e: unknown) => this.logService.error(e));
}
});
});
@@ -495,7 +494,7 @@ export class CredentialGeneratorComponent 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) {
this.generate$.next(requestor);
}
@@ -510,6 +509,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
private readonly destroyed = new Subject();
ngOnDestroy() {
+ this.destroyed.next();
this.destroyed.complete();
// finalize subjects
diff --git a/libs/tools/generator/components/src/forwarder-settings.component.html b/libs/tools/generator/components/src/forwarder-settings.component.html
index 64566fa9562..0e15c2e89ac 100644
--- a/libs/tools/generator/components/src/forwarder-settings.component.html
+++ b/libs/tools/generator/components/src/forwarder-settings.component.html
@@ -1,16 +1,28 @@
diff --git a/libs/tools/generator/components/src/forwarder-settings.component.ts b/libs/tools/generator/components/src/forwarder-settings.component.ts
index 67e93c611ee..f1caf91ade1 100644
--- a/libs/tools/generator/components/src/forwarder-settings.component.ts
+++ b/libs/tools/generator/components/src/forwarder-settings.component.ts
@@ -17,7 +17,6 @@ import {
skip,
Subject,
switchAll,
- switchMap,
takeUntil,
withLatestFrom,
} from "rxjs";
@@ -33,7 +32,7 @@ import {
toCredentialGeneratorConfiguration,
} from "@bitwarden/generator-core";
-import { completeOnAccountSwitch, toValidators } from "./util";
+import { completeOnAccountSwitch } from "./util";
const Controls = Object.freeze({
domain: "domain",
@@ -117,35 +116,17 @@ export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy
this.settings.patchValue(settings as any, { emitEvent: false });
});
- // bind policy to the reactive form
- forwarder$
- .pipe(
- switchMap((forwarder) => {
- const constraints$ = this.generatorService
- .policy$(forwarder, { userId$: singleUserId$ })
- .pipe(map(({ constraints }) => [constraints, forwarder] as const));
-
- return constraints$;
- }),
- takeUntil(this.destroyed$),
- )
- .subscribe(([constraints, forwarder]) => {
- for (const name in Controls) {
- const control = this.settings.get(name);
- if (forwarder.request.includes(name as any)) {
- control.enable({ emitEvent: false });
- control.setValidators(
- // the configuration's type erasure affects `toValidators` as well
- toValidators(name, forwarder, constraints),
- );
- } else {
- control.disable({ emitEvent: false });
- control.clearValidators();
- }
+ // enable requested forwarder inputs
+ forwarder$.pipe(takeUntil(this.destroyed$)).subscribe((forwarder) => {
+ for (const name in Controls) {
+ const control = this.settings.get(name);
+ if (forwarder.request.includes(name as any)) {
+ control.enable({ emitEvent: false });
+ } else {
+ control.disable({ emitEvent: false });
}
-
- this.settings.updateValueAndValidity({ emitEvent: false });
- });
+ }
+ });
// the first emission is the current value; subsequent emissions are updates
settings$$
@@ -157,13 +138,18 @@ export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy
.subscribe(this.onUpdated);
// now that outputs are set up, connect inputs
- this.settings.valueChanges
- .pipe(withLatestFrom(settings$$), takeUntil(this.destroyed$))
- .subscribe(([value, settings]) => {
+ this.saveSettings
+ .pipe(withLatestFrom(this.settings.valueChanges, settings$$), takeUntil(this.destroyed$))
+ .subscribe(([, value, settings]) => {
settings.next(value);
});
}
+ private saveSettings = new Subject();
+ save(site: string = "component api call") {
+ this.saveSettings.next(site);
+ }
+
ngOnChanges(changes: SimpleChanges): void {
this.refresh$.complete();
if ("forwarder" in changes) {
@@ -192,6 +178,7 @@ export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy
private readonly destroyed$ = new Subject();
ngOnDestroy(): void {
+ this.destroyed$.next();
this.destroyed$.complete();
}
}
diff --git a/libs/tools/generator/components/src/generator.module.ts b/libs/tools/generator/components/src/generator.module.ts
index 2d1cedca400..e73d687d7dd 100644
--- a/libs/tools/generator/components/src/generator.module.ts
+++ b/libs/tools/generator/components/src/generator.module.ts
@@ -7,6 +7,7 @@ import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
import { SafeInjectionToken } from "@bitwarden/angular/services/injection-tokens";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { StateProvider } from "@bitwarden/common/platform/state";
@@ -79,6 +80,7 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer");
I18nService,
EncryptService,
KeyService,
+ AccountService,
],
}),
],
diff --git a/libs/tools/generator/components/src/passphrase-settings.component.html b/libs/tools/generator/components/src/passphrase-settings.component.html
index d089de7a07b..4e073f34243 100644
--- a/libs/tools/generator/components/src/passphrase-settings.component.html
+++ b/libs/tools/generator/components/src/passphrase-settings.component.html
@@ -7,7 +7,13 @@
{{ "numWords" | i18n }}
-
+
{{ numWordsBoundariesHint$ | async }}
@@ -16,14 +22,33 @@
{{ "wordSeparator" | i18n }}
-
+
-
+
{{ "capitalize" | i18n }}
-
+
{{ "includeNumber" | i18n }}
{{ "generatorPolicyInEffect" | i18n }}
diff --git a/libs/tools/generator/components/src/passphrase-settings.component.ts b/libs/tools/generator/components/src/passphrase-settings.component.ts
index d65e897f4e1..f2f1749cb62 100644
--- a/libs/tools/generator/components/src/passphrase-settings.component.ts
+++ b/libs/tools/generator/components/src/passphrase-settings.component.ts
@@ -1,7 +1,15 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core";
import { FormBuilder } from "@angular/forms";
-import { BehaviorSubject, skip, takeUntil, Subject, ReplaySubject } from "rxjs";
+import {
+ BehaviorSubject,
+ skip,
+ takeUntil,
+ Subject,
+ map,
+ withLatestFrom,
+ ReplaySubject,
+} from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -12,7 +20,7 @@ import {
PassphraseGenerationOptions,
} from "@bitwarden/generator-core";
-import { completeOnAccountSwitch, toValidators } from "./util";
+import { completeOnAccountSwitch } from "./util";
const Controls = Object.freeze({
numWords: "numWords",
@@ -81,21 +89,12 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
// the first emission is the current value; subsequent emissions are updates
settings.pipe(skip(1), takeUntil(this.destroyed$)).subscribe(this.onUpdated);
- // dynamic policy enforcement
+ // explain policy & disable policy-overridden fields
this.generatorService
.policy$(Generators.passphrase, { userId$: singleUserId$ })
.pipe(takeUntil(this.destroyed$))
.subscribe(({ constraints }) => {
- this.settings
- .get(Controls.numWords)
- .setValidators(toValidators(Controls.numWords, Generators.passphrase, constraints));
-
- this.settings
- .get(Controls.wordSeparator)
- .setValidators(toValidators(Controls.wordSeparator, Generators.passphrase, constraints));
-
- this.settings.updateValueAndValidity({ emitEvent: false });
-
+ this.wordSeparatorMaxLength = constraints.wordSeparator.maxLength;
this.policyInEffect = constraints.policyInEffect;
this.toggleEnabled(Controls.capitalize, !constraints.capitalize?.readonly);
@@ -110,7 +109,21 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
});
// now that outputs are set up, connect inputs
- this.settings.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(settings);
+ this.saveSettings
+ .pipe(
+ withLatestFrom(this.settings.valueChanges),
+ map(([, settings]) => settings),
+ takeUntil(this.destroyed$),
+ )
+ .subscribe(settings);
+ }
+
+ /** attribute binding for wordSeparator[maxlength] */
+ protected wordSeparatorMaxLength: number;
+
+ private saveSettings = new Subject();
+ save(site: string = "component api call") {
+ this.saveSettings.next(site);
}
/** display binding for enterprise policy notice */
@@ -144,6 +157,7 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
private readonly destroyed$ = new Subject();
ngOnDestroy(): void {
+ this.destroyed$.next();
this.destroyed$.complete();
}
}
diff --git a/libs/tools/generator/components/src/password-generator.component.html b/libs/tools/generator/components/src/password-generator.component.html
index 6726df30855..81e18ed02a9 100644
--- a/libs/tools/generator/components/src/password-generator.component.html
+++ b/libs/tools/generator/components/src/password-generator.component.html
@@ -20,6 +20,7 @@
buttonType="main"
(click)="generate('user request')"
[appA11yTitle]="credentialTypeGenerateLabel$ | async"
+ [disabled]="!(algorithm$ | async)"
>
{{ credentialTypeGenerateLabel$ | async }}
@@ -31,10 +32,12 @@
[appA11yTitle]="credentialTypeCopyLabel$ | async"
[appCopyClick]="value$ | async"
[valueLabel]="credentialTypeLabel$ | async"
+ [disabled]="!(algorithm$ | async)"
>