From cf28435ce3e1a84c094bc290c853a9c4d5fc9c4d Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Wed, 10 Nov 2021 03:59:51 +1000 Subject: [PATCH] [Key Connector] Add support for key connector and OTP (#2156) Co-authored-by: Hinton --- jslib | 2 +- src/_locales/en/messages.json | 24 +++++++++++++++ src/background/main.background.ts | 15 +++++++--- src/popup/accounts/lock.component.ts | 6 ++-- .../accounts/remove-password.component.html | 29 +++++++++++++++++++ .../accounts/remove-password.component.ts | 10 +++++++ src/popup/accounts/sso.component.ts | 10 +++++-- src/popup/app-routing.module.ts | 7 +++++ src/popup/app.component.ts | 10 +++++-- src/popup/app.module.ts | 16 ++++++---- src/popup/services/services.module.ts | 5 ++++ src/popup/settings/export.component.html | 23 ++++----------- src/popup/settings/export.component.ts | 6 ++-- src/popup/settings/settings.component.html | 2 +- src/popup/settings/settings.component.ts | 6 +++- src/popup/vault/add-edit.component.html | 5 ++-- src/popup/vault/add-edit.component.ts | 6 ++-- 17 files changed, 139 insertions(+), 43 deletions(-) create mode 100644 src/popup/accounts/remove-password.component.html create mode 100644 src/popup/accounts/remove-password.component.ts diff --git a/jslib b/jslib index 2db9e1ce0d..8f177e2d3a 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 2db9e1ce0d7a702f07f20ecb916dd8191ff617e1 +Subproject commit 8f177e2d3a879b854db5c6e6d7d386b24d637a66 diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index f2853be848..3c65df4a41 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -121,6 +121,9 @@ "continue": { "message": "Continue" }, + "requestVerificationCode": { + "message": "Request one-time password" + }, "verificationCode": { "message": "Verification Code" }, @@ -411,6 +414,9 @@ "verificationCodeRequired": { "message": "Verification code is required." }, + "invalidVerificationCode": { + "message": "Invalid verification code" + }, "valueCopied": { "message": "$VALUE$ copied", "description": "Value has been copied to the clipboard.", @@ -1821,5 +1827,23 @@ }, "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." + }, + "convertOrganizationEncryptionDesc": { + "message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "leaveOrganization": { + "message": "Leave Organization" + }, + "removeMasterPassword": { + "message": "Remove Master Password" + }, + "removedMasterPassword": { + "message": "Master password removed." } } diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 349a2152b1..11ebc51f85 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -17,6 +17,7 @@ import { EventService } from 'jslib-common/services/event.service'; import { ExportService } from 'jslib-common/services/export.service'; import { FileUploadService } from 'jslib-common/services/fileUpload.service'; import { FolderService } from 'jslib-common/services/folder.service'; +import { KeyConnectorService } from 'jslib-common/services/keyConnector.service'; import { NotificationsService } from 'jslib-common/services/notifications.service'; import { PasswordGenerationService } from 'jslib-common/services/passwordGeneration.service'; import { PolicyService } from 'jslib-common/services/policy.service'; @@ -45,6 +46,7 @@ import { ExportService as ExportServiceAbstraction } from 'jslib-common/abstract import { FileUploadService as FileUploadServiceAbstraction } from 'jslib-common/abstractions/fileUpload.service'; import { FolderService as FolderServiceAbstraction } from 'jslib-common/abstractions/folder.service'; import { I18nService as I18nServiceAbstraction } from 'jslib-common/abstractions/i18n.service'; +import { KeyConnectorService as KeyConnectorServiceAbstraction } from 'jslib-common/abstractions/keyConnector.service'; import { LogService as LogServiceAbstraction } from 'jslib-common/abstractions/log.service'; import { MessagingService as MessagingServiceAbstraction } from 'jslib-common/abstractions/messaging.service'; import { NotificationsService as NotificationsServiceAbstraction } from 'jslib-common/abstractions/notifications.service'; @@ -64,8 +66,6 @@ import { UserService as UserServiceAbstraction } from 'jslib-common/abstractions import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib-common/abstractions/vaultTimeout.service'; import { AutofillService as AutofillServiceAbstraction } from '../services/abstractions/autofill.service'; -import { Utils } from 'jslib-common/misc/utils'; - import { BrowserApi } from '../browser/browserApi'; import { SafariApp } from '../browser/safariApp'; @@ -125,6 +125,7 @@ export default class MainBackground { popupUtilsService: PopupUtilsService; sendService: SendServiceAbstraction; fileUploadService: FileUploadServiceAbstraction; + keyConnectorService: KeyConnectorServiceAbstraction; onUpdatedRan: boolean; onReplacedRan: boolean; @@ -195,9 +196,12 @@ export default class MainBackground { this.storageService, this.i18nService, this.cryptoFunctionService); this.stateService = new StateService(); this.policyService = new PolicyService(this.userService, this.storageService, this.apiService); + this.keyConnectorService = new KeyConnectorService(this.storageService, this.userService, this.cryptoService, + this.apiService, this.environmentService, this.tokenService, this.logService); this.vaultTimeoutService = new VaultTimeoutService(this.cipherService, this.folderService, this.collectionService, this.cryptoService, this.platformUtilsService, this.storageService, this.messagingService, this.searchService, this.userService, this.tokenService, this.policyService, + this.keyConnectorService, async () => { if (this.notificationsService != null) { this.notificationsService.updateConnection(false); @@ -212,7 +216,8 @@ export default class MainBackground { this.syncService = new SyncService(this.userService, this.apiService, this.settingsService, this.folderService, this.cipherService, this.cryptoService, this.collectionService, this.storageService, this.messagingService, this.policyService, this.sendService, - this.logService, async (expired: boolean) => await this.logout(expired)); + this.logService, this.tokenService, this.keyConnectorService, + async (expired: boolean) => await this.logout(expired)); this.eventService = new EventService(this.storageService, this.apiService, this.userService, this.cipherService, this.logService); this.passwordGenerationService = new PasswordGenerationService(this.cryptoService, this.storageService, @@ -271,7 +276,8 @@ export default class MainBackground { const message = Object.assign({}, { command: subscriber }, arg); that.runtimeBackground.processMessage(message, that, null); } - }(), this.vaultTimeoutService, this.logService, this.cryptoFunctionService); + }(), this.vaultTimeoutService, this.logService, this.cryptoFunctionService, this.environmentService, + this.keyConnectorService); } async bootstrap() { @@ -362,6 +368,7 @@ export default class MainBackground { this.policyService.clear(userId), this.passwordGenerationService.clear(), this.vaultTimeoutService.clear(), + this.keyConnectorService.clear(), ]); this.searchService.clearIndex(); diff --git a/src/popup/accounts/lock.component.ts b/src/popup/accounts/lock.component.ts index 09c9bd22fd..ec7d48ff9b 100644 --- a/src/popup/accounts/lock.component.ts +++ b/src/popup/accounts/lock.component.ts @@ -8,6 +8,7 @@ import { ApiService } from 'jslib-common/abstractions/api.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { EnvironmentService } from 'jslib-common/abstractions/environment.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; @@ -30,9 +31,10 @@ export class LockComponent extends BaseLockComponent { userService: UserService, cryptoService: CryptoService, storageService: StorageService, vaultTimeoutService: VaultTimeoutService, environmentService: EnvironmentService, stateService: StateService, - apiService: ApiService, logService: LogService) { + apiService: ApiService, logService: LogService, keyConnectorService: KeyConnectorService) { super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService, - storageService, vaultTimeoutService, environmentService, stateService, apiService, logService); + storageService, vaultTimeoutService, environmentService, stateService, apiService, logService, + keyConnectorService); this.successRoute = '/tabs/current'; this.isInitialLockScreen = (window as any).previousPopupUrl == null; } diff --git a/src/popup/accounts/remove-password.component.html b/src/popup/accounts/remove-password.component.html new file mode 100644 index 0000000000..dcbaac8ade --- /dev/null +++ b/src/popup/accounts/remove-password.component.html @@ -0,0 +1,29 @@ +
+
+
+ {{'removeMasterPassword' | i18n}} +
+
+
+ + +
+
+
+

{{'convertOrganizationEncryptionDesc' | i18n : organization.name}}

+
+
+ +
+
+ +
+
+
+
diff --git a/src/popup/accounts/remove-password.component.ts b/src/popup/accounts/remove-password.component.ts new file mode 100644 index 0000000000..c83269494a --- /dev/null +++ b/src/popup/accounts/remove-password.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +import { RemovePasswordComponent as BaseRemovePasswordComponent } from 'jslib-angular/components/remove-password.component'; + +@Component({ + selector: 'app-remove-password', + templateUrl: 'remove-password.component.html', +}) +export class RemovePasswordComponent extends BaseRemovePasswordComponent { +} diff --git a/src/popup/accounts/sso.component.ts b/src/popup/accounts/sso.component.ts index 1ee79a0aae..e5ad186d26 100644 --- a/src/popup/accounts/sso.component.ts +++ b/src/popup/accounts/sso.component.ts @@ -16,6 +16,7 @@ import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.se import { StateService } from 'jslib-common/abstractions/state.service'; import { StorageService } from 'jslib-common/abstractions/storage.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; +import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service'; import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component'; import { BrowserApi } from '../../browser/browserApi'; @@ -30,7 +31,8 @@ export class SsoComponent extends BaseSsoComponent { storageService: StorageService, stateService: StateService, platformUtilsService: PlatformUtilsService, apiService: ApiService, cryptoFunctionService: CryptoFunctionService, passwordGenerationService: PasswordGenerationService, - syncService: SyncService, environmentService: EnvironmentService, logService: LogService) { + syncService: SyncService, environmentService: EnvironmentService, logService: LogService, + private vaultTimeoutService: VaultTimeoutService) { super(authService, router, i18nService, route, storageService, stateService, platformUtilsService, apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService); @@ -41,7 +43,11 @@ export class SsoComponent extends BaseSsoComponent { super.onSuccessfulLogin = async () => { await syncService.fullSync(true); - BrowserApi.reloadOpenWindows(); + if (await this.vaultTimeoutService.isLocked()) { + // If the vault is unlocked then this will clear keys from memory, which we don't want to do + BrowserApi.reloadOpenWindows(); + } + const thisWindow = window.open('', '_self'); thisWindow.close(); }; diff --git a/src/popup/app-routing.module.ts b/src/popup/app-routing.module.ts index f6558b7484..4b058f2842 100644 --- a/src/popup/app-routing.module.ts +++ b/src/popup/app-routing.module.ts @@ -18,6 +18,7 @@ import { HomeComponent } from './accounts/home.component'; import { LockComponent } from './accounts/lock.component'; import { LoginComponent } from './accounts/login.component'; import { RegisterComponent } from './accounts/register.component'; +import { RemovePasswordComponent } from './accounts/remove-password.component'; import { SetPasswordComponent } from './accounts/set-password.component'; import { SsoComponent } from './accounts/sso.component'; import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component'; @@ -105,6 +106,12 @@ const routes: Routes = [ component: SetPasswordComponent, data: { state: 'set-password' }, }, + { + path: 'remove-password', + component: RemovePasswordComponent, + canActivate: [AuthGuardService], + data: { state: 'remove-password' }, + }, { path: 'register', component: RegisterComponent, diff --git a/src/popup/app.component.ts b/src/popup/app.component.ts index b9551efb38..837f17d6e4 100644 --- a/src/popup/app.component.ts +++ b/src/popup/app.component.ts @@ -26,6 +26,7 @@ import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StateService } from 'jslib-common/abstractions/state.service'; @@ -33,7 +34,6 @@ import { StorageService } from 'jslib-common/abstractions/storage.service'; import { ConstantsService } from 'jslib-common/services/constants.service'; -import BrowserPlatformUtilsService from 'src/services/browserPlatformUtils.service'; import { routerTransition } from './app-routing.animations'; @Component({ @@ -63,7 +63,8 @@ export class AppComponent implements OnInit { private i18nService: I18nService, private router: Router, private stateService: StateService, private messagingService: MessagingService, private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, - private sanitizer: DomSanitizer, private platformUtilsService: PlatformUtilsService) { } + private sanitizer: DomSanitizer, private platformUtilsService: PlatformUtilsService, + private keyConnectoService: KeyConnectorService) { } ngOnInit() { if (BrowserApi.getBackgroundPage() == null) { @@ -121,6 +122,11 @@ export class AppComponent implements OnInit { this.ngZone.run(() => { this.router.navigate(['/']); }); + } else if (msg.command === 'convertAccountToKeyConnector') { + this.ngZone.run(async () => { + await this.keyConnectoService.setConvertAccountRequired(true); + this.router.navigate(['/remove-password']); + }); } else { msg.webExtSender = sender; this.broadcasterService.send(msg); diff --git a/src/popup/app.module.ts b/src/popup/app.module.ts index 57aa6a5507..89719c79e1 100644 --- a/src/popup/app.module.ts +++ b/src/popup/app.module.ts @@ -17,6 +17,7 @@ import { HomeComponent } from './accounts/home.component'; import { LockComponent } from './accounts/lock.component'; import { LoginComponent } from './accounts/login.component'; import { RegisterComponent } from './accounts/register.component'; +import { RemovePasswordComponent } from './accounts/remove-password.component'; import { SetPasswordComponent } from './accounts/set-password.component'; import { SsoComponent } from './accounts/sso.component'; import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component'; @@ -83,6 +84,7 @@ import { SetPinComponent } from './components/set-pin.component'; import { CalloutComponent } from 'jslib-angular/components/callout.component'; import { IconComponent } from 'jslib-angular/components/icon.component'; +import { VerifyMasterPasswordComponent } from 'jslib-angular/components/verify-master-password.component'; import { CurrencyPipe, @@ -192,6 +194,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); A11yTitleDirective, ActionButtonsComponent, AddEditComponent, + AddEditCustomFieldsComponent, ApiActionDirective, AppComponent, AttachmentsComponent, @@ -212,8 +215,8 @@ registerLocaleData(localeZhTw, 'zh-TW'); FolderAddEditComponent, FoldersComponent, GroupingsComponent, - HomeComponent, HintComponent, + HomeComponent, I18nPipe, IconComponent, InputVerbatimDirective, @@ -223,6 +226,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); PasswordGeneratorComponent, PasswordGeneratorHistoryComponent, PasswordHistoryComponent, + PasswordRepromptComponent, PopOutComponent, PremiumComponent, PrivateModeComponent, @@ -235,6 +239,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); SendListComponent, SendTypeComponent, SetPasswordComponent, + SetPinComponent, SettingsComponent, ShareComponent, SsoComponent, @@ -243,15 +248,14 @@ registerLocaleData(localeZhTw, 'zh-TW'); SyncComponent, TabsComponent, TrueFalseValueDirective, - TwoFactorOptionsComponent, TwoFactorComponent, + TwoFactorOptionsComponent, UpdateTempPasswordComponent, - ViewComponent, - PasswordRepromptComponent, - SetPinComponent, VaultTimeoutInputComponent, - AddEditCustomFieldsComponent, + VerifyMasterPasswordComponent, + ViewComponent, ViewCustomFieldsComponent, + RemovePasswordComponent, ], entryComponents: [], providers: [ diff --git a/src/popup/services/services.module.ts b/src/popup/services/services.module.ts index 4ca02093d4..3a1c84be61 100644 --- a/src/popup/services/services.module.ts +++ b/src/popup/services/services.module.ts @@ -33,6 +33,7 @@ import { ExportService } from 'jslib-common/abstractions/export.service'; import { FileUploadService } from 'jslib-common/abstractions/fileUpload.service'; import { FolderService } from 'jslib-common/abstractions/folder.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service'; import { LogService as LogServiceAbstraction } from 'jslib-common/abstractions/log.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { NotificationsService } from 'jslib-common/abstractions/notifications.service'; @@ -49,6 +50,7 @@ import { SyncService } from 'jslib-common/abstractions/sync.service'; import { TokenService } from 'jslib-common/abstractions/token.service'; import { TotpService } from 'jslib-common/abstractions/totp.service'; import { UserService } from 'jslib-common/abstractions/user.service'; +import { UserVerificationService as UserVerificationServiceAbstraction } from 'jslib-common/abstractions/userVerification.service'; import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service'; import { AutofillService } from '../../services/abstractions/autofill.service'; @@ -59,6 +61,7 @@ import { ConsoleLogService } from 'jslib-common/services/consoleLog.service'; import { ConstantsService } from 'jslib-common/services/constants.service'; import { SearchService } from 'jslib-common/services/search.service'; import { StateService } from 'jslib-common/services/state.service'; +import { UserVerificationService } from 'jslib-common/services/userVerification.service'; import { PopupSearchService } from './popup-search.service'; import { PopupUtilsService } from './popup-utils.service'; @@ -182,6 +185,7 @@ export function initFactory(platformUtilsService: PlatformUtilsService, i18nServ { provide: AutofillService, useFactory: getBgService('autofillService'), deps: [] }, { provide: ExportService, useFactory: getBgService('exportService'), deps: [] }, { provide: SendService, useFactory: getBgService('sendService'), deps: [] }, + { provide: KeyConnectorService, useFactory: getBgService('keyConnectorService'), deps: [] }, { provide: VaultTimeoutService, useFactory: getBgService('vaultTimeoutService'), @@ -204,6 +208,7 @@ export function initFactory(platformUtilsService: PlatformUtilsService, i18nServ deps: [], }, { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, + { provide: UserVerificationServiceAbstraction, useClass: UserVerificationService }, ], }) export class ServicesModule { diff --git a/src/popup/settings/export.component.html b/src/popup/settings/export.component.html index 922f3ebc15..75b918cebd 100644 --- a/src/popup/settings/export.component.html +++ b/src/popup/settings/export.component.html @@ -1,4 +1,4 @@ -
+
- +
@@ -22,25 +22,14 @@
- +
- - -
-
- + +
diff --git a/src/popup/settings/export.component.ts b/src/popup/settings/export.component.ts index ad936f0f05..b60986a5b5 100644 --- a/src/popup/settings/export.component.ts +++ b/src/popup/settings/export.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; @@ -8,6 +9,7 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { PolicyService } from 'jslib-common/abstractions/policy.service'; +import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service'; import { ExportComponent as BaseExportComponent } from 'jslib-angular/components/export.component'; @@ -19,9 +21,9 @@ export class ExportComponent extends BaseExportComponent { constructor(cryptoService: CryptoService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, exportService: ExportService, eventService: EventService, policyService: PolicyService, private router: Router, - logService: LogService) { + logService: LogService, userVerificationService: UserVerificationService, fb: FormBuilder) { super(cryptoService, i18nService, platformUtilsService, exportService, eventService, policyService, window, - logService); + logService, userVerificationService, fb); } protected saved() { diff --git a/src/popup/settings/settings.component.html b/src/popup/settings/settings.component.html index 66df2da28f..20908836e8 100644 --- a/src/popup/settings/settings.component.html +++ b/src/popup/settings/settings.component.html @@ -71,7 +71,7 @@
diff --git a/src/popup/settings/settings.component.ts b/src/popup/settings/settings.component.ts index 89c3c415d5..94fb371272 100644 --- a/src/popup/settings/settings.component.ts +++ b/src/popup/settings/settings.component.ts @@ -18,6 +18,7 @@ import { ConstantsService } from 'jslib-common/services/constants.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { EnvironmentService } from 'jslib-common/abstractions/environment.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StorageService } from 'jslib-common/abstractions/storage.service'; @@ -58,6 +59,7 @@ export class SettingsComponent implements OnInit { biometric: boolean = false; disableAutoBiometricsPrompt = true; previousVaultTimeout: number = null; + showChangeMasterPass = true; vaultTimeout: FormControl = new FormControl(null); @@ -66,7 +68,8 @@ export class SettingsComponent implements OnInit { public messagingService: MessagingService, private router: Router, private environmentService: EnvironmentService, private cryptoService: CryptoService, private userService: UserService, private popupUtilsService: PopupUtilsService, - private modalService: ModalService, private toasterService: ToasterService) { + private modalService: ModalService, private toasterService: ToasterService, + private keyConnectorService: KeyConnectorService) { } async ngOnInit() { @@ -118,6 +121,7 @@ export class SettingsComponent implements OnInit { this.biometric = await this.vaultTimeoutService.isBiometricLockSet(); this.disableAutoBiometricsPrompt = await this.storageService.get( ConstantsService.disableAutoBiometricsPromptKey) ?? true; + this.showChangeMasterPass = !await this.keyConnectorService.getUsesKeyConnector(); } async saveVaultTimeout(newValue: number) { diff --git a/src/popup/vault/add-edit.component.html b/src/popup/vault/add-edit.component.html index 6427b0cd21..591886b993 100644 --- a/src/popup/vault/add-edit.component.html +++ b/src/popup/vault/add-edit.component.html @@ -290,7 +290,7 @@ -
+
- +