mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-22 07:50:04 +02:00
fix service dropdown not selecting a forwarder
This commit is contained in:
parent
994877c005
commit
11dc419780
@ -25,8 +25,6 @@ import {
|
|||||||
CredentialAlgorithm,
|
CredentialAlgorithm,
|
||||||
CredentialCategory,
|
CredentialCategory,
|
||||||
CredentialGeneratorService,
|
CredentialGeneratorService,
|
||||||
EmailAlgorithm,
|
|
||||||
ForwarderIntegration,
|
|
||||||
GeneratedCredential,
|
GeneratedCredential,
|
||||||
Generators,
|
Generators,
|
||||||
getForwarderConfiguration,
|
getForwarderConfiguration,
|
||||||
@ -34,21 +32,14 @@ import {
|
|||||||
isForwarderIntegration,
|
isForwarderIntegration,
|
||||||
isPasswordAlgorithm,
|
isPasswordAlgorithm,
|
||||||
isUsernameAlgorithm,
|
isUsernameAlgorithm,
|
||||||
PasswordAlgorithm,
|
|
||||||
toCredentialGeneratorConfiguration,
|
toCredentialGeneratorConfiguration,
|
||||||
UsernameAlgorithm,
|
|
||||||
} from "@bitwarden/generator-core";
|
} from "@bitwarden/generator-core";
|
||||||
|
|
||||||
/** root category that drills into username and email categories */
|
// constants used to identify navigation selections that are not
|
||||||
|
// generator algorithms
|
||||||
const IDENTIFIER = "identifier";
|
const IDENTIFIER = "identifier";
|
||||||
/** options available for the top-level navigation */
|
|
||||||
type RootNavValue = PasswordAlgorithm | typeof IDENTIFIER;
|
|
||||||
|
|
||||||
const FORWARDER = "forwarder";
|
const FORWARDER = "forwarder";
|
||||||
type UsernameNavValue = UsernameAlgorithm | EmailAlgorithm | typeof FORWARDER;
|
|
||||||
|
|
||||||
const NONE_SELECTED = "none";
|
const NONE_SELECTED = "none";
|
||||||
type ForwarderNavValue = ForwarderIntegration | typeof NONE_SELECTED;
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "tools-credential-generator",
|
selector: "tools-credential-generator",
|
||||||
@ -73,11 +64,11 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
@Output()
|
@Output()
|
||||||
readonly onGenerated = new EventEmitter<GeneratedCredential>();
|
readonly onGenerated = new EventEmitter<GeneratedCredential>();
|
||||||
|
|
||||||
protected root$ = new BehaviorSubject<{ nav: RootNavValue }>({
|
protected root$ = new BehaviorSubject<{ nav: string }>({
|
||||||
nav: null,
|
nav: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
protected onRootChanged(value: { nav: RootNavValue }) {
|
protected onRootChanged(value: { nav: string }) {
|
||||||
// prevent subscription cycle
|
// prevent subscription cycle
|
||||||
if (this.root$.value.nav !== value.nav) {
|
if (this.root$.value.nav !== value.nav) {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
@ -87,11 +78,11 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected username = this.formBuilder.group({
|
protected username = this.formBuilder.group({
|
||||||
nav: [null as UsernameNavValue],
|
nav: [null as string],
|
||||||
});
|
});
|
||||||
|
|
||||||
protected forwarder = this.formBuilder.group({
|
protected forwarder = this.formBuilder.group({
|
||||||
nav: [null as ForwarderNavValue],
|
nav: [null as string],
|
||||||
});
|
});
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -112,11 +103,11 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
.pipe(
|
.pipe(
|
||||||
map((algorithms) => {
|
map((algorithms) => {
|
||||||
const usernames = algorithms.filter((a) => !isForwarderIntegration(a.id));
|
const usernames = algorithms.filter((a) => !isForwarderIntegration(a.id));
|
||||||
const usernameOptions = this.toOptions(usernames) as Option<UsernameNavValue>[];
|
const usernameOptions = this.toOptions(usernames);
|
||||||
usernameOptions.push({ value: FORWARDER, label: this.i18nService.t("forwardedEmail") });
|
usernameOptions.push({ value: FORWARDER, label: this.i18nService.t("forwardedEmail") });
|
||||||
|
|
||||||
const forwarders = algorithms.filter((a) => isForwarderIntegration(a.id));
|
const forwarders = algorithms.filter((a) => isForwarderIntegration(a.id));
|
||||||
const forwarderOptions = this.toOptions(forwarders) as Option<ForwarderNavValue>[];
|
const forwarderOptions = this.toOptions(forwarders);
|
||||||
forwarderOptions.unshift({ value: NONE_SELECTED, label: this.i18nService.t("select") });
|
forwarderOptions.unshift({ value: NONE_SELECTED, label: this.i18nService.t("select") });
|
||||||
|
|
||||||
return [usernameOptions, forwarderOptions] as const;
|
return [usernameOptions, forwarderOptions] as const;
|
||||||
@ -132,7 +123,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
.algorithms$("password", { userId$: this.userId$ })
|
.algorithms$("password", { userId$: this.userId$ })
|
||||||
.pipe(
|
.pipe(
|
||||||
map((algorithms) => {
|
map((algorithms) => {
|
||||||
const options = this.toOptions(algorithms) as Option<RootNavValue>[];
|
const options = this.toOptions(algorithms);
|
||||||
options.push({ value: IDENTIFIER, label: this.i18nService.t("username") });
|
options.push({ value: IDENTIFIER, label: this.i18nService.t("username") });
|
||||||
return options;
|
return options;
|
||||||
}),
|
}),
|
||||||
@ -182,7 +173,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const username$ = new Subject<{ nav: UsernameNavValue }>();
|
const username$ = new Subject<{ nav: string }>();
|
||||||
this.root$
|
this.root$
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(({ nav }) => !!nav),
|
filter(({ nav }) => !!nav),
|
||||||
@ -190,7 +181,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
if (maybeAlgorithm.nav === IDENTIFIER) {
|
if (maybeAlgorithm.nav === IDENTIFIER) {
|
||||||
return concat(of(this.username.value), this.username.valueChanges);
|
return concat(of(this.username.value), this.username.valueChanges);
|
||||||
} else {
|
} else {
|
||||||
return of(maybeAlgorithm as { nav: CredentialAlgorithm });
|
return of(maybeAlgorithm);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
takeUntil(this.destroyed),
|
takeUntil(this.destroyed),
|
||||||
@ -205,27 +196,28 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
if (maybeAlgorithm.nav === FORWARDER) {
|
if (maybeAlgorithm.nav === FORWARDER) {
|
||||||
return concat(of(this.forwarder.value), this.forwarder.valueChanges);
|
return concat(of(this.forwarder.value), this.forwarder.valueChanges);
|
||||||
} else {
|
} else {
|
||||||
return of(maybeAlgorithm as { nav: CredentialAlgorithm });
|
return of(maybeAlgorithm);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
map((maybeAlgorithm) => {
|
map((maybeAlgorithm) => {
|
||||||
if (maybeAlgorithm.nav === NONE_SELECTED) {
|
if (maybeAlgorithm.nav === NONE_SELECTED) {
|
||||||
return { nav: null };
|
return { nav: null };
|
||||||
} else {
|
} else {
|
||||||
return maybeAlgorithm as { nav: CredentialAlgorithm };
|
return maybeAlgorithm;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
filter(({ nav }) => !!nav),
|
filter(({ nav }) => !!nav),
|
||||||
withLatestFrom(preferences),
|
withLatestFrom(preferences),
|
||||||
takeUntil(this.destroyed),
|
takeUntil(this.destroyed),
|
||||||
)
|
)
|
||||||
.subscribe(([{ nav: algorithm }, preference]) => {
|
.subscribe(([{ nav }, preference]) => {
|
||||||
function setPreference(category: CredentialCategory) {
|
function setPreference(category: CredentialCategory) {
|
||||||
const p = preference[category];
|
const p = preference[category];
|
||||||
p.algorithm = algorithm;
|
p.algorithm = algorithm;
|
||||||
p.updated = new Date();
|
p.updated = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const algorithm = JSON.parse(nav);
|
||||||
// `is*Algorithm` decides `algorithm`'s type, which flows into `setPreference`
|
// `is*Algorithm` decides `algorithm`'s type, which flows into `setPreference`
|
||||||
if (isForwarderIntegration(algorithm) && algorithm.forwarder === null) {
|
if (isForwarderIntegration(algorithm) && algorithm.forwarder === null) {
|
||||||
return;
|
return;
|
||||||
@ -261,19 +253,22 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
// populate the form with the user's preferences to kick off interactivity
|
// populate the form with the user's preferences to kick off interactivity
|
||||||
preferences.pipe(takeUntil(this.destroyed)).subscribe(({ email, username, password }) => {
|
preferences.pipe(takeUntil(this.destroyed)).subscribe(({ email, username, password }) => {
|
||||||
// the last preference set by the user "wins"
|
// the last preference set by the user "wins"
|
||||||
const forwarderPref = isForwarderIntegration(email.algorithm) ? email : null;
|
let forwarderPref = null;
|
||||||
|
let forwarderId: IntegrationId = null;
|
||||||
|
if (isForwarderIntegration(email.algorithm)) {
|
||||||
|
forwarderPref = email;
|
||||||
|
forwarderId = email.algorithm.forwarder;
|
||||||
|
}
|
||||||
const usernamePref = email.updated > username.updated ? email : username;
|
const usernamePref = email.updated > username.updated ? email : username;
|
||||||
const rootPref = usernamePref.updated > password.updated ? usernamePref : password;
|
const rootPref = usernamePref.updated > password.updated ? usernamePref : password;
|
||||||
|
|
||||||
// inject drilldown flags
|
// inject drilldown flags
|
||||||
const forwarderNav = !forwarderPref
|
const forwarderNav = !forwarderPref ? NONE_SELECTED : JSON.stringify(forwarderPref.algorithm);
|
||||||
? NONE_SELECTED
|
const userNav = forwarderPref ? FORWARDER : JSON.stringify(usernamePref.algorithm);
|
||||||
: (forwarderPref.algorithm as ForwarderIntegration);
|
|
||||||
const userNav = forwarderPref ? FORWARDER : (usernamePref.algorithm as UsernameAlgorithm);
|
|
||||||
const rootNav =
|
const rootNav =
|
||||||
rootPref.algorithm == usernamePref.algorithm
|
rootPref.algorithm == usernamePref.algorithm
|
||||||
? IDENTIFIER
|
? IDENTIFIER
|
||||||
: (rootPref.algorithm as PasswordAlgorithm);
|
: JSON.stringify(rootPref.algorithm);
|
||||||
|
|
||||||
// update navigation; break subscription loop
|
// update navigation; break subscription loop
|
||||||
this.onRootChanged({ nav: rootNav });
|
this.onRootChanged({ nav: rootNav });
|
||||||
@ -292,7 +287,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
this.showForwarder$.next(showForwarder);
|
this.showForwarder$.next(showForwarder);
|
||||||
|
|
||||||
if (showForwarder && forwarderNav !== NONE_SELECTED) {
|
if (showForwarder && forwarderNav !== NONE_SELECTED) {
|
||||||
this.forwarderId$.next(forwarderNav.forwarder);
|
this.forwarderId$.next(forwarderId);
|
||||||
} else {
|
} else {
|
||||||
this.forwarderId$.next(null);
|
this.forwarderId$.next(null);
|
||||||
}
|
}
|
||||||
@ -342,14 +337,19 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
throw new Error(`Invalid generator type: "${type}"`);
|
throw new Error(`Invalid generator type: "${type}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Lists the top-level credential types supported by the component. */
|
/** Lists the top-level credential types supported by the component.
|
||||||
protected rootOptions$ = new BehaviorSubject<Option<RootNavValue>[]>([]);
|
* @remarks This is string-typed because angular doesn't support
|
||||||
|
* structural equality for objects, which prevents `CredentialAlgorithm`
|
||||||
|
* from being selectable within a dropdown when its value contains a
|
||||||
|
* `ForwarderIntegration`.
|
||||||
|
*/
|
||||||
|
protected rootOptions$ = new BehaviorSubject<Option<string>[]>([]);
|
||||||
|
|
||||||
/** Lists the credential types of the username algorithm box. */
|
/** Lists the credential types of the username algorithm box. */
|
||||||
protected usernameOptions$ = new BehaviorSubject<Option<UsernameNavValue>[]>([]);
|
protected usernameOptions$ = new BehaviorSubject<Option<string>[]>([]);
|
||||||
|
|
||||||
/** Lists the credential types of the username algorithm box. */
|
/** Lists the credential types of the username algorithm box. */
|
||||||
protected forwarderOptions$ = new BehaviorSubject<Option<ForwarderNavValue>[]>([]);
|
protected forwarderOptions$ = new BehaviorSubject<Option<string>[]>([]);
|
||||||
|
|
||||||
/** Tracks the currently selected forwarder. */
|
/** Tracks the currently selected forwarder. */
|
||||||
protected forwarderId$ = new BehaviorSubject<IntegrationId>(null);
|
protected forwarderId$ = new BehaviorSubject<IntegrationId>(null);
|
||||||
@ -381,8 +381,8 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
|||||||
protected readonly generate$ = new Subject<void>();
|
protected readonly generate$ = new Subject<void>();
|
||||||
|
|
||||||
private toOptions(algorithms: AlgorithmInfo[]) {
|
private toOptions(algorithms: AlgorithmInfo[]) {
|
||||||
const options: Option<CredentialAlgorithm>[] = algorithms.map((algorithm) => ({
|
const options: Option<string>[] = algorithms.map((algorithm) => ({
|
||||||
value: algorithm.id,
|
value: JSON.stringify(algorithm.id),
|
||||||
label: algorithm.name,
|
label: algorithm.name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user