1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-18 10:23:52 +00:00

Merge remote-tracking branch 'origin/main' into playwright

This commit is contained in:
Matt Gibson
2026-01-26 12:57:05 -08:00
1790 changed files with 150488 additions and 32025 deletions

View File

@@ -8,15 +8,18 @@ import {
Output,
SimpleChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule } from "@bitwarden/components";
import {
CatchallGenerationOptions,
CredentialGeneratorService,
BuiltIn,
} from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for catchall emails */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({
selector: "tools-catchall-settings",
templateUrl: "catchall-settings.component.html",
standalone: false,
imports: [ReactiveFormsModule, FormFieldModule, JslibModule, I18nPipe],
})
export class CatchallSettingsComponent implements OnInit, OnDestroy, OnChanges {
/** Instantiates the component

View File

@@ -1,8 +1,11 @@
<bit-dialog #dialog background="alt">
<span bitDialogTitle>{{ "generatorHistory" | i18n }}</span>
<ng-container bitDialogContent>
<bit-empty-credential-history *ngIf="!(hasHistory$ | async)" style="display: contents" />
<bit-credential-generator-history [account]="account$ | async" *ngIf="hasHistory$ | async" />
@if (hasHistory$ | async) {
<bit-credential-generator-history [account]="account$ | async" />
} @else {
<bit-empty-credential-history style="display: contents" />
}
</ng-container>
<ng-container bitDialogFooter>
<button

View File

@@ -1,22 +1,24 @@
<bit-item *ngFor="let credential of credentials$ | async">
<bit-item-content>
<bit-color-password class="tw-font-mono" [password]="credential.credential" />
<div slot="secondary">
{{ credential.generationDate | date: "medium" }}
</div>
</bit-item-content>
<ng-container slot="end">
<bit-item-action>
<button
type="button"
bitIconButton="bwi-clone"
[appCopyClick]="credential.credential"
[valueLabel]="getGeneratedValueText(credential)"
[label]="getCopyText(credential)"
showToast
>
{{ getCopyText(credential) }}
</button>
</bit-item-action>
</ng-container>
</bit-item>
@for (credential of credentials$ | async; track credential) {
<bit-item>
<bit-item-content>
<bit-color-password class="tw-font-mono" [password]="credential.credential" />
<div slot="secondary">
{{ credential.generationDate | date: "medium" }}
</div>
</bit-item-content>
<ng-container slot="end">
<bit-item-action>
<button
type="button"
bitIconButton="bwi-clone"
[appCopyClick]="credential.credential"
[valueLabel]="getGeneratedValueText(credential)"
[label]="getCopyText(credential)"
showToast
>
{{ getCopyText(credential) }}
</button>
</bit-item-action>
</ng-container>
</bit-item>
}

View File

@@ -23,7 +23,6 @@ import {
import { AlgorithmsByType, CredentialGeneratorService } from "@bitwarden/generator-core";
import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history";
import { GeneratorModule } from "./generator.module";
import { translate } from "./util";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -32,13 +31,12 @@ import { translate } from "./util";
selector: "bit-credential-generator-history",
templateUrl: "credential-generator-history.component.html",
imports: [
ColorPasswordModule,
CommonModule,
ColorPasswordModule,
IconButtonModule,
NoItemsModule,
JslibModule,
ItemModule,
GeneratorModule,
],
})
export class CredentialGeneratorHistoryComponent implements OnChanges, OnInit, OnDestroy {

View File

@@ -6,9 +6,11 @@
(selectedChange)="onRootChanged({ nav: $event })"
attr.aria-label="{{ 'type' | i18n }}"
>
<bit-toggle *ngFor="let option of rootOptions$ | async" [value]="option.value">
{{ option.label }}
</bit-toggle>
@for (option of rootOptions$ | async; track option) {
<bit-toggle [value]="option.value">
{{ option.label }}
</bit-toggle>
}
</bit-toggle-group>
<nudge-generator-spotlight></nudge-generator-spotlight>
@@ -40,69 +42,80 @@
></button>
</div>
</bit-card>
<tools-password-settings
class="tw-mt-6"
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.password"
[account]="account$ | async"
(onUpdated)="generate('password settings')"
/>
<tools-passphrase-settings
class="tw-mt-6"
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.passphrase"
[account]="account$ | async"
(onUpdated)="generate('passphrase settings')"
/>
<bit-section *ngIf="(category$ | async) !== 'password'">
<bit-section-header>
<h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header>
<div class="tw-mb-4">
<bit-card>
<form [formGroup]="username" class="tw-container">
<bit-form-field>
<bit-label>{{ "type" | i18n }}</bit-label>
<bit-select
[items]="usernameOptions$ | async"
formControlName="nav"
data-testid="username-type"
>
</bit-select>
<bit-hint *ngIf="!!(credentialTypeHint$ | async)">{{
credentialTypeHint$ | async
}}</bit-hint>
</bit-form-field>
</form>
<form *ngIf="showForwarder$ | async" [formGroup]="forwarder" class="tw-container">
<bit-form-field>
<bit-label>{{ "service" | i18n }}</bit-label>
<bit-select
[items]="forwarderOptions$ | async"
formControlName="nav"
data-testid="email-forwarding-service"
>
</bit-select>
</bit-form-field>
</form>
<tools-catchall-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.catchall"
[account]="account$ | async"
(onUpdated)="generate('catchall settings')"
/>
<tools-forwarder-settings
*ngIf="!!(forwarderId$ | async)"
[account]="account$ | async"
[forwarder]="forwarderId$ | async"
/>
<tools-subaddress-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.plusAddress"
[account]="account$ | async"
(onUpdated)="generate('subaddress settings')"
/>
<tools-username-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.username"
[account]="account$ | async"
(onUpdated)="generate('username settings')"
/>
</bit-card>
</div>
</bit-section>
@let showAlgorithm = showAlgorithm$ | async;
@let account = account$ | async;
@switch (showAlgorithm?.id) {
@case (Algorithm.password) {
<tools-password-settings
class="tw-mt-6"
[account]="account"
(onUpdated)="generate('password settings')"
/>
}
@case (Algorithm.passphrase) {
<tools-passphrase-settings
class="tw-mt-6"
[account]="account"
(onUpdated)="generate('passphrase settings')"
/>
}
}
@if ((category$ | async) !== "password") {
<bit-section>
<bit-section-header>
<h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header>
<div class="tw-mb-4">
<bit-card>
<form [formGroup]="username" class="tw-container">
<bit-form-field>
<bit-label>{{ "type" | i18n }}</bit-label>
<bit-select
[items]="usernameOptions$ | async"
formControlName="nav"
data-testid="username-type"
>
</bit-select>
@if (credentialTypeHint$ | async) {
<bit-hint>{{ credentialTypeHint$ | async }}</bit-hint>
}
</bit-form-field>
</form>
@if (showForwarder$ | async) {
<form [formGroup]="forwarder" class="tw-container">
<bit-form-field>
<bit-label>{{ "service" | i18n }}</bit-label>
<bit-select
[items]="forwarderOptions$ | async"
formControlName="nav"
data-testid="email-forwarding-service"
>
</bit-select>
</bit-form-field>
</form>
}
@if (showAlgorithm?.id === Algorithm.catchall) {
<tools-catchall-settings
[account]="account"
(onUpdated)="generate('catchall settings')"
/>
}
@if (forwarderId$ | async; as forwarderId) {
<tools-forwarder-settings [account]="account" [forwarder]="forwarderId" />
}
@if (showAlgorithm?.id === Algorithm.plusAddress) {
<tools-subaddress-settings
[account]="account"
(onUpdated)="generate('subaddress settings')"
/>
}
@if (showAlgorithm?.id === Algorithm.username) {
<tools-username-settings
[account]="account"
(onUpdated)="generate('username settings')"
/>
}
</bit-card>
</div>
</bit-section>
}

View File

@@ -1,4 +1,5 @@
import { LiveAnnouncer } from "@angular/cdk/a11y";
import { AsyncPipe } from "@angular/common";
import {
Component,
EventEmitter,
@@ -10,7 +11,7 @@ import {
Output,
SimpleChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import {
BehaviorSubject,
catchError,
@@ -27,6 +28,7 @@ import {
withLatestFrom,
} from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -37,7 +39,23 @@ import {
ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components";
import {
ToastService,
Option,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
SectionComponent,
SectionHeaderComponent,
ToggleGroupModule,
TypographyModule,
FormFieldModule,
SelectModule,
} from "@bitwarden/components";
import {
CredentialType,
CredentialGeneratorService,
@@ -55,7 +73,15 @@ import {
Type,
} from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameSettingsComponent } from "./username-settings.component";
import { translate } from "./util";
// constants used to identify navigation selections that are not
@@ -69,7 +95,32 @@ const NONE_SELECTED = "none";
@Component({
selector: "tools-credential-generator",
templateUrl: "credential-generator.component.html",
standalone: false,
imports: [
ToggleGroupModule,
NudgeGeneratorSpotlightComponent,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
PasswordSettingsComponent,
PassphraseSettingsComponent,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
FormFieldModule,
SelectModule,
CatchallSettingsComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
UsernameSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
})
export class CredentialGeneratorComponent implements OnInit, OnChanges, OnDestroy {
private readonly destroyed = new Subject<void>();

View File

@@ -1,28 +1,34 @@
<form [formGroup]="settings" class="tw-container">
<bit-form-field *ngIf="displayDomain">
<bit-label>{{ "forwarderDomainName" | i18n }}</bit-label>
<input
bitInput
formControlName="domain"
type="text"
placeholder="example.com"
(change)="save('domain')"
/>
<bit-hint>{{ "forwarderDomainNameHint" | i18n }}</bit-hint>
</bit-form-field>
<bit-form-field *ngIf="displayToken">
<bit-label>{{ "apiKey" | i18n }}</bit-label>
<input bitInput formControlName="token" type="password" (change)="save('password')" />
<button
type="button"
bitIconButton
bitSuffix
bitPasswordInputToggle
(change)="save('token')"
></button>
</bit-form-field>
<bit-form-field *ngIf="displayBaseUrl" disableMargin>
<bit-label>{{ "selfHostBaseUrl" | i18n }}</bit-label>
<input bitInput formControlName="baseUrl" type="text" (change)="save('baseUrl')" />
</bit-form-field>
@if (displayDomain) {
<bit-form-field>
<bit-label>{{ "forwarderDomainName" | i18n }}</bit-label>
<input
bitInput
formControlName="domain"
type="text"
placeholder="example.com"
(change)="save('domain')"
/>
<bit-hint>{{ "forwarderDomainNameHint" | i18n }}</bit-hint>
</bit-form-field>
}
@if (displayToken) {
<bit-form-field>
<bit-label>{{ "apiKey" | i18n }}</bit-label>
<input bitInput formControlName="token" type="password" (change)="save('password')" />
<button
type="button"
bitIconButton
bitSuffix
bitPasswordInputToggle
(change)="save('token')"
></button>
</bit-form-field>
}
@if (displayBaseUrl) {
<bit-form-field disableMargin>
<bit-label>{{ "selfHostBaseUrl" | i18n }}</bit-label>
<input bitInput formControlName="baseUrl" type="text" (change)="save('baseUrl')" />
</bit-form-field>
}
</form>

View File

@@ -8,16 +8,24 @@ import {
Output,
SimpleChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, switchAll, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { VendorId } from "@bitwarden/common/tools/extension";
import {
FormFieldModule,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
} from "@bitwarden/components";
import {
CredentialGeneratorService,
ForwarderOptions,
GeneratorMetadata,
} from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
const Controls = Object.freeze({
domain: "domain",
@@ -31,7 +39,15 @@ const Controls = Object.freeze({
@Component({
selector: "tools-forwarder-settings",
templateUrl: "forwarder-settings.component.html",
standalone: false,
imports: [
ReactiveFormsModule,
FormFieldModule,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
JslibModule,
I18nPipe,
],
})
export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component

View File

@@ -1,67 +1,13 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { ReactiveFormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
CardComponent,
ColorPasswordModule,
CheckboxModule,
FormFieldModule,
IconButtonModule,
InputModule,
ItemModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
ToggleGroupModule,
TypographyModule,
} from "@bitwarden/components";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { CredentialGeneratorComponent } from "./credential-generator.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { GeneratorServicesModule } from "./generator-services.module";
import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordGeneratorComponent } from "./password-generator.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameGeneratorComponent } from "./username-generator.component";
import { UsernameSettingsComponent } from "./username-settings.component";
/** Shared module containing generator component dependencies */
/** @deprecated Use individual components instead. */
@NgModule({
imports: [
CardComponent,
ColorPasswordModule,
CheckboxModule,
CommonModule,
FormFieldModule,
GeneratorServicesModule,
IconButtonModule,
InputModule,
ItemModule,
JslibModule,
ReactiveFormsModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
ToggleGroupModule,
TypographyModule,
NudgeGeneratorSpotlightComponent,
],
declarations: [
CatchallSettingsComponent,
CredentialGeneratorComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
PasswordGeneratorComponent,
PassphraseSettingsComponent,
PasswordSettingsComponent,
UsernameGeneratorComponent,
UsernameSettingsComponent,
],
imports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
exports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
})
export class GeneratorModule {

View File

@@ -1,5 +1,15 @@
/**
* This file contains the public interface for the generator components library.
*
* Be mindful of what you export here, as those components should be considered stable
* and part of the public API contract.
*/
export { CredentialGeneratorComponent } from "./credential-generator.component";
export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component";
export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component";
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
export { GeneratorModule } from "./generator.module";
export { GeneratorServicesModule, SYSTEM_SERVICE_PROVIDER } from "./generator-services.module";
export { PasswordGeneratorComponent } from "./password-generator.component";
export { UsernameGeneratorComponent } from "./username-generator.component";

View File

@@ -1,16 +1,18 @@
<div class="tw-mb-4" *ngIf="showGeneratorSpotlight$ | async">
<bit-spotlight
[title]="'generatorNudgeTitle' | i18n"
(onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)"
>
<p class="tw-text-main tw-mb-0" bitTypography="body2">
<span class="tw-sr-only">
{{ "generatorNudgeBodyAria" | i18n }}
</span>
<span aria-hidden="true">
{{ "generatorNudgeBodyOne" | i18n }} <i class="bwi bwi-generate"></i>
{{ "generatorNudgeBodyTwo" | i18n }}
</span>
</p>
</bit-spotlight>
</div>
@if (showGeneratorSpotlight$ | async) {
<div class="tw-mb-4">
<bit-spotlight
[title]="'generatorNudgeTitle' | i18n"
(onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)"
>
<p class="tw-text-main tw-mb-0" bitTypography="body2">
<span class="tw-sr-only">
{{ "generatorNudgeBodyAria" | i18n }}
</span>
<span aria-hidden="true">
{{ "generatorNudgeBodyOne" | i18n }} <i class="bwi bwi-generate"></i>
{{ "generatorNudgeBodyTwo" | i18n }}
</span>
</p>
</bit-spotlight>
</div>
}

View File

@@ -1,7 +1,9 @@
<bit-section [disableMargin]="disableMargin">
<bit-section-header *ngIf="showHeader">
<h6 bitTypography="h6">{{ "options" | i18n }}</h6>
</bit-section-header>
@if (showHeader) {
<bit-section-header>
<h6 bitTypography="h6">{{ "options" | i18n }}</h6>
</bit-section-header>
}
<form [formGroup]="settings" class="tw-container">
<div class="tw-mb-4">
<bit-card>
@@ -51,7 +53,9 @@
/>
<bit-label>{{ "includeNumber" | i18n }}</bit-label>
</bit-form-control>
<p *ngIf="policyInEffect" bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
@if (policyInEffect) {
<p bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
}
</bit-card>
</div>
</form>

View File

@@ -1,4 +1,5 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import {
OnInit,
Input,
@@ -9,9 +10,10 @@ import {
SimpleChanges,
OnChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { skip, takeUntil, Subject, map, withLatestFrom, ReplaySubject, tap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -20,11 +22,21 @@ import {
disabledSemanticLoggerProvider,
ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log";
import {
SectionComponent,
SectionHeaderComponent,
BaseCardDirective,
CardComponent,
TypographyModule,
FormFieldModule,
CheckboxModule,
} from "@bitwarden/components";
import {
CredentialGeneratorService,
PassphraseGenerationOptions,
BuiltIn,
} from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
const Controls = Object.freeze({
numWords: "numWords",
@@ -39,7 +51,19 @@ const Controls = Object.freeze({
@Component({
selector: "tools-passphrase-settings",
templateUrl: "passphrase-settings.component.html",
standalone: false,
imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
BaseCardDirective,
CardComponent,
FormFieldModule,
CheckboxModule,
AsyncPipe,
JslibModule,
I18nPipe,
],
})
export class PassphraseSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component

View File

@@ -1,15 +1,18 @@
<bit-toggle-group
fullWidth
class="tw-mb-4"
[selected]="credentialType$ | async"
(selectedChange)="onCredentialTypeChanged($event)"
*ngIf="showCredentialTypes$ | async"
attr.aria-label="{{ 'type' | i18n }}"
>
<bit-toggle *ngFor="let option of passwordOptions$ | async" [value]="option.value">
{{ option.label }}
</bit-toggle>
</bit-toggle-group>
@if (showCredentialTypes$ | async) {
<bit-toggle-group
fullWidth
class="tw-mb-4"
[selected]="credentialType$ | async"
(selectedChange)="onCredentialTypeChanged($event)"
attr.aria-label="{{ 'type' | i18n }}"
>
@for (option of passwordOptions$ | async; track option) {
<bit-toggle [value]="option.value">
{{ option.label }}
</bit-toggle>
}
</bit-toggle-group>
}
<bit-card class="tw-flex tw-justify-between tw-mb-4">
<div class="tw-grow tw-flex tw-items-center tw-min-w-0">
<bit-color-password class="tw-font-mono" [password]="value$ | async"></bit-color-password>
@@ -37,17 +40,19 @@
></button>
</div>
</bit-card>
<tools-password-settings
class="tw-mt-6"
*ngIf="(algorithm$ | async)?.id === Algorithm.password"
[account]="account$ | async"
[disableMargin]="disableMargin"
(onUpdated)="generate('password settings')"
/>
<tools-passphrase-settings
class="tw-mt-6"
*ngIf="(algorithm$ | async)?.id === Algorithm.passphrase"
[account]="account$ | async"
(onUpdated)="generate('passphrase settings')"
[disableMargin]="disableMargin"
/>
@if ((algorithm$ | async)?.id === Algorithm.password) {
<tools-password-settings
class="tw-mt-6"
[account]="account$ | async"
[disableMargin]="disableMargin"
(onUpdated)="generate('password settings')"
/>
}
@if ((algorithm$ | async)?.id === Algorithm.passphrase) {
<tools-passphrase-settings
class="tw-mt-6"
[account]="account$ | async"
(onUpdated)="generate('passphrase settings')"
[disableMargin]="disableMargin"
/>
}

View File

@@ -1,5 +1,6 @@
import { LiveAnnouncer } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import {
Component,
EventEmitter,
@@ -24,6 +25,7 @@ import {
withLatestFrom,
} from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -33,7 +35,18 @@ import {
ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components";
import {
ToastService,
Option,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
ToggleGroupModule,
} from "@bitwarden/components";
import {
CredentialGeneratorService,
GeneratedCredential,
@@ -49,7 +62,10 @@ import {
Profile,
} from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { toAlgorithmInfo, translate } from "./util";
/** Options group for passwords */
@@ -58,7 +74,21 @@ import { toAlgorithmInfo, translate } from "./util";
@Component({
selector: "tools-password-generator",
templateUrl: "password-generator.component.html",
standalone: false,
imports: [
ToggleGroupModule,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
PasswordSettingsComponent,
PassphraseSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
})
export class PasswordGeneratorComponent implements OnInit, OnChanges, OnDestroy {
constructor(

View File

@@ -1,7 +1,9 @@
<bit-section [disableMargin]="disableMargin">
<bit-section-header *ngIf="showHeader">
<h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header>
@if (showHeader) {
<bit-section-header>
<h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header>
}
<form [formGroup]="settings" class="tw-container">
<div class="tw-mb-4">
<bit-card>
@@ -50,11 +52,7 @@
<input bitCheckbox type="checkbox" formControlName="number" (change)="save('number')" />
<bit-label>{{ "numbersLabel" | i18n }}</bit-label>
</bit-form-control>
<bit-form-control
class="!tw-mb-0"
attr.aria-description="{{ 'specialCharactersDescription' | i18n }}"
title="{{ 'specialCharactersDescription' | i18n }}"
>
<bit-form-control class="!tw-mb-0" title="{{ 'specialCharactersDescription' | i18n }}">
<input
bitCheckbox
type="checkbox"
@@ -62,10 +60,15 @@
(change)="save('special')"
/>
<!-- hard-coded the special characters string because `$` indicates an i18n interpolation,
and is handled inconsistently across browsers. Angular template syntax is used to
ensure special characters are entity-encoded.
-->
<bit-label>{{ "!@#$%^&*" }}</bit-label>
and is handled inconsistently across browsers. Angular template syntax is used to
ensure special characters are entity-encoded.
-->
<bit-label>
<span aria-hidden="true">{{ "!@#$%^&*" }}</span>
<span class="tw-sr-only">
{{ "specialCharactersDescription" | i18n }}
</span>
</bit-label>
</bit-form-control>
</div>
<div class="tw-flex">
@@ -97,7 +100,9 @@
/>
<bit-label>{{ "avoidAmbiguous" | i18n }}</bit-label>
</bit-form-control>
<p *ngIf="policyInEffect" bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
@if (policyInEffect) {
<p bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
}
</bit-card>
</div>
</form>

View File

@@ -1,4 +1,5 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import {
OnInit,
Input,
@@ -9,16 +10,27 @@ import {
SimpleChanges,
OnChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { takeUntil, Subject, map, filter, tap, skip, ReplaySubject, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
SectionComponent,
SectionHeaderComponent,
BaseCardDirective,
CardComponent,
FormFieldModule,
TypographyModule,
CheckboxModule,
} from "@bitwarden/components";
import {
CredentialGeneratorService,
PasswordGenerationOptions,
BuiltIn,
} from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
import { hasRangeOfValues } from "./util";
@@ -39,7 +51,19 @@ const Controls = Object.freeze({
@Component({
selector: "tools-password-settings",
templateUrl: "password-settings.component.html",
standalone: false,
imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
BaseCardDirective,
CardComponent,
FormFieldModule,
CheckboxModule,
AsyncPipe,
JslibModule,
I18nPipe,
],
})
export class PasswordSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component

View File

@@ -8,15 +8,18 @@ import {
Output,
SimpleChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule } from "@bitwarden/components";
import {
CredentialGeneratorService,
BuiltIn,
SubaddressGenerationOptions,
} from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for plus-addressed emails */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({
selector: "tools-subaddress-settings",
templateUrl: "subaddress-settings.component.html",
standalone: false,
imports: [ReactiveFormsModule, FormFieldModule, JslibModule, I18nPipe],
})
export class SubaddressSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component

View File

@@ -42,42 +42,41 @@
data-testid="username-type"
>
</bit-select>
<bit-hint *ngIf="!!(credentialTypeHint$ | async)">{{
credentialTypeHint$ | async
}}</bit-hint>
@if (credentialTypeHint$ | async) {
<bit-hint>{{ credentialTypeHint$ | async }}</bit-hint>
}
</bit-form-field>
</form>
<form *ngIf="showForwarder$ | async" [formGroup]="forwarder" class="tw-container">
<bit-form-field>
<bit-label>{{ "service" | i18n }}</bit-label>
<bit-select
[items]="forwarderOptions$ | async"
formControlName="nav"
data-testid="email-forwarding-service"
>
</bit-select>
</bit-form-field>
</form>
<tools-catchall-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.catchall"
[account]="account$ | async"
(onUpdated)="generate('catchall settings')"
/>
<tools-forwarder-settings
*ngIf="!!(forwarderId$ | async)"
[forwarder]="forwarderId$ | async"
[account]="account$ | async"
/>
<tools-subaddress-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.plusAddress"
[account]="account$ | async"
(onUpdated)="generate('subaddress settings')"
/>
<tools-username-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.username"
[account]="account$ | async"
(onUpdated)="generate('username settings')"
/>
@if (showForwarder$ | async) {
<form [formGroup]="forwarder" class="tw-container">
<bit-form-field>
<bit-label>{{ "service" | i18n }}</bit-label>
<bit-select
[items]="forwarderOptions$ | async"
formControlName="nav"
data-testid="email-forwarding-service"
>
</bit-select>
</bit-form-field>
</form>
}
@let showAlgorithm = showAlgorithm$ | async;
@let account = account$ | async;
@if (showAlgorithm?.id === Algorithm.catchall) {
<tools-catchall-settings [account]="account" (onUpdated)="generate('catchall settings')" />
}
@if (forwarderId$ | async; as forwarderId) {
<tools-forwarder-settings [forwarder]="forwarderId" [account]="account" />
}
@if (showAlgorithm?.id === Algorithm.plusAddress) {
<tools-subaddress-settings
[account]="account"
(onUpdated)="generate('subaddress settings')"
/>
}
@if (showAlgorithm?.id === Algorithm.username) {
<tools-username-settings [account]="account" (onUpdated)="generate('username settings')" />
}
</bit-card>
</div>
</bit-section>

View File

@@ -1,5 +1,6 @@
import { LiveAnnouncer } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { NgClass, AsyncPipe } from "@angular/common";
import {
Component,
EventEmitter,
@@ -11,7 +12,7 @@ import {
Output,
SimpleChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import {
BehaviorSubject,
catchError,
@@ -28,6 +29,7 @@ import {
withLatestFrom,
} from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -38,7 +40,22 @@ import {
ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components";
import {
ToastService,
Option,
AriaDisableDirective,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
CopyClickDirective,
BitIconButtonComponent,
TooltipDirective,
SectionComponent,
SectionHeaderComponent,
SelectComponent,
TypographyModule,
FormFieldModule,
} from "@bitwarden/components";
import {
AlgorithmInfo,
CredentialGeneratorService,
@@ -55,7 +72,12 @@ import {
Algorithm,
} from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameSettingsComponent } from "./username-settings.component";
import { toAlgorithmInfo, translate } from "./util";
// constants used to identify navigation selections that are not
@@ -69,7 +91,29 @@ const NONE_SELECTED = "none";
@Component({
selector: "tools-username-generator",
templateUrl: "username-generator.component.html",
standalone: false,
imports: [
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
NgClass,
ReactiveFormsModule,
SelectComponent,
FormFieldModule,
CatchallSettingsComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
UsernameSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
})
export class UsernameGeneratorComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the username generator

View File

@@ -8,15 +8,18 @@ import {
Output,
SimpleChanges,
} from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule, CheckboxModule } from "@bitwarden/components";
import {
CredentialGeneratorService,
EffUsernameGenerationOptions,
BuiltIn,
} from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for usernames */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({
selector: "tools-username-settings",
templateUrl: "username-settings.component.html",
standalone: false,
imports: [ReactiveFormsModule, FormFieldModule, CheckboxModule, JslibModule, I18nPipe],
})
export class UsernameSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component

View File

@@ -7,8 +7,7 @@ import { ForwarderContext } from "../forwarder-context";
export class CreateForwardingAddressRpc<
Settings extends ApiSettings,
Req extends IntegrationRequest = IntegrationRequest,
> implements JsonRpc<Req, string>
{
> implements JsonRpc<Req, string> {
constructor(
readonly requestor: ForwarderConfiguration<Settings>,
readonly context: ForwarderContext<Settings>,

View File

@@ -9,8 +9,7 @@ import { ForwarderContext } from "../forwarder-context";
export class GetAccountIdRpc<
Settings extends ApiSettings,
Req extends IntegrationRequest = IntegrationRequest,
> implements JsonRpc<Req, string>
{
> implements JsonRpc<Req, string> {
constructor(
readonly requestor: ForwarderConfiguration<Settings>,
readonly context: ForwarderContext<Settings>,

View File

@@ -24,6 +24,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
overridePasswordType: override,
},
enabled: true,
revisionDate: new Date().toISOString(),
});
const result = availableAlgorithms([policy]);
@@ -44,6 +45,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
overridePasswordType: override,
},
enabled: true,
revisionDate: new Date().toISOString(),
});
const result = availableAlgorithms([policy, policy]);
@@ -64,6 +66,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
overridePasswordType: "password",
},
enabled: true,
revisionDate: new Date().toISOString(),
});
const passphrase = new Policy({
id: "" as PolicyId,
@@ -73,6 +76,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
overridePasswordType: "passphrase",
},
enabled: true,
revisionDate: new Date().toISOString(),
});
const result = availableAlgorithms([password, passphrase]);
@@ -93,6 +97,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
some: "policy",
},
enabled: true,
revisionDate: new Date().toISOString(),
});
const result = availableAlgorithms([policy]);
@@ -111,6 +116,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
some: "policy",
},
enabled: false,
revisionDate: new Date().toISOString(),
});
const result = availableAlgorithms([policy]);
@@ -129,6 +135,7 @@ describe("availableAlgorithms_vNextPolicy", () => {
some: "policy",
},
enabled: true,
revisionDate: new Date().toISOString(),
});
const result = availableAlgorithms([policy]);

View File

@@ -2,9 +2,10 @@ import { PolicyEvaluator } from "../abstractions";
import { NoPolicy } from "../types";
/** A policy evaluator that does not apply any policy */
export class DefaultPolicyEvaluator<PolicyTarget>
implements PolicyEvaluator<NoPolicy, PolicyTarget>
{
export class DefaultPolicyEvaluator<PolicyTarget> implements PolicyEvaluator<
NoPolicy,
PolicyTarget
> {
/** {@link PolicyEvaluator.policy} */
get policy() {
return {};

View File

@@ -13,9 +13,7 @@ import { atLeast, atLeastSum, maybe, readonlyTrueWhen, AtLeastOne, Zero } from "
import { PasswordPolicyConstraints } from "./password-policy-constraints";
/** Creates state constraints by blending policy and password settings. */
export class DynamicPasswordPolicyConstraints
implements DynamicStateConstraints<PasswordGeneratorSettings>
{
export class DynamicPasswordPolicyConstraints implements DynamicStateConstraints<PasswordGeneratorSettings> {
/** Instantiates the object.
* @param policy the password policy to enforce. This cannot be
* `null` or `undefined`.

View File

@@ -17,6 +17,7 @@ function createPolicy(
data,
enabled,
type,
revisionDate: new Date().toISOString(),
});
}

View File

@@ -17,6 +17,7 @@ function createPolicy(
data,
enabled,
type,
revisionDate: new Date().toISOString(),
});
}

View File

@@ -25,7 +25,11 @@ import { deepFreeze } from "@bitwarden/common/tools/util";
import { UserId } from "@bitwarden/common/types/guid";
import { BitwardenClient } from "@bitwarden/sdk-internal";
import { FakeAccountService, FakeStateProvider } from "../../../../../common/spec";
import {
FakeAccountService,
FakeStateProvider,
mockAccountInfoWith,
} from "../../../../../common/spec";
import { Algorithm, AlgorithmsByType, CredentialAlgorithm, Type, Types } from "../metadata";
import catchall from "../metadata/email/catchall";
import plusAddress from "../metadata/email/plus-address";
@@ -40,9 +44,10 @@ import { GeneratorMetadataProvider } from "./generator-metadata-provider";
const SomeUser = "some user" as UserId;
const SomeAccount = {
id: SomeUser,
email: "someone@example.com",
emailVerified: true,
name: "Someone",
...mockAccountInfoWith({
email: "someone@example.com",
name: "Someone",
}),
};
const SomeAccount$ = new BehaviorSubject<Account>(SomeAccount);

View File

@@ -15,7 +15,12 @@ import { UserStateSubjectDependencyProvider } from "@bitwarden/common/tools/stat
import { StateConstraints } from "@bitwarden/common/tools/types";
import { OrganizationId, PolicyId, UserId } from "@bitwarden/common/types/guid";
import { FakeStateProvider, FakeAccountService, awaitAsync } from "../../../../../common/spec";
import {
FakeStateProvider,
FakeAccountService,
awaitAsync,
mockAccountInfoWith,
} from "../../../../../common/spec";
import { CoreProfileMetadata, ProfileContext } from "../metadata/profile-metadata";
import { GeneratorConstraints } from "../types";
@@ -31,21 +36,25 @@ const UnverifiedEmailUser = "UnverifiedEmailUser" as UserId;
const accounts: Record<UserId, Account> = {
[SomeUser]: {
id: SomeUser,
name: "some user",
email: "some.user@example.com",
emailVerified: true,
...mockAccountInfoWith({
name: "some user",
email: "some.user@example.com",
}),
},
[AnotherUser]: {
id: AnotherUser,
name: "some other user",
email: "some.other.user@example.com",
emailVerified: true,
...mockAccountInfoWith({
name: "some other user",
email: "some.other.user@example.com",
}),
},
[UnverifiedEmailUser]: {
id: UnverifiedEmailUser,
name: "a user with an unverfied email",
email: "unverified@example.com",
emailVerified: false,
...mockAccountInfoWith({
name: "a user with an unverfied email",
email: "unverified@example.com",
emailVerified: false,
}),
},
};
const accountService = new FakeAccountService(accounts);
@@ -57,6 +66,7 @@ const somePolicy = new Policy({
id: "" as PolicyId,
organizationId: "" as OrganizationId,
enabled: true,
revisionDate: new Date().toISOString(),
});
const stateProvider = new FakeStateProvider(accountService);

View File

@@ -8,7 +8,7 @@ import { Vendor } from "@bitwarden/common/tools/extension/vendor/data";
import { SemanticLogger, ifEnabledSemanticLoggerProvider } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid";
import { awaitAsync } from "../../../../../common/spec";
import { awaitAsync, mockAccountInfoWith } from "../../../../../common/spec";
import {
Algorithm,
CredentialAlgorithm,
@@ -56,9 +56,10 @@ describe("DefaultCredentialGeneratorService", () => {
// Use a hard-coded value for mockAccount
account = {
id: "test-account-id" as UserId,
emailVerified: true,
email: "test@example.com",
name: "Test User",
...mockAccountInfoWith({
email: "test@example.com",
name: "Test User",
}),
};
system = {

View File

@@ -13,9 +13,10 @@ import { observe$PerUserId, sharedStateByUserId } from "../util";
import { CATCHALL_SETTINGS } from "./storage";
/** Strategy for creating usernames using a catchall email address */
export class CatchallGeneratorStrategy
implements GeneratorStrategy<CatchallGenerationOptions, NoPolicy>
{
export class CatchallGeneratorStrategy implements GeneratorStrategy<
CatchallGenerationOptions,
NoPolicy
> {
/** Instantiates the generation strategy
* @param usernameService generates a catchall address for a domain
*/

View File

@@ -16,9 +16,10 @@ const UsernameDigits = Object.freeze({
});
/** Strategy for creating usernames from the EFF wordlist */
export class EffUsernameGeneratorStrategy
implements GeneratorStrategy<EffUsernameGenerationOptions, NoPolicy>
{
export class EffUsernameGeneratorStrategy implements GeneratorStrategy<
EffUsernameGenerationOptions,
NoPolicy
> {
/** Instantiates the generation strategy
* @param usernameService generates a username from EFF word list
*/

View File

@@ -10,8 +10,7 @@ import { Classifier } from "@bitwarden/common/tools/state/classifier";
export class OptionsClassifier<
Settings,
Options extends IntegrationRequest & Settings = IntegrationRequest & Settings,
> implements Classifier<Options, Record<string, never>, Settings>
{
> implements Classifier<Options, Record<string, never>, Settings> {
/** Partitions `secret` into its disclosed properties and secret properties.
* @param value The object to partition
* @returns an object that classifies secrets.

View File

@@ -14,9 +14,10 @@ import { observe$PerUserId, optionsToEffWordListRequest, sharedStateByUserId } f
import { PASSPHRASE_SETTINGS } from "./storage";
/** Generates passphrases composed of random words */
export class PassphraseGeneratorStrategy
implements GeneratorStrategy<PassphraseGenerationOptions, PassphraseGeneratorPolicy>
{
export class PassphraseGeneratorStrategy implements GeneratorStrategy<
PassphraseGenerationOptions,
PassphraseGeneratorPolicy
> {
/** instantiates the password generator strategy.
* @param legacy generates the passphrase
* @param stateProvider provides durable state

View File

@@ -12,9 +12,10 @@ import { observe$PerUserId, optionsToRandomAsciiRequest, sharedStateByUserId } f
import { PASSWORD_SETTINGS } from "./storage";
/** Generates passwords composed of random characters */
export class PasswordGeneratorStrategy
implements GeneratorStrategy<PasswordGenerationOptions, PasswordGeneratorPolicy>
{
export class PasswordGeneratorStrategy implements GeneratorStrategy<
PasswordGenerationOptions,
PasswordGeneratorPolicy
> {
/** instantiates the password generator strategy.
* @param legacy generates the password
*/

View File

@@ -17,9 +17,10 @@ import { SUBADDRESS_SETTINGS } from "./storage";
* For example, if the email address is `jd+xyz@domain.io`,
* the subaddress is `xyz`.
*/
export class SubaddressGeneratorStrategy
implements GeneratorStrategy<SubaddressGenerationOptions, NoPolicy>
{
export class SubaddressGeneratorStrategy implements GeneratorStrategy<
SubaddressGenerationOptions,
NoPolicy
> {
/** Instantiates the generation strategy
* @param usernameService generates an email subaddress from an email address
*/

View File

@@ -70,6 +70,7 @@ describe("DefaultGeneratorNavigationService", () => {
enabled: true,
type: PolicyType.PasswordGenerator,
data: { overridePasswordType: "password" },
revisionDate: new Date().toISOString(),
}),
]);
},

View File

@@ -8,9 +8,10 @@ import { GeneratorNavigationPolicy } from "./generator-navigation-policy";
/** Enforces policy for generator navigation options.
*/
export class GeneratorNavigationEvaluator
implements PolicyEvaluator<GeneratorNavigationPolicy, GeneratorNavigation>
{
export class GeneratorNavigationEvaluator implements PolicyEvaluator<
GeneratorNavigationPolicy,
GeneratorNavigation
> {
/** Instantiates the evaluator.
* @param policy The policy applied by the evaluator. When this conflicts with
* the defaults, the policy takes precedence.

View File

@@ -17,6 +17,7 @@ function createPolicy(
data,
enabled,
type,
revisionDate: new Date().toISOString(),
});
}