diff --git a/src/app/accounts/lock.component.html b/src/app/accounts/lock.component.html new file mode 100644 index 00000000..0fa0edba --- /dev/null +++ b/src/app/accounts/lock.component.html @@ -0,0 +1,23 @@ +
diff --git a/src/app/accounts/lock.component.ts b/src/app/accounts/lock.component.ts new file mode 100644 index 00000000..9cdcf011 --- /dev/null +++ b/src/app/accounts/lock.component.ts @@ -0,0 +1,56 @@ +import * as template from './lock.component.html'; + +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { CryptoService } from 'jslib/abstractions/crypto.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { MessagingService } from 'jslib/abstractions/messaging.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { UserService } from 'jslib/abstractions/user.service'; + +@Component({ + selector: 'app-lock', + template: template, +}) +export class LockComponent { + masterPassword: string = ''; + + constructor(private router: Router, private analytics: Angulartics2, + private toasterService: ToasterService, private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, private messagingService: MessagingService, + private userService: UserService, private cryptoService: CryptoService) { } + + async submit() { + if (this.masterPassword == null || this.masterPassword === '') { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPassRequired')); + return; + } + + const email = await this.userService.getEmail(); + const key = this.cryptoService.makeKey(this.masterPassword, email); + const keyHash = await this.cryptoService.hashPassword(this.masterPassword, key); + const storedKeyHash = await this.cryptoService.getKeyHash(); + + if (storedKeyHash != null && keyHash != null && storedKeyHash === keyHash) { + await this.cryptoService.setKey(key); + this.messagingService.send('unlocked'); + this.router.navigate(['vault']); + } else { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('invalidMasterPassword')); + } + } + + async logOut() { + const confirmed = await this.platformUtilsService.showDialog(this.i18nService.t('logOutConfirmation'), + this.i18nService.t('logOut'), this.i18nService.t('logOut'), this.i18nService.t('cancel')); + if (confirmed) { + this.messagingService.send('logout'); + } + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 196756bf..ed54d30d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -7,6 +7,7 @@ import { import { AuthGuardService } from './services/auth-guard.service'; import { HintComponent } from './accounts/hint.component'; +import { LockComponent } from './accounts/lock.component'; import { LoginComponent } from './accounts/login.component'; import { RegisterComponent } from './accounts/register.component'; import { TwoFactorComponent } from './accounts/two-factor.component'; @@ -14,6 +15,7 @@ import { VaultComponent } from './vault/vault.component'; const routes: Routes = [ { path: '', redirectTo: '/vault', pathMatch: 'full' }, + { path: 'lock', component: LockComponent }, { path: 'login', component: LoginComponent }, { path: '2fa', component: TwoFactorComponent }, { path: 'register', component: RegisterComponent }, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 41b76cbe..34fd0adf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -21,6 +21,7 @@ import { CipherService } from 'jslib/abstractions/cipher.service'; import { CryptoService } from 'jslib/abstractions/crypto.service'; import { FolderService } from 'jslib/abstractions/folder.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; +import { LockService } from 'jslib/abstractions/lock.service'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { SettingsService } from 'jslib/abstractions/settings.service'; @@ -50,7 +51,8 @@ export class AppComponent implements OnInit { private passwordGenerationService: PasswordGenerationService, private cipherService: CipherService, private authService: AuthService, private router: Router, private analytics: Angulartics2, private toasterService: ToasterService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, private ngZone: NgZone) { } + private platformUtilsService: PlatformUtilsService, private ngZone: NgZone, + private lockService: LockService) { } ngOnInit() { this.broadcasterService.subscribe((message: any) => { @@ -59,9 +61,13 @@ export class AppComponent implements OnInit { case 'loggedIn': break; case 'logout': - this.logOut(message.expired); + this.logOut(!!message.expired); + break; + case 'lockVault': + await this.lockService.lock(); break; case 'locked': + this.router.navigate(['lock']); break; case 'unlocked': break; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1e492927..b398b6c0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -17,6 +17,7 @@ import { ModalComponent } from './modal.component'; import { EnvironmentComponent } from './accounts/environment.component'; import { HintComponent } from './accounts/hint.component'; +import { LockComponent } from './accounts/lock.component'; import { LoginComponent } from './accounts/login.component'; import { RegisterComponent } from './accounts/register.component'; import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component'; @@ -72,6 +73,7 @@ import { ViewComponent } from './vault/view.component'; HintComponent, I18nPipe, IconComponent, + LockComponent, LoginComponent, ModalComponent, PasswordGeneratorComponent, diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts index 164978d5..4c361084 100644 --- a/src/app/services/services.module.ts +++ b/src/app/services/services.module.ts @@ -87,8 +87,7 @@ const folderService = new FolderService(cryptoService, userService, () => i18nService.t('noneFolder'), apiService, storageService, i18nService); const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService); const lockService = new LockService(cipherService, folderService, collectionService, - cryptoService, platformUtilsService, storageService, - () => { /* set icon */ }, () => { /* refresh badge and menu */ }); + cryptoService, platformUtilsService, storageService, messagingService); const syncService = new SyncService(userService, apiService, settingsService, folderService, cipherService, cryptoService, collectionService, storageService, messagingService, (expired: boolean) => { /* log out */ }); @@ -141,6 +140,7 @@ function initFactory(i18n: I18nService, platformUtils: DesktopPlatformUtilsServi { provide: MessagingServiceAbstraction, useValue: messagingService }, { provide: BroadcasterService, useValue: broadcasterService }, { provide: SettingsServiceAbstraction, useValue: settingsService }, + { provide: LockServiceAbstraction, useValue: lockService }, { provide: APP_INITIALIZER, useFactory: initFactory, diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 934417b0..5d32b103 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -625,8 +625,8 @@ "loading": { "message": "Loading..." }, - "lock": { - "message": "Lock" + "lockVault": { + "message": "Lock Vault" }, "passwordGenerator": { "message": "Password Generator" @@ -678,5 +678,14 @@ }, "syncingFailed": { "message": "Syncing failed" + }, + "yourVaultIsLocked": { + "message": "Your vault is locked. Verify your master password to continue." + }, + "unlock": { + "message": "Unlock" + }, + "invalidMasterPassword": { + "message": "Invalid master password" } } diff --git a/src/main/menu.main.ts b/src/main/menu.main.ts index 37b793b3..ff190d8f 100644 --- a/src/main/menu.main.ts +++ b/src/main/menu.main.ts @@ -83,9 +83,9 @@ export class MenuMain { } }, { - label: this.i18nService.t('lock'), + label: this.i18nService.t('lockVault'), click() { - self.send('lockApp'); + self.send('lockVault'); }, accelerator: 'CmdOrCtrl+L' }, diff --git a/src/scss/pages.scss b/src/scss/pages.scss index b6abec1f..0a935a95 100644 --- a/src/scss/pages.scss +++ b/src/scss/pages.scss @@ -1,6 +1,6 @@ @import "variables.scss"; -#login-page { +#login-page, #lock-page { display: flex; justify-content: center; align-items: center; @@ -37,15 +37,15 @@ display: block; border-radius: $border-radius; } - - p { - text-align: center - } } -#login-page, #register-page, #hint-page, #two-factor-page { +#login-page, #register-page, #hint-page, #two-factor-page, #lock-page { .content { - max-width: 300px; + width: 300px; + + p { + text-align: center + } p.lead, h1 { font-size: $font-size-large;