Skip to content

Commit

Permalink
Merge pull request #242 from NakulManchanda/activity-storage-event
Browse files Browse the repository at this point in the history
login activity, storage event changes
  • Loading branch information
DivergentEuropeans authored Jun 22, 2020
2 parents ed36eaf + 19f84fd commit 4a6c02d
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { ZluxPopupManagerModule } from '@zlux/widgets';
import { LoginComponent } from './login/login.component';
import { AuthenticationManager } from './authentication-manager.service';
import { StartURLManagerModule } from '../start-url-manager';
import { StorageService } from './storage.service';
import { IdleWarnService } from './idleWarn.service';

@NgModule({
imports: [
Expand All @@ -36,7 +38,9 @@ import { StartURLManagerModule } from '../start-url-manager';
providers: [
AuthenticationManager,
/* Expose authentication manager to window managers */
{ provide: MVDHosting.Tokens.AuthenticationManagerToken, useExisting: AuthenticationManager }
{ provide: MVDHosting.Tokens.AuthenticationManagerToken, useExisting: AuthenticationManager },
StorageService,
IdleWarnService
]
})
export class AuthenticationModule {
Expand Down
118 changes: 118 additions & 0 deletions virtual-desktop/src/app/authentication-manager/idleWarn.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import * as moment from 'moment';
import { Injectable } from '@angular/core';
import { ZluxPopupManagerService, ZluxErrorSeverity } from '@zlux/widgets';
import { TranslationService } from 'angular-l10n';
import { BaseLogger } from 'virtual-desktop-logger';
import { Subscription } from 'rxjs/Subscription';
import { StorageService } from './storage.service';

@Injectable()
export class IdleWarnService {

private report: any;
private readonly logger: ZLUX.ComponentLogger = BaseLogger;

constructor(private popupManager: ZluxPopupManagerService,
public translation: TranslationService,
private storageService: StorageService
) {
}

public createRetryErrorReport(renewSession: any, isIdle: any) {
this.removeErrorReport();
this.report = this.popupManager.createErrorReport(
ZluxErrorSeverity.WARNING,
this.translation.translate('Session Renewal Error'),
this.translation.translate('Session could not be renewed. Logout will occur unless renewed. Click here to retry.'),
{
blocking: false,
buttons: [this.translation.translate('Retry'), this.translation.translate('Dismiss')]
}
);

this.report.subscription = new Subscription();
this.onUserActionSubscribe(renewSession,'Retry');
this.onActivitySubscribe(renewSession, isIdle);
}

onUserActionSubscribe(renewSession: any, action: string) {
if(this.report) {
this.report.subscription.add(this.report.subject.subscribe((buttonName:any)=> {
if (buttonName == this.translation.translate(action)) {
renewSession();
}
}));
}
}

public createContinueErrorReport(renewSession: any, isIdle: any, expirationInMS: number, desktopSize: any) {
this.removeErrorReport();
let popupStyle;

/* According to the size of the desktop, we move the expiration prompt to align with the app bar */
switch (desktopSize) {
case 3: {
popupStyle = {
'margin-bottom': '70px',
'margin-right': '-5px'
};
break;
}
case 1: {
popupStyle = {
'margin-bottom': '15px',
'margin-right': '-10px'
};
break;
}
default: {
popupStyle = {
'margin-bottom': '35px',
'margin-right': '-5px'
};
break;
}
}

this.report = this.popupManager.createErrorReport(
ZluxErrorSeverity.WARNING,
this.translation.translate('Session Expiring Soon'),
//TODO: Add translation
//this.translation.translate('You will be logged out at ', { expirationInMS: moment().add(expirationInMS/1000, 'seconds').format('LT') }),
this.translation.translate('You will be logged out at ' + moment().add(expirationInMS/1000, 'seconds').format('LT')),
{
blocking: false,
buttons: [this.translation.translate('Continue session')],
timestamp: false,
theme: "dark",
style: popupStyle,
callToAction: true
});

this.report.subscription = new Subscription();
this.onUserActionSubscribe(renewSession, 'Continue session');
this.onActivitySubscribe(renewSession, isIdle);
}


onActivitySubscribe(renewSession: any, isIdle: any) {
if(this.report) {
this.report.subscription.add(this.storageService.lastActive.subscribe(()=> {
if (!isIdle()) {
this.logger.info('ZWED5047I', 'renew on activity'); /*this.logger.info('Near session expiration, but renewing session due to activity');*/
renewSession();
this.removeErrorReport();
}
}));
}
}

removeErrorReport() {
if (this.report) {
this.popupManager.removeReport(this.report.id);
this.report.subscription.unsubscribe();
this.report = undefined;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { Component, OnInit, ChangeDetectorRef, Injector } from '@angular/core';
import { AuthenticationManager,
LoginExpirationIdleCheckEvent } from '../authentication-manager.service';
import { TranslationService } from 'angular-l10n';
//import { Observable } from 'rxjs/Observable';
import { ZluxPopupManagerService, ZluxErrorSeverity } from '@zlux/widgets';
import { BaseLogger } from 'virtual-desktop-logger';
import * as moment from 'moment';
import { StorageService } from '../storage.service';
import { StorageKey } from '../storage-enum';
import { IdleWarnService } from '../idleWarn.service';

const ACTIVITY_IDLE_TIMEOUT_MS = 300000; //5 minutes
const HTTP_STATUS_PRECONDITION_REQUIRED = 428;
Expand All @@ -43,17 +43,16 @@ export class LoginComponent implements OnInit {
confirmNewPassword: string;
errorMessage: string;
loginMessage: string;
private idleWarnModal: any;
private lastActive: number = 0;
expiredPassword: boolean;
private passwordServices: Set<string>;
private themeManager: any;

constructor(
private authenticationService: AuthenticationManager,
private storageService: StorageService,
public translation: TranslationService,
private idleWarnService: IdleWarnService,
private cdr: ChangeDetectorRef,
private popupManager: ZluxPopupManagerService,
private injector: Injector
) {
this.themeManager = this.injector.get(MVDHosting.Tokens.ThemeEmitterToken);
Expand All @@ -68,6 +67,8 @@ export class LoginComponent implements OnInit {
this.errorMessage = '';
this.expiredPassword = false;
this.passwordServices = new Set<string>();
this.renewSession = this.renewSession.bind(this);
this.isIdle = this.isIdle.bind(this);
this.authenticationService.loginScreenVisibilityChanged.subscribe((eventReason: MVDHosting.LoginScreenChangeReason) => {
switch (eventReason) {
case MVDHosting.LoginScreenChangeReason.UserLogout:
Expand All @@ -77,6 +78,7 @@ export class LoginComponent implements OnInit {
case MVDHosting.LoginScreenChangeReason.UserLogin:
this.errorMessage = '';
this.needLogin = false;
this.detectActivity();
break;
case MVDHosting.LoginScreenChangeReason.PasswordChange:
this.changePassword = true;
Expand All @@ -89,10 +91,7 @@ export class LoginComponent implements OnInit {
break;
case MVDHosting.LoginScreenChangeReason.SessionExpired:
this.backButton();
if (this.idleWarnModal) {
this.popupManager.removeReport(this.idleWarnModal.id);
this.idleWarnModal = undefined;
}
this.idleWarnService.removeErrorReport();
this.errorMessage = this.translation.translate('Session Expired');
this.needLogin = true;
break;
Expand All @@ -114,88 +113,24 @@ export class LoginComponent implements OnInit {
}

private isIdle(): boolean {
const lastActive = parseInt(window.localStorage.getItem('ZoweZLUX.lastActive') || '0');
let idle = (Date.now() - lastActive) > ACTIVITY_IDLE_TIMEOUT_MS;
this.logger.debug("ZWED5304I", lastActive, Date.now(), idle); //this.logger.debug(`User lastActive=${lastActive}, now=${Date.now()}, idle={idle}`);
const activityTime = parseInt(StorageService.getItem(StorageKey.LAST_ACTIVE) || '0');
let idle = (Date.now() - activityTime) > ACTIVITY_IDLE_TIMEOUT_MS;
this.logger.debug("ZWED5304I", activityTime, Date.now(), idle);
//this.logger.debug(`User lastActive=${lastActive}, now=${Date.now()}, idle={idle}`);
return idle;
}

renewSession(): void {
this.authenticationService.performSessionRenewal().subscribe((result:any)=> {
if (this.idleWarnModal) {
this.idleWarnModal.subject.unsubscribe();
this.idleWarnModal = undefined;
}
this.idleWarnService.removeErrorReport();
}, (errorObservable)=> {
if (this.idleWarnModal) {
this.idleWarnModal.subject.unsubscribe();
this.idleWarnModal = this.popupManager.createErrorReport(
ZluxErrorSeverity.WARNING,
this.translation.translate('Session Renewal Error'),
this.translation.translate('Session could not be renewed. Logout will occur unless renewed. Click here to retry.'),
{
blocking: false,
buttons: [this.translation.translate('Retry'), this.translation.translate('Dismiss')]
});
this.idleWarnModal.subject.subscribe((buttonName:any)=> {
if (buttonName == this.translation.translate('Retry')) {
this.renewSession();
}
});
}
this.idleWarnService.createRetryErrorReport(this.renewSession, this.isIdle);
});
}

spawnExpirationPrompt(expirationInMS: number): void {
let desktopSize = this.themeManager.mainSize || 2;
let popupStyle;

/* According to the size of the desktop, we move the expiration prompt to align with the app bar */
switch (desktopSize) {
case 3: {
popupStyle = {
'margin-bottom': '70px',
'margin-right': '-5px'
};
break;
}
case 1: {
popupStyle = {
'margin-bottom': '15px',
'margin-right': '-10px'
};
break;
}
default: {
popupStyle = {
'margin-bottom': '35px',
'margin-right': '-5px'
};
break;
}
}

this.idleWarnModal = this.popupManager.createErrorReport(
ZluxErrorSeverity.WARNING,
this.translation.translate('Session Expiring Soon'),
//TODO: Add translation
//this.translation.translate('You will be logged out at ', { expirationInMS: moment().add(expirationInMS/1000, 'seconds').format('LT') }),
this.translation.translate('You will be logged out at ' + moment().add(expirationInMS/1000, 'seconds').format('LT')),
{
blocking: false,
buttons: [this.translation.translate('Continue session')],
timestamp: false,
theme: "dark",
style: popupStyle,
callToAction: true
});

this.idleWarnModal.subject.subscribe((buttonName:any)=> {
if (buttonName == this.translation.translate('Continue')) {
//may fail, so don't touch timers yet
this.renewSession();
}
});
this.idleWarnService.createContinueErrorReport(this.renewSession, this.isIdle, expirationInMS, desktopSize)
}

ngOnInit(): void {
Expand Down Expand Up @@ -256,13 +191,8 @@ export class LoginComponent implements OnInit {
}

detectActivity(): void {
this.logger.debug('ZWED5305I'); //this.logger.debug('User activity detected');
this.lastActive = Date.now();
window.localStorage.setItem('ZoweZLUX.lastActive',this.lastActive.toString());
if (this.idleWarnModal) {
this.popupManager.removeReport(this.idleWarnModal.id);
this.idleWarnModal = undefined;
}
this.storageService.updateLastActive();
this.idleWarnService.removeErrorReport();
}

attemptPasswordReset(): void {
Expand Down
29 changes: 29 additions & 0 deletions virtual-desktop/src/app/authentication-manager/storage-enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@


/*
This program and the accompanying materials are
made available under the terms of the Eclipse Public License v2.0 which accompanies
this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zowe Project.
*/

export const enum StorageKey {
LAST_ACTIVE = 'ZoweZLUX.lastActive',
I18_NEXT_LANG = 'i18nextLng',
USERNAME = 'username'
};


/*
This program and the accompanying materials are
made available under the terms of the Eclipse Public License v2.0 which accompanies
this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zowe Project.
*/

Loading

0 comments on commit 4a6c02d

Please sign in to comment.