mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-17165] Remove v1 generator UI from web (#13240)
* Remove v1 generator from web Remove conditional routing based on `generator-tools-modernization` Remove generatorSwap helper Remove generator and password-generator-history components including the base ones in libs/angular * Remove the feature flag `generator-tools-modernization` * Remove unused keys from en/messages.json * Remove unused css --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
fc62d80b70
commit
0b5b1b347e
@@ -12,7 +12,6 @@ import {
|
||||
activeAuthGuard,
|
||||
} from "@bitwarden/angular/auth/guards";
|
||||
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
|
||||
import { generatorSwap } from "@bitwarden/angular/tools/generator/generator-swap";
|
||||
import { twofactorRefactorSwap } from "@bitwarden/angular/utils/two-factor-component-refactor-route-swap";
|
||||
import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards";
|
||||
import {
|
||||
@@ -90,7 +89,6 @@ import { SMLandingComponent } from "./secrets-manager/secrets-manager-landing/sm
|
||||
import { DomainRulesComponent } from "./settings/domain-rules.component";
|
||||
import { PreferencesComponent } from "./settings/preferences.component";
|
||||
import { CredentialGeneratorComponent } from "./tools/credential-generator/credential-generator.component";
|
||||
import { GeneratorComponent } from "./tools/generator.component";
|
||||
import { ReportsModule } from "./tools/reports";
|
||||
import { AccessComponent } from "./tools/send/access.component";
|
||||
import { SendAccessExplainerComponent } from "./tools/send/send-access-explainer.component";
|
||||
@@ -831,10 +829,11 @@ const routes: Routes = [
|
||||
titleId: "exportVault",
|
||||
} satisfies RouteDataProperties,
|
||||
},
|
||||
...generatorSwap(GeneratorComponent, CredentialGeneratorComponent, {
|
||||
{
|
||||
path: "generator",
|
||||
component: CredentialGeneratorComponent,
|
||||
data: { titleId: "generator" } satisfies RouteDataProperties,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -66,8 +66,6 @@ import { ProductSwitcherModule } from "../layouts/product-switcher/product-switc
|
||||
import { UserLayoutComponent } from "../layouts/user-layout.component";
|
||||
import { DomainRulesComponent } from "../settings/domain-rules.component";
|
||||
import { PreferencesComponent } from "../settings/preferences.component";
|
||||
import { GeneratorComponent } from "../tools/generator.component";
|
||||
import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-history.component";
|
||||
import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component";
|
||||
import { PremiumBadgeComponent } from "../vault/components/premium-badge.component";
|
||||
import { AddEditCustomFieldsComponent } from "../vault/individual-vault/add-edit-custom-fields.component";
|
||||
@@ -139,8 +137,6 @@ import { SharedModule } from "./shared.module";
|
||||
OrgUnsecuredWebsitesReportComponent,
|
||||
OrgUserConfirmComponent,
|
||||
OrgWeakPasswordsReportComponent,
|
||||
GeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PreferencesComponent,
|
||||
PremiumBadgeComponent,
|
||||
ProfileComponent,
|
||||
@@ -206,8 +202,6 @@ import { SharedModule } from "./shared.module";
|
||||
OrgUnsecuredWebsitesReportComponent,
|
||||
OrgUserConfirmComponent,
|
||||
OrgWeakPasswordsReportComponent,
|
||||
GeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PreferencesComponent,
|
||||
PremiumBadgeComponent,
|
||||
ProfileComponent,
|
||||
|
||||
@@ -1,479 +0,0 @@
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'">
|
||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="card card-generated bg-light my-4">
|
||||
<div class="card-body">
|
||||
<bit-color-password
|
||||
[password]="type === 'password' ? password : username"
|
||||
[appCopyText]="type === 'password' ? password : username"
|
||||
></bit-color-password>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" role="radiogroup" aria-labelledby="typeHeading">
|
||||
<label id="typeHeading" class="d-block">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||
<div class="form-check form-check-inline" *ngFor="let o of typeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="type"
|
||||
name="Type"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="type === 'password'">
|
||||
<div aria-labelledby="passwordTypeHeading" class="form-group" role="radiogroup">
|
||||
<label id="passwordTypeHeading" class="d-block">{{ "passwordType" | i18n }}</label>
|
||||
<div class="form-check form-check-inline" *ngFor="let o of passTypeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
name="PasswordType"
|
||||
id="passwordType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
[checked]="passwordOptions.type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="passwordType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="passwordOptions.type === 'passphrase'">
|
||||
<div class="row">
|
||||
<div class="form-group col-4">
|
||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||
<input
|
||||
id="num-words"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="3"
|
||||
max="20"
|
||||
[(ngModel)]="passwordOptions.numWords"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||
<input
|
||||
id="word-separator"
|
||||
class="form-control"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
[(ngModel)]="passwordOptions.wordSeparator"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="capitalize"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.capitalize"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||
/>
|
||||
<label for="capitalize" class="form-check-label">{{ "capitalize" | i18n }}</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="include-number"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.includeNumber"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||
/>
|
||||
<label for="include-number" class="form-check-label">{{ "includeNumber" | i18n }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="passwordOptions.type === 'password'">
|
||||
<div class="row">
|
||||
<div class="form-group col-4">
|
||||
<label for="length">{{ "length" | i18n }}</label>
|
||||
<input
|
||||
id="length"
|
||||
class="form-control"
|
||||
type="number"
|
||||
[min]="passwordOptions.minLength"
|
||||
max="128"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
(blur)="savePasswordOptions()"
|
||||
(change)="lengthChanged()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="min-length">{{ "passwordMinLength" | i18n }}</label>
|
||||
<input
|
||||
id="min-length"
|
||||
class="form-control"
|
||||
type="text"
|
||||
readonly="true"
|
||||
[value]="passwordOptions.length"
|
||||
/>
|
||||
<span
|
||||
class="tw-sr-only"
|
||||
attr.aria-label="{{ 'passwordMinLength' | i18n }}"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{ passwordOptionsMinLengthForReader$ | async }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||
<input
|
||||
id="min-number"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
[(ngModel)]="passwordOptions.minNumber"
|
||||
(input)="onPasswordOptionsMinNumberInput($event)"
|
||||
(change)="minNumberChanged()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||
<input
|
||||
id="min-special"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
[(ngModel)]="passwordOptions.minSpecial"
|
||||
(input)="onPasswordOptionsMinSpecialInput($event)"
|
||||
(change)="minSpecialChanged()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="uppercase"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||
/>
|
||||
<label for="uppercase" class="form-check-label">A-Z</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="lowercase"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||
/>
|
||||
<label for="lowercase" class="form-check-label">a-z</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="numbers"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[ngModel]="passwordOptions.number"
|
||||
(ngModelChange)="setPasswordOptionsNumber($event)"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||
/>
|
||||
<label for="numbers" class="form-check-label">0-9</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="special"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[ngModel]="passwordOptions.special"
|
||||
(ngModelChange)="setPasswordOptionsSpecial($event)"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||
/>
|
||||
<label for="special" class="form-check-label">!@#$%^&*</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="ambiguous"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="avoidAmbiguous"
|
||||
/>
|
||||
<label for="ambiguous" class="form-check-label">{{ "ambiguous" | i18n }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary" (click)="regenerate()">
|
||||
{{ "regeneratePassword" | i18n }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
|
||||
{{ "copyPassword" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="history()"
|
||||
appA11yTitle="{{ 'passwordHistory' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-clock bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="type === 'username'">
|
||||
<div aria-labelledby="usernameTypeHeading" class="form-group" role="radiogroup">
|
||||
<div class="d-block">
|
||||
<label id="usernameTypeHeading">{{ "usernameType" | i18n }}</label>
|
||||
<a
|
||||
class="ml-auto"
|
||||
href="https://bitwarden.com/help/generator/#username-types"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-check" *ngFor="let o of usernameTypeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.type"
|
||||
name="UsernameType"
|
||||
id="usernameType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="usernameType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
<div class="small text-muted">{{ o.desc }}</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'forwarded'">
|
||||
<div class="form-group" role="listbox">
|
||||
<label class="d-block">{{ "service" | i18n }}</label>
|
||||
<select
|
||||
id="ForwardTypeDropdown"
|
||||
name="ForwardType"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
(change)="saveUsernameOptions()"
|
||||
class="form-control w-auto"
|
||||
>
|
||||
<option *ngFor="let o of forwardOptions" [ngValue]="o.value" role="option">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'simplelogin'">
|
||||
<div class="form-group col-4">
|
||||
<label for="simplelogin-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginApiKey"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4" *ngIf="isSelfHosted">
|
||||
<label for="simplelogin-baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-baseUrl"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="SimpleLoginDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginBaseUrl"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'duckduckgo'">
|
||||
<div class="form-group col-4">
|
||||
<label for="duckduckgo-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="duckduckgo-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
name="DuckDuckGoApiKey"
|
||||
[(ngModel)]="usernameOptions.forwardedDuckDuckGoToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'anonaddy'">
|
||||
<div class="form-group col-4">
|
||||
<label for="anonaddy-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="anonaddy-domain">{{ "aliasDomain" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4" *ngIf="isSelfHosted">
|
||||
<label for="anonaddy-baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-baseUrl"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="AnonAddyDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyBaseUrl"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'firefoxrelay'">
|
||||
<div class="form-group col-4">
|
||||
<label for="firefox-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="firefox-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedFirefoxApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'fastmail'">
|
||||
<div class="form-group col-4">
|
||||
<label for="fastmail-apiToken">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="fastmail-apiToken"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedFastmailApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'forwardemail'">
|
||||
<div class="form-group col-4">
|
||||
<label for="forwardemail-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="forwardemail-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedForwardEmailApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="forwardemail-domain">{{ "aliasDomain" | i18n }}</label>
|
||||
<input
|
||||
id="forwardemail-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.forwardedForwardEmailDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="row" *ngIf="usernameOptions.type === 'subaddress'">
|
||||
<div class="form-group col-4">
|
||||
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="subaddress-email"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.subaddressEmail"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.type === 'catchall'">
|
||||
<div class="form-group col-4">
|
||||
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
|
||||
<input
|
||||
id="catchall-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.catchallDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'word'">
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="capitalizeUsername"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordCapitalize"
|
||||
/>
|
||||
<label for="capitalizeUsername" class="form-check-label">{{
|
||||
"capitalize" | i18n
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="includeNumberUsername"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordIncludeNumber"
|
||||
/>
|
||||
<label for="includeNumberUsername" class="form-check-label">{{
|
||||
"includeNumber" | i18n
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div #form [appApiAction]="usernameGeneratingPromise">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-submit btn-primary"
|
||||
(click)="$any(form).loading ? false : regenerate()"
|
||||
[attr.aria-disabled]="$any(form).loading ? 'true' : null"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "regenerateUsername" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
|
||||
{{ "copyUsername" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #historyTemplate></ng-template>
|
||||
</bit-container>
|
||||
@@ -1,73 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component";
|
||||
import { 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";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
import {
|
||||
PasswordGenerationServiceAbstraction,
|
||||
UsernameGenerationServiceAbstraction,
|
||||
} from "@bitwarden/generator-legacy";
|
||||
|
||||
import { PasswordGeneratorHistoryComponent } from "./password-generator-history.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-generator",
|
||||
templateUrl: "generator.component.html",
|
||||
})
|
||||
export class GeneratorComponent extends BaseGeneratorComponent {
|
||||
constructor(
|
||||
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
usernameGenerationService: UsernameGenerationServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
logService: LogService,
|
||||
route: ActivatedRoute,
|
||||
ngZone: NgZone,
|
||||
private dialogService: DialogService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
passwordGenerationService,
|
||||
usernameGenerationService,
|
||||
platformUtilsService,
|
||||
accountService,
|
||||
i18nService,
|
||||
logService,
|
||||
route,
|
||||
ngZone,
|
||||
window,
|
||||
toastService,
|
||||
);
|
||||
if (platformUtilsService.isSelfHost()) {
|
||||
// Allow only valid email forwarders for self host
|
||||
this.forwardOptions = this.forwardOptions.filter((forwarder) => forwarder.validForSelfHosted);
|
||||
}
|
||||
}
|
||||
|
||||
get isSelfHosted(): boolean {
|
||||
return this.platformUtilsService.isSelfHost();
|
||||
}
|
||||
|
||||
async history() {
|
||||
this.dialogService.open(PasswordGeneratorHistoryComponent);
|
||||
}
|
||||
|
||||
lengthChanged() {
|
||||
document.getElementById("length").focus();
|
||||
}
|
||||
|
||||
minNumberChanged() {
|
||||
document.getElementById("min-number").focus();
|
||||
}
|
||||
|
||||
minSpecialChanged() {
|
||||
document.getElementById("min-special").focus();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle>
|
||||
{{ "passwordHistory" | i18n }}
|
||||
</span>
|
||||
<span bitDialogContent>
|
||||
<bit-table *ngIf="history.length">
|
||||
<ng-template body>
|
||||
<tr bitRow *ngFor="let h of history">
|
||||
<td bitCell>
|
||||
<bit-color-password
|
||||
[password]="h.password"
|
||||
class="tw-block tw-font-mono"
|
||||
[appCopyText]="h.password"
|
||||
></bit-color-password>
|
||||
<small bitTypography="body2" class="tw-text-muted">
|
||||
{{ h.date | date: "medium" }}
|
||||
</small>
|
||||
</td>
|
||||
<td bitCell class="tw-w-0">
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-clone"
|
||||
(click)="copy(h.password)"
|
||||
[appA11yTitle]="'copyPassword' | i18n"
|
||||
></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<div *ngIf="!history.length">
|
||||
{{ "noPasswordsInList" | i18n }}
|
||||
</div>
|
||||
</span>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="button" bitButton buttonType="secondary" bitDialogClose>
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tw-ml-auto"
|
||||
bitIconButton="bwi-trash"
|
||||
buttonType="danger"
|
||||
title="{{ 'clear' | i18n }}"
|
||||
[bitAction]="clear"
|
||||
></button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "@bitwarden/angular/tools/generator/components/password-generator-history.component";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
|
||||
@Component({
|
||||
selector: "app-password-generator-history",
|
||||
templateUrl: "password-generator-history.component.html",
|
||||
})
|
||||
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
|
||||
constructor(
|
||||
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window, toastService);
|
||||
}
|
||||
}
|
||||
@@ -1718,9 +1718,6 @@
|
||||
"message": "Avoid ambiguous characters",
|
||||
"description": "Label for the avoid ambiguous characters checkbox."
|
||||
},
|
||||
"regeneratePassword": {
|
||||
"message": "Regenerate password"
|
||||
},
|
||||
"length": {
|
||||
"message": "Length"
|
||||
},
|
||||
@@ -4773,9 +4770,6 @@
|
||||
"passwordGeneratorPolicyDesc": {
|
||||
"message": "Set requirements for password generator."
|
||||
},
|
||||
"passwordGeneratorPolicyInEffect": {
|
||||
"message": "One or more organization policies are affecting your generator settings."
|
||||
},
|
||||
"masterPasswordPolicyInEffect": {
|
||||
"message": "One or more organization policies require your master password to meet the following requirements:"
|
||||
},
|
||||
@@ -6744,15 +6738,6 @@
|
||||
"message": "Generator",
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password type"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate username"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate username"
|
||||
},
|
||||
@@ -6793,9 +6778,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username type"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus addressed email",
|
||||
"description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com"
|
||||
@@ -7015,9 +6997,6 @@
|
||||
"message": "Hostname",
|
||||
"description": "Part of a URL."
|
||||
},
|
||||
"apiAccessToken": {
|
||||
"message": "API access token"
|
||||
},
|
||||
"deviceVerification": {
|
||||
"message": "Device verification"
|
||||
},
|
||||
@@ -8728,9 +8707,6 @@
|
||||
"message": "Self-host server URL",
|
||||
"description": "Label for field requesting a self-hosted integration service URL"
|
||||
},
|
||||
"aliasDomain": {
|
||||
"message": "Alias domain"
|
||||
},
|
||||
"alreadyHaveAccount": {
|
||||
"message": "Already have an account?"
|
||||
},
|
||||
|
||||
@@ -1,37 +1,3 @@
|
||||
app-generator {
|
||||
#lengthRange {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-generated {
|
||||
.card-body {
|
||||
@include themify($themes) {
|
||||
background: themed("foregroundColor");
|
||||
}
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-family: $font-family-monospace;
|
||||
font-size: $font-size-lg;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-password-generator-history {
|
||||
.list-group-item {
|
||||
line-height: 1;
|
||||
@include themify($themes) {
|
||||
background: themed("backgroundColor");
|
||||
}
|
||||
|
||||
.password {
|
||||
font-family: $font-family-monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tools-import {
|
||||
textarea {
|
||||
height: 150px;
|
||||
|
||||
@@ -1,389 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { BehaviorSubject, combineLatest, firstValueFrom, Subject } from "rxjs";
|
||||
import { debounceTime, first, map, skipWhile, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { PasswordGeneratorPolicyOptions } from "@bitwarden/common/admin-console/models/domain/password-generator-policy-options";
|
||||
import { 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";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import {
|
||||
GeneratorType,
|
||||
DefaultPasswordBoundaries as DefaultBoundaries,
|
||||
} from "@bitwarden/generator-core";
|
||||
import {
|
||||
PasswordGenerationServiceAbstraction,
|
||||
UsernameGenerationServiceAbstraction,
|
||||
UsernameGeneratorOptions,
|
||||
PasswordGeneratorOptions,
|
||||
} from "@bitwarden/generator-legacy";
|
||||
|
||||
export class EmailForwarderOptions {
|
||||
name: string;
|
||||
value: string;
|
||||
validForSelfHosted: boolean;
|
||||
}
|
||||
|
||||
@Directive()
|
||||
export class GeneratorComponent implements OnInit, OnDestroy {
|
||||
@Input() comingFromAddEdit = false;
|
||||
@Input() type: GeneratorType | "";
|
||||
@Output() onSelected = new EventEmitter<string>();
|
||||
|
||||
usernameGeneratingPromise: Promise<string>;
|
||||
typeOptions: any[];
|
||||
usernameTypeOptions: any[];
|
||||
subaddressOptions: any[];
|
||||
catchallOptions: any[];
|
||||
forwardOptions: EmailForwarderOptions[];
|
||||
usernameOptions: UsernameGeneratorOptions = { website: null };
|
||||
passwordOptions: PasswordGeneratorOptions = {};
|
||||
username = "-";
|
||||
password = "-";
|
||||
showOptions = false;
|
||||
avoidAmbiguous = false;
|
||||
enforcedPasswordPolicyOptions: PasswordGeneratorPolicyOptions;
|
||||
usernameWebsite: string = null;
|
||||
|
||||
get passTypeOptions() {
|
||||
return this._passTypeOptions.filter((o) => !o.disabled);
|
||||
}
|
||||
private _passTypeOptions: { name: string; value: GeneratorType; disabled: boolean }[];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
private isInitialized$ = new BehaviorSubject(false);
|
||||
|
||||
// update screen reader minimum password length with 500ms debounce
|
||||
// so that the user isn't flooded with status updates
|
||||
private _passwordOptionsMinLengthForReader = new BehaviorSubject<number>(
|
||||
DefaultBoundaries.length.min,
|
||||
);
|
||||
protected passwordOptionsMinLengthForReader$ = this._passwordOptionsMinLengthForReader.pipe(
|
||||
map((val) => val || DefaultBoundaries.length.min),
|
||||
debounceTime(500),
|
||||
);
|
||||
|
||||
private _password = new BehaviorSubject<string>("-");
|
||||
|
||||
constructor(
|
||||
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
protected usernameGenerationService: UsernameGenerationServiceAbstraction,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected accountService: AccountService,
|
||||
protected i18nService: I18nService,
|
||||
protected logService: LogService,
|
||||
protected route: ActivatedRoute,
|
||||
protected ngZone: NgZone,
|
||||
private win: Window,
|
||||
protected toastService: ToastService,
|
||||
) {
|
||||
this.typeOptions = [
|
||||
{ name: i18nService.t("password"), value: "password" },
|
||||
{ name: i18nService.t("username"), value: "username" },
|
||||
];
|
||||
this._passTypeOptions = [
|
||||
{ name: i18nService.t("password"), value: "password", disabled: false },
|
||||
{ name: i18nService.t("passphrase"), value: "passphrase", disabled: false },
|
||||
];
|
||||
this.usernameTypeOptions = [
|
||||
{
|
||||
name: i18nService.t("plusAddressedEmail"),
|
||||
value: "subaddress",
|
||||
desc: i18nService.t("plusAddressedEmailDesc"),
|
||||
},
|
||||
{
|
||||
name: i18nService.t("catchallEmail"),
|
||||
value: "catchall",
|
||||
desc: i18nService.t("catchallEmailDesc"),
|
||||
},
|
||||
{
|
||||
name: i18nService.t("forwardedEmail"),
|
||||
value: "forwarded",
|
||||
desc: i18nService.t("forwardedEmailDesc"),
|
||||
},
|
||||
{ name: i18nService.t("randomWord"), value: "word" },
|
||||
];
|
||||
this.subaddressOptions = [{ name: i18nService.t("random"), value: "random" }];
|
||||
this.catchallOptions = [{ name: i18nService.t("random"), value: "random" }];
|
||||
|
||||
this.forwardOptions = [
|
||||
{ name: "", value: "", validForSelfHosted: false },
|
||||
{ name: "addy.io", value: "anonaddy", validForSelfHosted: true },
|
||||
{ name: "DuckDuckGo", value: "duckduckgo", validForSelfHosted: false },
|
||||
{ name: "Fastmail", value: "fastmail", validForSelfHosted: true },
|
||||
{ name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false },
|
||||
{ name: "SimpleLogin", value: "simplelogin", validForSelfHosted: true },
|
||||
{ name: "Forward Email", value: "forwardemail", validForSelfHosted: true },
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
this._password.pipe(debounceTime(250)).subscribe((password) => {
|
||||
ngZone.run(() => {
|
||||
this.password = password;
|
||||
});
|
||||
this.passwordGenerationService.addHistory(this.password).catch((e) => {
|
||||
this.logService.error(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cascadeOptions(navigationType: GeneratorType = undefined, accountEmail: string) {
|
||||
this.avoidAmbiguous = !this.passwordOptions.ambiguous;
|
||||
|
||||
if (!this.type) {
|
||||
if (navigationType) {
|
||||
this.type = navigationType;
|
||||
} else {
|
||||
this.type = this.passwordOptions.type === "username" ? "username" : "password";
|
||||
}
|
||||
}
|
||||
|
||||
this.passwordOptions.type =
|
||||
this.passwordOptions.type === "passphrase" ? "passphrase" : "password";
|
||||
|
||||
const overrideType = this.enforcedPasswordPolicyOptions.overridePasswordType ?? "";
|
||||
const isDisabled = overrideType.length
|
||||
? (value: string, policyValue: string) => value !== policyValue
|
||||
: (_value: string, _policyValue: string) => false;
|
||||
for (const option of this._passTypeOptions) {
|
||||
option.disabled = isDisabled(option.value, overrideType);
|
||||
}
|
||||
|
||||
if (this.usernameOptions.type == null) {
|
||||
this.usernameOptions.type = "word";
|
||||
}
|
||||
if (
|
||||
this.usernameOptions.subaddressEmail == null ||
|
||||
this.usernameOptions.subaddressEmail === ""
|
||||
) {
|
||||
this.usernameOptions.subaddressEmail = accountEmail;
|
||||
}
|
||||
if (this.usernameWebsite == null) {
|
||||
this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random";
|
||||
} else {
|
||||
this.usernameOptions.website = this.usernameWebsite;
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
combineLatest([
|
||||
this.route.queryParams.pipe(first()),
|
||||
this.accountService.activeAccount$.pipe(first()),
|
||||
this.passwordGenerationService.getOptions$(),
|
||||
this.usernameGenerationService.getOptions$(),
|
||||
])
|
||||
.pipe(
|
||||
map(([qParams, account, [passwordOptions, passwordPolicy], usernameOptions]) => ({
|
||||
navigationType: qParams.type as GeneratorType,
|
||||
accountEmail: account.email,
|
||||
passwordOptions,
|
||||
passwordPolicy,
|
||||
usernameOptions,
|
||||
})),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((options) => {
|
||||
this.passwordOptions = options.passwordOptions;
|
||||
this.enforcedPasswordPolicyOptions = options.passwordPolicy;
|
||||
this.usernameOptions = options.usernameOptions;
|
||||
|
||||
this.cascadeOptions(options.navigationType, options.accountEmail);
|
||||
this._passwordOptionsMinLengthForReader.next(this.passwordOptions.minLength);
|
||||
|
||||
if (this.regenerateWithoutButtonPress()) {
|
||||
this.regenerate().catch((e) => {
|
||||
this.logService.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
this.isInitialized$.next(true);
|
||||
});
|
||||
|
||||
// once initialization is complete, `ngOnInit` should return.
|
||||
//
|
||||
// FIXME(#6944): if a sync is in progress, wait to complete until after
|
||||
// the sync completes.
|
||||
await firstValueFrom(
|
||||
this.isInitialized$.pipe(
|
||||
skipWhile((initialized) => !initialized),
|
||||
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() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
this.isInitialized$.complete();
|
||||
this._passwordOptionsMinLengthForReader.complete();
|
||||
}
|
||||
|
||||
async typeChanged() {
|
||||
await this.savePasswordOptions();
|
||||
}
|
||||
|
||||
async regenerate() {
|
||||
if (this.type === "password") {
|
||||
await this.regeneratePassword();
|
||||
} else if (this.type === "username") {
|
||||
await this.regenerateUsername();
|
||||
}
|
||||
}
|
||||
|
||||
async sliderChanged() {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.savePasswordOptions();
|
||||
await this.passwordGenerationService.addHistory(this.password);
|
||||
}
|
||||
|
||||
async onPasswordOptionsMinNumberInput($event: Event) {
|
||||
// `savePasswordOptions()` replaces the null
|
||||
this.passwordOptions.number = null;
|
||||
|
||||
await this.savePasswordOptions();
|
||||
|
||||
// fixes UI desync that occurs when minNumber has a fixed value
|
||||
// that is reset through normalization
|
||||
($event.target as HTMLInputElement).value = `${this.passwordOptions.minNumber}`;
|
||||
}
|
||||
|
||||
async setPasswordOptionsNumber($event: boolean) {
|
||||
this.passwordOptions.number = $event;
|
||||
// `savePasswordOptions()` replaces the null
|
||||
this.passwordOptions.minNumber = null;
|
||||
|
||||
await this.savePasswordOptions();
|
||||
}
|
||||
|
||||
async onPasswordOptionsMinSpecialInput($event: Event) {
|
||||
// `savePasswordOptions()` replaces the null
|
||||
this.passwordOptions.special = null;
|
||||
|
||||
await this.savePasswordOptions();
|
||||
|
||||
// fixes UI desync that occurs when minSpecial has a fixed value
|
||||
// that is reset through normalization
|
||||
($event.target as HTMLInputElement).value = `${this.passwordOptions.minSpecial}`;
|
||||
}
|
||||
|
||||
async setPasswordOptionsSpecial($event: boolean) {
|
||||
this.passwordOptions.special = $event;
|
||||
// `savePasswordOptions()` replaces the null
|
||||
this.passwordOptions.minSpecial = null;
|
||||
|
||||
await this.savePasswordOptions();
|
||||
}
|
||||
|
||||
async sliderInput() {
|
||||
await this.normalizePasswordOptions();
|
||||
}
|
||||
|
||||
async savePasswordOptions() {
|
||||
// map navigation state into generator type
|
||||
const restoreType = this.passwordOptions.type;
|
||||
if (this.type === "username") {
|
||||
this.passwordOptions.type = this.type;
|
||||
}
|
||||
|
||||
// save options
|
||||
await this.normalizePasswordOptions();
|
||||
await this.passwordGenerationService.saveOptions(this.passwordOptions);
|
||||
|
||||
// restore the original format
|
||||
this.passwordOptions.type = restoreType;
|
||||
}
|
||||
|
||||
async saveUsernameOptions() {
|
||||
await this.usernameGenerationService.saveOptions(this.usernameOptions);
|
||||
if (this.usernameOptions.type === "forwarded") {
|
||||
this.username = "-";
|
||||
}
|
||||
}
|
||||
|
||||
async regeneratePassword() {
|
||||
this._password.next(
|
||||
await this.passwordGenerationService.generatePassword(this.passwordOptions),
|
||||
);
|
||||
}
|
||||
|
||||
regenerateUsername() {
|
||||
return this.generateUsername();
|
||||
}
|
||||
|
||||
async generateUsername() {
|
||||
try {
|
||||
this.usernameGeneratingPromise = this.usernameGenerationService.generateUsername(
|
||||
this.usernameOptions,
|
||||
);
|
||||
this.username = await this.usernameGeneratingPromise;
|
||||
if (this.username === "" || this.username === null) {
|
||||
this.username = "-";
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
copy() {
|
||||
const password = this.type === "password";
|
||||
const copyOptions = this.win != null ? { window: this.win } : null;
|
||||
this.platformUtilsService.copyToClipboard(
|
||||
password ? this.password : this.username,
|
||||
copyOptions,
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "info",
|
||||
title: null,
|
||||
message: this.i18nService.t(
|
||||
"valueCopied",
|
||||
this.i18nService.t(password ? "password" : "username"),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
select() {
|
||||
this.onSelected.emit(this.type === "password" ? this.password : this.username);
|
||||
}
|
||||
|
||||
toggleOptions() {
|
||||
this.showOptions = !this.showOptions;
|
||||
}
|
||||
|
||||
regenerateWithoutButtonPress() {
|
||||
return this.type !== "username" || this.usernameOptions.type !== "forwarded";
|
||||
}
|
||||
|
||||
private async normalizePasswordOptions() {
|
||||
// Application level normalize options dependent on class variables
|
||||
this.passwordOptions.ambiguous = !this.avoidAmbiguous;
|
||||
|
||||
if (
|
||||
!this.passwordOptions.uppercase &&
|
||||
!this.passwordOptions.lowercase &&
|
||||
!this.passwordOptions.number &&
|
||||
!this.passwordOptions.special
|
||||
) {
|
||||
this.passwordOptions.lowercase = true;
|
||||
if (this.win != null) {
|
||||
const lowercase = this.win.document.querySelector("#lowercase") as HTMLInputElement;
|
||||
if (lowercase) {
|
||||
lowercase.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.passwordGenerationService.enforcePasswordGeneratorPoliciesOnOptions(
|
||||
this.passwordOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, OnInit } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { GeneratedPasswordHistory } from "@bitwarden/generator-history";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
|
||||
@Directive()
|
||||
export class PasswordGeneratorHistoryComponent implements OnInit {
|
||||
history: GeneratedPasswordHistory[] = [];
|
||||
|
||||
constructor(
|
||||
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected i18nService: I18nService,
|
||||
private win: Window,
|
||||
protected toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.history = await this.passwordGenerationService.getHistory();
|
||||
}
|
||||
|
||||
clear = async () => {
|
||||
this.history = await this.passwordGenerationService.clear();
|
||||
};
|
||||
|
||||
copy(password: string) {
|
||||
const copyOptions = this.win != null ? { window: this.win } : null;
|
||||
this.platformUtilsService.copyToClipboard(password, copyOptions);
|
||||
this.toastService.showToast({
|
||||
variant: "info",
|
||||
title: null,
|
||||
message: this.i18nService.t("valueCopied", this.i18nService.t("password")),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Type, inject } from "@angular/core";
|
||||
import { Route, Routes } from "@angular/router";
|
||||
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
import { componentRouteSwap } from "../../utils/component-route-swap";
|
||||
|
||||
/**
|
||||
* Helper function to swap between two components based on the GeneratorToolsModernization feature flag.
|
||||
* @param defaultComponent - The current non-refreshed component to render.
|
||||
* @param refreshedComponent - The new refreshed component to render.
|
||||
* @param options - The shared route options to apply to the default component, and to the alt component if altOptions is not provided.
|
||||
* @param altOptions - The alt route options to apply to the alt component.
|
||||
*/
|
||||
export function generatorSwap(
|
||||
defaultComponent: Type<any>,
|
||||
refreshedComponent: Type<any>,
|
||||
options: Route,
|
||||
altOptions?: Route,
|
||||
): Routes {
|
||||
return componentRouteSwap(
|
||||
defaultComponent,
|
||||
refreshedComponent,
|
||||
async () => {
|
||||
const configService = inject(ConfigService);
|
||||
return configService.getFeatureFlag(FeatureFlag.GeneratorToolsModernization);
|
||||
},
|
||||
options,
|
||||
altOptions,
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,6 @@ export enum FeatureFlag {
|
||||
|
||||
/* Tools */
|
||||
ItemShare = "item-share",
|
||||
GeneratorToolsModernization = "generator-tools-modernization",
|
||||
CriticalApps = "pm-14466-risk-insights-critical-application",
|
||||
EnableRiskInsightsNotifications = "enable-risk-insights-notifications",
|
||||
|
||||
@@ -88,7 +87,6 @@ export const DefaultFeatureFlagValue = {
|
||||
|
||||
/* Tools */
|
||||
[FeatureFlag.ItemShare]: FALSE,
|
||||
[FeatureFlag.GeneratorToolsModernization]: FALSE,
|
||||
[FeatureFlag.CriticalApps]: FALSE,
|
||||
[FeatureFlag.EnableRiskInsightsNotifications]: FALSE,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user