2018-04-10 22:20:49 +02:00
import { Angulartics2 } from 'angulartics2' ;
2020-03-04 17:42:21 +01:00
import Swal from 'sweetalert2/src/sweetalert2.js' ;
2018-04-10 22:20:49 +02:00
import {
Component ,
2018-10-04 04:46:11 +02:00
ElementRef ,
2018-04-10 22:20:49 +02:00
OnInit ,
2018-10-04 04:46:11 +02:00
ViewChild ,
2018-04-10 22:20:49 +02:00
} from '@angular/core' ;
import { Router } from '@angular/router' ;
import { BrowserApi } from '../../browser/browserApi' ;
import { DeviceType } from 'jslib/enums/deviceType' ;
import { ConstantsService } from 'jslib/services/constants.service' ;
2018-11-16 17:08:36 +01:00
import { CryptoService } from 'jslib/abstractions/crypto.service' ;
2018-06-25 14:06:38 +02:00
import { EnvironmentService } from 'jslib/abstractions/environment.service' ;
2018-04-10 22:20:49 +02:00
import { I18nService } from 'jslib/abstractions/i18n.service' ;
import { MessagingService } from 'jslib/abstractions/messaging.service' ;
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service' ;
import { StorageService } from 'jslib/abstractions/storage.service' ;
2018-11-16 17:08:36 +01:00
import { UserService } from 'jslib/abstractions/user.service' ;
2020-04-06 17:40:16 +02:00
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service' ;
2018-04-10 22:20:49 +02:00
const RateUrls = {
2018-07-09 15:12:41 +02:00
[ DeviceType . ChromeExtension ] :
2018-04-10 22:20:49 +02:00
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews' ,
2018-07-09 15:12:41 +02:00
[ DeviceType . FirefoxExtension ] :
2018-04-10 22:20:49 +02:00
'https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/#reviews' ,
2018-07-09 15:12:41 +02:00
[ DeviceType . OperaExtension ] :
2018-04-10 22:20:49 +02:00
'https://addons.opera.com/en/extensions/details/bitwarden-free-password-manager/#feedback-container' ,
2018-07-09 15:12:41 +02:00
[ DeviceType . EdgeExtension ] :
2020-03-03 14:48:11 +01:00
'https://microsoftedge.microsoft.com/addons/detail/jbkfoedolllekgbhcbcoahefnbanhhlh' ,
2018-07-09 15:12:41 +02:00
[ DeviceType . VivaldiExtension ] :
2018-04-10 22:20:49 +02:00
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews' ,
2018-07-09 15:12:41 +02:00
[ DeviceType . SafariExtension ] :
2019-08-21 16:05:17 +02:00
'https://apps.apple.com/app/bitwarden/id1352778147' ,
2018-04-10 22:20:49 +02:00
} ;
2018-04-09 23:35:16 +02:00
@Component ( {
selector : 'app-settings' ,
templateUrl : 'settings.component.html' ,
} )
2018-04-10 22:20:49 +02:00
export class SettingsComponent implements OnInit {
2020-08-18 22:13:37 +02:00
@ViewChild ( 'vaultTimeoutSelect' , { read : ElementRef , static : true } ) vaultTimeoutSelectRef : ElementRef ;
@ViewChild ( 'vaultTimeoutActionSelect' , { read : ElementRef , static : true } ) vaultTimeoutActionSelectRef : ElementRef ;
2020-04-06 17:40:16 +02:00
vaultTimeouts : any [ ] ;
vaultTimeout : number = null ;
vaultTimeoutActions : any [ ] ;
vaultTimeoutAction : string ;
2019-02-13 05:53:04 +01:00
pin : boolean = null ;
2020-10-09 17:16:15 +02:00
biometric : boolean = null ;
2020-04-06 17:40:16 +02:00
previousVaultTimeout : number = null ;
2018-04-10 22:20:49 +02:00
constructor ( private platformUtilsService : PlatformUtilsService , private i18nService : I18nService ,
2020-04-06 17:40:16 +02:00
private analytics : Angulartics2 , private vaultTimeoutService : VaultTimeoutService ,
2018-04-10 22:20:49 +02:00
private storageService : StorageService , public messagingService : MessagingService ,
2018-11-16 17:08:36 +01:00
private router : Router , private environmentService : EnvironmentService ,
private cryptoService : CryptoService , private userService : UserService ) {
2018-04-10 22:20:49 +02:00
}
async ngOnInit() {
2020-09-15 16:50:45 +02:00
const showOnLocked = ! this . platformUtilsService . isFirefox ( ) && ! this . platformUtilsService . isSafari ( ) ;
2018-04-10 22:20:49 +02:00
2020-04-06 17:40:16 +02:00
this . vaultTimeouts = [
2018-04-10 22:20:49 +02:00
{ name : this.i18nService.t ( 'immediately' ) , value : 0 } ,
{ name : this.i18nService.t ( 'oneMinute' ) , value : 1 } ,
{ name : this.i18nService.t ( 'fiveMinutes' ) , value : 5 } ,
{ name : this.i18nService.t ( 'fifteenMinutes' ) , value : 15 } ,
{ name : this.i18nService.t ( 'thirtyMinutes' ) , value : 30 } ,
{ name : this.i18nService.t ( 'oneHour' ) , value : 60 } ,
{ name : this.i18nService.t ( 'fourHours' ) , value : 240 } ,
// { name: i18nService.t('onIdle'), value: -4 },
// { name: i18nService.t('onSleep'), value: -3 },
] ;
if ( showOnLocked ) {
2020-04-06 17:40:16 +02:00
this . vaultTimeouts . push ( { name : this.i18nService.t ( 'onLocked' ) , value : - 2 } ) ;
2018-04-10 22:20:49 +02:00
}
2020-04-06 17:40:16 +02:00
this . vaultTimeouts . push ( { name : this.i18nService.t ( 'onRestart' ) , value : - 1 } ) ;
this . vaultTimeouts . push ( { name : this.i18nService.t ( 'never' ) , value : null } ) ;
2018-04-10 22:20:49 +02:00
2020-04-06 17:40:16 +02:00
this . vaultTimeoutActions = [
{ name : this.i18nService.t ( 'lock' ) , value : 'lock' } ,
{ name : this.i18nService.t ( 'logOut' ) , value : 'logOut' } ,
] ;
let timeout = await this . storageService . get < number > ( ConstantsService . vaultTimeoutKey ) ;
if ( timeout != null ) {
if ( timeout === - 2 && ! showOnLocked ) {
timeout = - 1 ;
2018-04-10 22:20:49 +02:00
}
2020-04-06 17:40:16 +02:00
this . vaultTimeout = timeout ;
2018-04-10 22:20:49 +02:00
}
2020-04-06 17:40:16 +02:00
this . previousVaultTimeout = this . vaultTimeout ;
const action = await this . storageService . get < string > ( ConstantsService . vaultTimeoutActionKey ) ;
this . vaultTimeoutAction = action == null ? 'lock' : action ;
2019-02-13 05:53:04 +01:00
2020-04-06 17:40:16 +02:00
const pinSet = await this . vaultTimeoutService . isPinLockSet ( ) ;
2019-02-14 06:46:28 +01:00
this . pin = pinSet [ 0 ] || pinSet [ 1 ] ;
2020-10-09 17:16:15 +02:00
this . biometric = await this . vaultTimeoutService . isBiometricLockSet ( ) ;
2018-04-10 22:20:49 +02:00
}
2020-04-06 17:40:16 +02:00
async saveVaultTimeout ( newValue : number ) {
2019-08-19 21:57:34 +02:00
if ( newValue == null ) {
2018-10-04 04:46:11 +02:00
const confirmed = await this . platformUtilsService . showDialog (
this . i18nService . t ( 'neverLockWarning' ) , null ,
this . i18nService . t ( 'yes' ) , this . i18nService . t ( 'cancel' ) , 'warning' ) ;
if ( ! confirmed ) {
2020-04-06 17:40:16 +02:00
this . vaultTimeouts . forEach ( ( option : any , i ) = > {
if ( option . value === this . vaultTimeout ) {
this . vaultTimeoutSelectRef . nativeElement . value = i + ': ' + this . vaultTimeout ;
2018-10-04 04:46:11 +02:00
}
} ) ;
return ;
}
}
2020-04-06 17:40:16 +02:00
this . previousVaultTimeout = this . vaultTimeout ;
this . vaultTimeout = newValue ;
await this . vaultTimeoutService . setVaultTimeoutOptions ( this . vaultTimeout != null ? this . vaultTimeout : null ,
this . vaultTimeoutAction ) ;
if ( this . previousVaultTimeout == null ) {
2018-10-04 04:46:11 +02:00
this . messagingService . send ( 'bgReseedStorage' ) ;
}
2018-04-10 22:20:49 +02:00
}
2020-04-06 17:40:16 +02:00
async saveVaultTimeoutAction ( newValue : string ) {
2020-04-25 16:13:00 +02:00
if ( newValue === 'logOut' ) {
const confirmed = await this . platformUtilsService . showDialog (
this . i18nService . t ( 'vaultTimeoutLogOutConfirmation' ) ,
this . i18nService . t ( 'vaultTimeoutLogOutConfirmationTitle' ) ,
this . i18nService . t ( 'yes' ) , this . i18nService . t ( 'cancel' ) , 'warning' ) ;
if ( ! confirmed ) {
this . vaultTimeoutActions . forEach ( ( option : any , i ) = > {
if ( option . value === this . vaultTimeoutAction ) {
this . vaultTimeoutActionSelectRef . nativeElement . value = i + ': ' + this . vaultTimeoutAction ;
}
} ) ;
return ;
}
}
2020-04-06 17:40:16 +02:00
this . vaultTimeoutAction = newValue ;
await this . vaultTimeoutService . setVaultTimeoutOptions ( this . vaultTimeout != null ? this . vaultTimeout : null ,
this . vaultTimeoutAction ) ;
}
2019-02-13 05:53:04 +01:00
async updatePin() {
if ( this . pin ) {
2019-02-14 06:46:28 +01:00
const div = document . createElement ( 'div' ) ;
const label = document . createElement ( 'label' ) ;
label . className = 'checkbox' ;
const checkboxText = document . createElement ( 'span' ) ;
const restartText = document . createTextNode ( this . i18nService . t ( 'lockWithMasterPassOnRestart' ) ) ;
checkboxText . appendChild ( restartText ) ;
label . innerHTML = '<input type="checkbox" id="master-pass-restart" checked>' ;
label . appendChild ( checkboxText ) ;
2020-03-04 17:42:21 +01:00
div . innerHTML =
` <div class="swal2-text"> ${ this . i18nService . t ( 'setYourPinCode' ) } </div> ` +
'<input type="text" class="swal2-input" id="pin-val" autocomplete="off" ' +
2019-03-06 20:40:09 +01:00
'autocapitalize="none" autocorrect="none" spellcheck="false" inputmode="verbatim">' ;
2020-03-04 17:42:21 +01:00
2019-02-14 06:46:28 +01:00
( div . querySelector ( '#pin-val' ) as HTMLInputElement ) . placeholder = this . i18nService . t ( 'pin' ) ;
div . appendChild ( label ) ;
2020-03-04 17:42:21 +01:00
const submitted = await Swal . fire ( {
heightAuto : false ,
buttonsStyling : false ,
html : div ,
showCancelButton : true ,
cancelButtonText : this.i18nService.t ( 'cancel' ) ,
showConfirmButton : true ,
confirmButtonText : this.i18nService.t ( 'submit' ) ,
2019-02-13 05:53:04 +01:00
} ) ;
2020-03-04 17:42:21 +01:00
2019-02-14 06:46:28 +01:00
let pin : string = null ;
let masterPassOnRestart : boolean = null ;
2020-03-04 17:42:21 +01:00
if ( submitted . value ) {
2019-02-14 06:46:28 +01:00
pin = ( document . getElementById ( 'pin-val' ) as HTMLInputElement ) . value ;
masterPassOnRestart = ( document . getElementById ( 'master-pass-restart' ) as HTMLInputElement ) . checked ;
}
2019-02-13 05:53:04 +01:00
if ( pin != null && pin . trim ( ) !== '' ) {
2019-08-29 15:41:04 +02:00
const kdf = await this . userService . getKdf ( ) ;
const kdfIterations = await this . userService . getKdfIterations ( ) ;
const email = await this . userService . getEmail ( ) ;
const pinKey = await this . cryptoService . makePinKey ( pin , email , kdf , kdfIterations ) ;
const key = await this . cryptoService . getKey ( ) ;
const pinProtectedKey = await this . cryptoService . encrypt ( key . key , pinKey ) ;
2019-02-14 06:46:28 +01:00
if ( masterPassOnRestart ) {
const encPin = await this . cryptoService . encrypt ( pin ) ;
await this . storageService . save ( ConstantsService . protectedPin , encPin . encryptedString ) ;
2020-04-06 17:40:16 +02:00
this . vaultTimeoutService . pinProtectedKey = pinProtectedKey ;
2019-02-14 06:46:28 +01:00
} else {
await this . storageService . save ( ConstantsService . pinProtectedKey , pinProtectedKey . encryptedString ) ;
}
2019-02-13 05:53:04 +01:00
} else {
this . pin = false ;
}
}
if ( ! this . pin ) {
2019-08-29 15:41:04 +02:00
await this . cryptoService . clearPinProtectedKey ( ) ;
2020-04-06 17:40:16 +02:00
await this . vaultTimeoutService . clear ( ) ;
2019-02-13 05:53:04 +01:00
}
}
2020-10-09 17:16:15 +02:00
async updateBiometric() {
const current = this . biometric ;
if ( this . biometric ) {
this . biometric = false ;
} else {
const div = document . createElement ( 'div' ) ;
div . innerHTML = ` <div class="swal2-text"> ${ this . i18nService . t ( 'awaitDesktop' ) } </div> ` ;
const submitted = Swal . fire ( {
heightAuto : false ,
buttonsStyling : false ,
html : div ,
showCancelButton : true ,
cancelButtonText : this.i18nService.t ( 'cancel' ) ,
showConfirmButton : false ,
} ) ;
// TODO: Show waiting message
this . biometric = await this . platformUtilsService . authenticateBiometric ( ) ;
Swal . close ( ) ;
if ( this . biometric == false ) {
this . platformUtilsService . showToast ( "error" , "Unable to enable biometrics" , "Ensure the desktop application is running, and browser integration is enabled." ) ;
}
}
if ( this . biometric === current ) {
return ;
}
if ( this . biometric ) {
await this . storageService . save ( ConstantsService . biometricUnlockKey , true ) ;
} else {
await this . storageService . remove ( ConstantsService . biometricUnlockKey ) ;
}
this . vaultTimeoutService . biometricLocked = false ;
await this . cryptoService . toggleKey ( ) ;
}
2018-04-10 22:20:49 +02:00
async lock() {
this . analytics . eventTrack . next ( { action : 'Lock Now' } ) ;
2020-04-06 17:40:16 +02:00
await this . vaultTimeoutService . lock ( true ) ;
2018-04-10 22:20:49 +02:00
}
async logOut() {
const confirmed = await this . platformUtilsService . showDialog (
this . i18nService . t ( 'logOutConfirmation' ) , this . i18nService . t ( 'logOut' ) ,
this . i18nService . t ( 'yes' ) , this . i18nService . t ( 'cancel' ) ) ;
if ( confirmed ) {
this . messagingService . send ( 'logout' ) ;
}
}
async changePassword() {
this . analytics . eventTrack . next ( { action : 'Clicked Change Password' } ) ;
const confirmed = await this . platformUtilsService . showDialog (
this . i18nService . t ( 'changeMasterPasswordConfirmation' ) , this . i18nService . t ( 'changeMasterPassword' ) ,
this . i18nService . t ( 'yes' ) , this . i18nService . t ( 'cancel' ) ) ;
if ( confirmed ) {
BrowserApi . createNewTab ( 'https://help.bitwarden.com/article/change-your-master-password/' ) ;
}
}
async twoStep() {
this . analytics . eventTrack . next ( { action : 'Clicked Two-step Login' } ) ;
const confirmed = await this . platformUtilsService . showDialog (
this . i18nService . t ( 'twoStepLoginConfirmation' ) , this . i18nService . t ( 'twoStepLogin' ) ,
this . i18nService . t ( 'yes' ) , this . i18nService . t ( 'cancel' ) ) ;
if ( confirmed ) {
BrowserApi . createNewTab ( 'https://help.bitwarden.com/article/setup-two-step-login/' ) ;
}
}
async share() {
2018-04-17 19:19:58 +02:00
this . analytics . eventTrack . next ( { action : 'Clicked Share Vault' } ) ;
const confirmed = await this . platformUtilsService . showDialog (
this . i18nService . t ( 'shareVaultConfirmation' ) , this . i18nService . t ( 'shareVault' ) ,
this . i18nService . t ( 'yes' ) , this . i18nService . t ( 'cancel' ) ) ;
if ( confirmed ) {
BrowserApi . createNewTab ( 'https://help.bitwarden.com/article/what-is-an-organization/' ) ;
}
}
async webVault() {
this . analytics . eventTrack . next ( { action : 'Clicked Web Vault' } ) ;
2018-06-25 14:06:38 +02:00
let url = this . environmentService . getWebVaultUrl ( ) ;
if ( url == null ) {
url = 'https://vault.bitwarden.com' ;
}
BrowserApi . createNewTab ( url ) ;
2018-04-10 22:20:49 +02:00
}
import ( ) {
this . analytics . eventTrack . next ( { action : 'Clicked Import Items' } ) ;
BrowserApi . createNewTab ( 'https://help.bitwarden.com/article/import-data/' ) ;
}
2018-04-14 04:18:21 +02:00
export ( ) {
this . router . navigate ( [ '/export' ] ) ;
}
2018-04-12 23:28:33 +02:00
help() {
this . analytics . eventTrack . next ( { action : 'Clicked Help and Feedback' } ) ;
BrowserApi . createNewTab ( 'https://help.bitwarden.com/' ) ;
}
about() {
this . analytics . eventTrack . next ( { action : 'Clicked About' } ) ;
2018-04-19 15:45:51 +02:00
const year = ( new Date ( ) ) . getFullYear ( ) ;
2018-04-12 23:28:33 +02:00
const versionText = document . createTextNode (
this . i18nService . t ( 'version' ) + ': ' + BrowserApi . getApplicationVersion ( ) ) ;
const div = document . createElement ( 'div' ) ;
2019-10-08 23:04:44 +02:00
div . innerHTML = ` <p class="text-center"><i class="fa fa-shield fa-3x" aria-hidden="true"></i></p>
2020-02-19 04:35:28 +01:00
< p class = "text-center" > < b > Bitwarden < / b > < br > & copy ; Bitwarden Inc . 2015 - ` + year + ` < / p > ` ;
2018-04-12 23:28:33 +02:00
div . appendChild ( versionText ) ;
2020-03-04 17:42:21 +01:00
Swal . fire ( {
heightAuto : false ,
buttonsStyling : false ,
html : div ,
showConfirmButton : false ,
showCancelButton : true ,
cancelButtonText : this.i18nService.t ( 'close' ) ,
2018-04-12 23:28:33 +02:00
} ) ;
}
2018-11-16 17:08:36 +01:00
async fingerprint() {
this . analytics . eventTrack . next ( { action : 'Clicked Fingerprint' } ) ;
const fingerprint = await this . cryptoService . getFingerprint ( await this . userService . getUserId ( ) ) ;
const p = document . createElement ( 'p' ) ;
p . innerText = this . i18nService . t ( 'yourAccountsFingerprint' ) + ':' ;
const p2 = document . createElement ( 'p' ) ;
p2 . innerText = fingerprint . join ( '-' ) ;
const div = document . createElement ( 'div' ) ;
div . appendChild ( p ) ;
div . appendChild ( p2 ) ;
2020-03-04 17:42:21 +01:00
const result = await Swal . fire ( {
heightAuto : false ,
buttonsStyling : false ,
html : div ,
showCancelButton : true ,
cancelButtonText : this.i18nService.t ( 'close' ) ,
showConfirmButton : true ,
confirmButtonText : this.i18nService.t ( 'learnMore' ) ,
2018-11-16 17:08:36 +01:00
} ) ;
2020-03-04 17:42:21 +01:00
if ( result . value ) {
2018-11-16 17:17:16 +01:00
this . platformUtilsService . launchUri ( 'https://help.bitwarden.com/article/fingerprint-phrase/' ) ;
2018-11-16 17:08:36 +01:00
}
}
2018-04-10 22:20:49 +02:00
rate() {
this . analytics . eventTrack . next ( { action : 'Rate Extension' } ) ;
2020-09-15 16:50:45 +02:00
const deviceType = this . platformUtilsService . getDevice ( ) ;
2020-03-03 14:48:11 +01:00
BrowserApi . createNewTab ( ( RateUrls as any ) [ deviceType ] ) ;
2018-04-10 22:20:49 +02:00
}
}