diff --git a/jslib b/jslib index 32a636e5..3cc75979 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 32a636e5a5068f705b167f8f16dd15b53493b821 +Subproject commit 3cc759791e12b7692fc2d2b4be1a2b010eee1c8e diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 869c5896..e4bf23c4 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -18,6 +18,7 @@ import { AccountComponent } from './settings/account.component'; import { DomainRulesComponent } from './settings/domain-rules.component'; import { OptionsComponent } from './settings/options.component'; import { SettingsComponent } from './settings/settings.component'; +import { TwoFactorSetupComponent } from './settings/two-factor-setup.component'; import { ExportComponent } from './tools/export.component'; import { ImportComponent } from './tools/import.component'; @@ -55,6 +56,7 @@ const routes: Routes = [ { path: 'account', component: AccountComponent, canActivate: [AuthGuardService] }, { path: 'options', component: OptionsComponent, canActivate: [AuthGuardService] }, { path: 'domain-rules', component: DomainRulesComponent, canActivate: [AuthGuardService] }, + { path: 'two-factor', component: TwoFactorSetupComponent, canActivate: [AuthGuardService] }, ], }, { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 123ae8df..9a996ab8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -42,6 +42,7 @@ import { OptionsComponent } from './settings/options.component'; import { ProfileComponent } from './settings/profile.component'; import { PurgeVaultComponent } from './settings/purge-vault.component'; import { SettingsComponent } from './settings/settings.component'; +import { TwoFactorSetupComponent } from './settings/two-factor-setup.component'; import { ExportComponent } from './tools/export.component'; import { ImportComponent } from './tools/import.component'; @@ -144,6 +145,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; TrueFalseValueDirective, TwoFactorComponent, TwoFactorOptionsComponent, + TwoFactorSetupComponent, UserLayoutComponent, VaultComponent, ], diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index 42d383e3..5388bffb 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -16,8 +16,8 @@ Billing & Licensing - - Two-step Login + + {{'twoStepLogin' | i18n}} Domain Rules diff --git a/src/app/settings/two-factor-setup.component.html b/src/app/settings/two-factor-setup.component.html new file mode 100644 index 00000000..c7faabe7 --- /dev/null +++ b/src/app/settings/two-factor-setup.component.html @@ -0,0 +1,35 @@ + + +

{{'twoStepLoginRecoveryWarning' | i18n}}

+ +
+

+ {{'providers' | i18n}} + + + +

+
diff --git a/src/app/settings/two-factor-setup.component.ts b/src/app/settings/two-factor-setup.component.ts new file mode 100644 index 00000000..0274577f --- /dev/null +++ b/src/app/settings/two-factor-setup.component.ts @@ -0,0 +1,69 @@ +import { + Component, + OnInit, +} from '@angular/core'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { TokenService } from 'jslib/abstractions/token.service'; + +import { TwoFactorProviders } from 'jslib/services/auth.service'; + +import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType'; + +@Component({ + selector: 'app-two-factor-setup', + templateUrl: 'two-factor-setup.component.html', +}) +export class TwoFactorSetupComponent implements OnInit { + providers: any[] = []; + premium: boolean; + loading = true; + + constructor(private apiService: ApiService, private i18nService: I18nService, + private analytics: Angulartics2, private toasterService: ToasterService, + private tokenService: TokenService) { } + + async ngOnInit() { + this.premium = this.tokenService.getPremium(); + + for (const key in TwoFactorProviders) { + if (!TwoFactorProviders.hasOwnProperty(key)) { + continue; + } + + const p = (TwoFactorProviders as any)[key]; + if (p.type === TwoFactorProviderType.OrganizationDuo) { + continue; + } + + this.providers.push({ + type: p.type, + name: p.name, + description: p.description, + enabled: false, + premium: p.premium, + sort: p.sort, + }); + } + + this.providers.sort((a: any, b: any) => a.sort - b.sort); + await this.load(); + } + + async load() { + this.loading = true; + const providerList = await this.apiService.getTwoFactorProviders(); + providerList.data.forEach((p) => { + this.providers.forEach((p2) => { + if (p.type === p2.type) { + p2.enabled = p.enabled; + } + }); + }); + this.loading = false; + } +} diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 0d1cfd96..0ed3a826 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -943,7 +943,7 @@ "message": "New Custom Domain" }, "newCustomDomainDesc": { - "message": "Only \"base\" domains are allowed. Do not enter subdomains. For example, enter \"google.com\" instead of \"www.google.com\". You can also enter \"androidapp://package.name\" to associate an android app with other website domains." + "message": "Enter a list of domains separated by commas. Only \"base\" domains are allowed. Do not enter subdomains. For example, enter \"google.com\" instead of \"www.google.com\". You can also enter \"androidapp://package.name\" to associate an android app with other website domains." }, "customDomainX": { "message": "Custom Domain $INDEX$", @@ -956,5 +956,27 @@ }, "domainsUpdated": { "message": "Domains updated" + }, + "twoStepLogin": { + "message": "Two-step Login" + }, + "twoStepLoginRecoveryWarning": { + "message": "Enabling two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (ex. you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." + }, + "viewRecoveryCode": { + "message": "View Recovery Code" + }, + "providers": { + "message": "Providers", + "description": "Two-step login providers such as YubiKey, Duo, Authenticator apps, Email, etc." + }, + "enabled": { + "message": "Enabled" + }, + "premium": { + "message": "Premium" + }, + "manage": { + "message": "Manage" } } diff --git a/src/scss/styles.scss b/src/scss/styles.scss index ff696d82..2724e9cd 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -502,3 +502,9 @@ app-avatar { } } } + +.list-group-2fa { + .logo-2fa { + min-width: 100px; + } +} diff --git a/tslint.json b/tslint.json index b6d55716..7e4320f7 100644 --- a/tslint.json +++ b/tslint.json @@ -48,6 +48,7 @@ "check-preblock", "check-separator", "check-type" - ] + ], + "max-classes-per-file": false } }