[Auto-Logout] Implement upstream changes (#506)

* Initial commit of auto logout functionality

* Update jslib 31a2574 -> 28e3fff

* Reverting prod URLs

* Set log out expired param to false

Co-authored-by: Vincent Salucci <vsalucci@bitwarden.com>
This commit is contained in:
Vincent Salucci 2020-03-30 09:59:47 -05:00 committed by GitHub
parent 5bf3ca2708
commit d58550c2b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 38 deletions

2
jslib

@ -1 +1 @@
Subproject commit 31a257407be7f8f47624b0d021363aaf2cfda2d7
Subproject commit 28e3fff739e64c2dd80d3d98717e2921895d16df

View File

@ -4,12 +4,12 @@ import { Router } from '@angular/router';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LockService } from 'jslib/abstractions/lock.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { UserService } from 'jslib/abstractions/user.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
import { RouterService } from '../services/router.service';
@ -23,11 +23,11 @@ export class LockComponent extends BaseLockComponent {
constructor(router: Router, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
userService: UserService, cryptoService: CryptoService,
storageService: StorageService, lockService: LockService,
storageService: StorageService, vaultTimeoutService: VaultTimeoutService,
environmentService: EnvironmentService, private routerService: RouterService,
stateService: StateService) {
super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService,
storageService, lockService, environmentService, stateService);
storageService, vaultTimeoutService, environmentService, stateService);
}
async ngOnInit() {

View File

@ -35,7 +35,6 @@ import { CryptoService } from 'jslib/abstractions/crypto.service';
import { EventService } from 'jslib/abstractions/event.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LockService } from 'jslib/abstractions/lock.service';
import { NotificationsService } from 'jslib/abstractions/notifications.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
@ -46,6 +45,7 @@ import { StateService } from 'jslib/abstractions/state.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { TokenService } from 'jslib/abstractions/token.service';
import { UserService } from 'jslib/abstractions/user.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
import { ConstantsService } from 'jslib/services/constants.service';
@ -78,7 +78,7 @@ export class AppComponent implements OnDestroy, OnInit {
private authService: AuthService, private router: Router, private analytics: Angulartics2,
private toasterService: ToasterService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private ngZone: NgZone,
private lockService: LockService, private storageService: StorageService,
private vaultTimeoutService: VaultTimeoutService, private storageService: StorageService,
private cryptoService: CryptoService, private collectionService: CollectionService,
private sanitizer: DomSanitizer, private searchService: SearchService,
private notificationsService: NotificationsService, private routerService: RouterService,
@ -110,7 +110,7 @@ export class AppComponent implements OnDestroy, OnInit {
this.logOut(!!message.expired);
break;
case 'lockVault':
await this.lockService.lock();
await this.vaultTimeoutService.lock();
break;
case 'locked':
this.notificationsService.updateConnection(false);

View File

@ -38,7 +38,6 @@ import { EventService as EventLoggingService } from 'jslib/services/event.servic
import { ExportService } from 'jslib/services/export.service';
import { FolderService } from 'jslib/services/folder.service';
import { ImportService } from 'jslib/services/import.service';
import { LockService } from 'jslib/services/lock.service';
import { NotificationsService } from 'jslib/services/notifications.service';
import { PasswordGenerationService } from 'jslib/services/passwordGeneration.service';
import { PolicyService } from 'jslib/services/policy.service';
@ -49,6 +48,7 @@ import { SyncService } from 'jslib/services/sync.service';
import { TokenService } from 'jslib/services/token.service';
import { TotpService } from 'jslib/services/totp.service';
import { UserService } from 'jslib/services/user.service';
import { VaultTimeoutService } from 'jslib/services/vaultTimeout.service';
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
import { ApiService as ApiServiceAbstraction } from 'jslib/abstractions/api.service';
@ -65,7 +65,6 @@ import { ExportService as ExportServiceAbstraction } from 'jslib/abstractions/ex
import { FolderService as FolderServiceAbstraction } from 'jslib/abstractions/folder.service';
import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
import { ImportService as ImportServiceAbstraction } from 'jslib/abstractions/import.service';
import { LockService as LockServiceAbstraction } from 'jslib/abstractions/lock.service';
import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service';
import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service';
import { NotificationsService as NotificationsServiceAbstraction } from 'jslib/abstractions/notifications.service';
@ -82,6 +81,7 @@ import { SyncService as SyncServiceAbstraction } from 'jslib/abstractions/sync.s
import { TokenService as TokenServiceAbstraction } from 'jslib/abstractions/token.service';
import { TotpService as TotpServiceAbstraction } from 'jslib/abstractions/totp.service';
import { UserService as UserServiceAbstraction } from 'jslib/abstractions/user.service';
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib/abstractions/vaultTimeout.service';
const i18nService = new I18nService(window.navigator.language, 'locales');
const stateService = new StateService();
@ -108,8 +108,9 @@ const folderService = new FolderService(cryptoService, userService, apiService,
const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService);
searchService = new SearchService(cipherService, platformUtilsService);
const policyService = new PolicyService(userService, storageService);
const lockService = new LockService(cipherService, folderService, collectionService,
cryptoService, platformUtilsService, storageService, messagingService, searchService, userService, null);
const vaultTimeoutService = new VaultTimeoutService(cipherService, folderService, collectionService,
cryptoService, platformUtilsService, storageService, messagingService, searchService, userService, null,
async () => messagingService.send('logout', { expired: false }));
const syncService = new SyncService(userService, apiService, settingsService,
folderService, cipherService, cryptoService, collectionService, storageService, messagingService, policyService,
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
@ -121,7 +122,7 @@ const authService = new AuthService(cryptoService, apiService,
const exportService = new ExportService(folderService, cipherService, apiService);
const importService = new ImportService(cipherService, folderService, apiService, i18nService, collectionService);
const notificationsService = new NotificationsService(userService, syncService, appIdService,
apiService, lockService, async () => messagingService.send('logout', { expired: true }));
apiService, vaultTimeoutService, async () => messagingService.send('logout', { expired: true }));
const environmentService = new EnvironmentService(apiService, storageService, notificationsService);
const auditService = new AuditService(cryptoFunctionService, apiService);
const eventLoggingService = new EventLoggingService(storageService, apiService, userService, cipherService);
@ -156,7 +157,7 @@ export function initFactory(): Function {
});
setTimeout(() => notificationsService.init(environmentService), 3000);
lockService.init(true);
vaultTimeoutService.init(true);
const locale = await storageService.get<string>(ConstantsService.localeKey);
await i18nService.init(locale);
eventLoggingService.init(true);
@ -205,7 +206,7 @@ export function initFactory(): Function {
{ provide: MessagingServiceAbstraction, useValue: messagingService },
{ provide: BroadcasterService, useValue: broadcasterService },
{ provide: SettingsServiceAbstraction, useValue: settingsService },
{ provide: LockServiceAbstraction, useValue: lockService },
{ provide: VaultTimeoutServiceAbstraction, useValue: vaultTimeoutService },
{ provide: StorageServiceAbstraction, useValue: storageService },
{ provide: StateServiceAbstraction, useValue: stateService },
{ provide: ExportServiceAbstraction, useValue: exportService },

View File

@ -4,18 +4,18 @@ import {
Router,
} from '@angular/router';
import { LockService } from 'jslib/abstractions/lock.service';
import { UserService } from 'jslib/abstractions/user.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
@Injectable()
export class UnauthGuardService implements CanActivate {
constructor(private lockService: LockService, private userService: UserService,
constructor(private vaultTimeoutService: VaultTimeoutService, private userService: UserService,
private router: Router) { }
async canActivate() {
const isAuthed = await this.userService.isAuthenticated();
if (isAuthed) {
const locked = await this.lockService.isLocked();
const locked = await this.vaultTimeoutService.isLocked();
if (locked) {
this.router.navigate(['lock']);
} else {

View File

@ -6,14 +6,33 @@
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="lockOption">{{'lockOptions' | i18n}}</label>
<select id="lockOption" name="LockOption" [(ngModel)]="lockOption" class="form-control">
<option *ngFor="let o of lockOptions" [ngValue]="o.value">{{o.name}}</option>
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
<select id="vaultTimeout" name="VaultTimeout" [(ngModel)]="vaultTimeout" class="form-control">
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
</select>
<small class="form-text text-muted">{{'lockOptionsDesc' | i18n}}</small>
<small class="form-text text-muted">{{'vaultTimeoutDesc' | i18n}}</small>
</div>
</div>
</div>
<div class="form-group">
<label>{{'vaultTimeoutAction' | i18n}}</label>
<div class="form-check form-check-block">
<input class="form-check-input" type="radio" name="vaultTimeoutAction" id="vaultTimeoutActionLock"
value="lock" [(ngModel)]="vaultTimeoutAction">
<label class="form-check-label" for="vaultTimeoutActionLock">
{{'lock' | i18n}}
<small>{{'vaultTimeoutActionLockDesc' | i18n}}</small>
</label>
</div>
<div class="form-check mt-2 form-check-block">
<input class="form-check-input" type="radio" name="vaultTimeoutAction" id="vaultTimeoutActionLogOut"
value="logOut" [(ngModel)]="vaultTimeoutAction">
<label class="form-check-label" for="vaultTimeoutActionLogOut">
{{'logOut' | i18n}}
<small>{{'vaultTimeoutActionLogOutDesc' | i18n}}</small>
</label>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group">

View File

@ -7,10 +7,10 @@ import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LockService } from 'jslib/abstractions/lock.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
import { ConstantsService } from 'jslib/services/constants.service';
@ -21,20 +21,21 @@ import { Utils } from 'jslib/misc/utils';
templateUrl: 'options.component.html',
})
export class OptionsComponent implements OnInit {
lockOption: number = null;
vaultTimeout: number = null;
vaultTimeoutAction: string = 'lock';
disableIcons: boolean;
enableGravatars: boolean;
locale: string;
lockOptions: any[];
vaultTimeouts: any[];
localeOptions: any[];
private startingLocale: string;
constructor(private storageService: StorageService, private stateService: StateService,
private analytics: Angulartics2, private i18nService: I18nService,
private toasterService: ToasterService, private lockService: LockService,
private toasterService: ToasterService, private vaultTimeoutService: VaultTimeoutService,
private platformUtilsService: PlatformUtilsService) {
this.lockOptions = [
this.vaultTimeouts = [
{ name: i18nService.t('oneMinute'), value: 1 },
{ name: i18nService.t('fiveMinutes'), value: 5 },
{ name: i18nService.t('fifteenMinutes'), value: 15 },
@ -44,7 +45,7 @@ export class OptionsComponent implements OnInit {
{ name: i18nService.t('onRefresh'), value: -1 },
];
if (this.platformUtilsService.isDev()) {
this.lockOptions.push({ name: i18nService.t('never'), value: null });
this.vaultTimeouts.push({ name: i18nService.t('never'), value: null });
}
const localeOptions: any[] = [];
@ -61,14 +62,16 @@ export class OptionsComponent implements OnInit {
}
async ngOnInit() {
this.lockOption = await this.storageService.get<number>(ConstantsService.lockOptionKey);
this.vaultTimeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
this.vaultTimeoutAction = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
this.disableIcons = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
this.enableGravatars = await this.storageService.get<boolean>('enableGravatars');
this.locale = this.startingLocale = await this.storageService.get<string>(ConstantsService.localeKey);
}
async submit() {
await this.lockService.setLockOption(this.lockOption != null ? this.lockOption : null);
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout != null ? this.vaultTimeout : null,
this.vaultTimeoutAction);
await this.storageService.save(ConstantsService.disableFaviconKey, this.disableIcons);
await this.stateService.save(ConstantsService.disableFaviconKey, this.disableIcons);
await this.storageService.save('enableGravatars', this.enableGravatars);

View File

@ -2816,11 +2816,11 @@
"filters": {
"message": "Filters"
},
"lockOptions": {
"message": "Lock Options"
"vaultTimeout": {
"message": "Vault Timeout"
},
"lockOptionsDesc": {
"message": "Choose when your vault locks. A locked vault requires that you re-enter your master password to access it again."
"vaultTimeoutDesc": {
"message": "Choose when your vault will timeout and perform the selected action."
},
"oneMinute": {
"message": "1 minute"
@ -3040,5 +3040,17 @@
},
"userPreference": {
"message": "User Preference"
},
"vaultTimeoutAction": {
"message": "Vault Timeout Action"
},
"vaultTimeoutActionLockDesc": {
"message": "A locked vault requires that you re-enter your master password to access it again."
},
"vaultTimeoutActionLogOutDesc": {
"message": "A logged out vault requires that you re-authenticate to access it again."
},
"lock": {
"message": "Lock"
}
}

View File

@ -4,16 +4,24 @@ import { ConstantsService } from 'jslib/services';
export class HtmlStorageService implements StorageService {
private localStorageKeys = new Set(['appId', 'anonymousAppId', 'rememberedEmail', 'passwordGenerationOptions',
ConstantsService.disableFaviconKey, ConstantsService.lockOptionKey, 'rememberEmail', 'enableGravatars',
ConstantsService.localeKey, ConstantsService.lockOptionKey, ConstantsService.autoConfirmFingerprints]);
ConstantsService.disableFaviconKey, 'rememberEmail', 'enableGravatars', ConstantsService.localeKey,
ConstantsService.autoConfirmFingerprints, ConstantsService.vaultTimeoutKey,
ConstantsService.vaultTimeoutActionKey]);
private localStorageStartsWithKeys = ['twoFactorToken_', ConstantsService.collapsedGroupingsKey + '_'];
constructor(private platformUtilsService: PlatformUtilsService) { }
async init() {
const lockOption = await this.get<number>(ConstantsService.lockOptionKey);
if (lockOption == null && !this.platformUtilsService.isDev()) {
await this.save(ConstantsService.lockOptionKey, 15);
// LockOption -> VaultTimeout (uses the same legacy string value for backwards compat)
const vaultTimeout = await this.get<number>(ConstantsService.vaultTimeoutKey);
if (vaultTimeout == null && !this.platformUtilsService.isDev()) {
await this.save(ConstantsService.vaultTimeoutKey, 15);
}
// Default Action to lock
const vaultTimeoutAction = await this.get<string>(ConstantsService.vaultTimeoutActionKey);
if (vaultTimeoutAction == null) {
await this.save(ConstantsService.vaultTimeoutActionKey, 'lock');
}
}