MOBILE-4405 autologout: Create autologout service
parent
4993f9a71b
commit
56caf062d8
|
@ -0,0 +1,274 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreStorage } from '@services/storage';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Auto logout service
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreAutoLogoutService {
|
||||
|
||||
/**
|
||||
* Timestamp indicating the last time the application was in the foreground.
|
||||
*/
|
||||
protected static readonly TIMESTAMP_DB_KEY = 'CoreAutoLogoutTimestamp';
|
||||
|
||||
/**
|
||||
* How often we will store a timestamp (in miliseconds).
|
||||
*/
|
||||
protected static readonly DEFAULT_TIMESTAMP_STORE_TIME = 10000;
|
||||
|
||||
/**
|
||||
* Grace period if you return to the application too soon (in miliseconds).
|
||||
*/
|
||||
protected static readonly GRACE_PERIOD = 30000;
|
||||
|
||||
protected platformResumeSubscription?: Subscription;
|
||||
protected platformPauseSubscription?: Subscription;
|
||||
protected interval?: ReturnType<typeof setInterval>;
|
||||
protected backgroundTimestamp?: number;
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
initialize(): void {
|
||||
CoreEvents.on(CoreEvents.LOGIN, async() => await this.refreshListeners());
|
||||
CoreEvents.on(CoreEvents.LOGOUT, async() => {
|
||||
this.cancelListeners();
|
||||
const storage = CoreStorage.forCurrentSite();
|
||||
await storage.remove(CoreAutoLogoutService.TIMESTAMP_DB_KEY);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh listeners for auto logout.
|
||||
*/
|
||||
async refreshListeners(): Promise<void> {
|
||||
if (!CoreSites.isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const site = CoreSites.getCurrentSite();
|
||||
|
||||
if (!site) {
|
||||
return;
|
||||
}
|
||||
|
||||
const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
|
||||
this.cancelListeners();
|
||||
|
||||
if (!autoLogoutType || autoLogoutType === CoreAutoLogoutType.NEVER) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoLogoutType === CoreAutoLogoutType.CUSTOM) {
|
||||
await this.setTimestamp();
|
||||
this.setInterval();
|
||||
}
|
||||
|
||||
this.platformPauseSubscription = CorePlatform.pause.subscribe(async () => {
|
||||
this.backgroundTimestamp = new Date().getTime();
|
||||
this.clearInterval();
|
||||
});
|
||||
|
||||
this.platformResumeSubscription = CorePlatform.resume.subscribe(async () => {
|
||||
if (autoLogoutType !== CoreAutoLogoutType.CUSTOM) {
|
||||
await this.handleAppClosed(site);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const autoLogoutTime = Number(site.getStoredConfig('tool_mobile_autologouttime'));
|
||||
const loggedOut = await this.handleSessionClosed(autoLogoutTime, site);
|
||||
|
||||
if (!loggedOut) {
|
||||
await this.setTimestamp();
|
||||
this.setInterval();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set site logged out.
|
||||
*
|
||||
* @param siteId site id.
|
||||
*/
|
||||
protected async logout(siteId: string): Promise<void> {
|
||||
await CoreSites.setSiteLoggedOut(siteId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves stored timestamp.
|
||||
*/
|
||||
protected async setTimestamp(): Promise<void> {
|
||||
const date = new Date().getTime();
|
||||
const storage = CoreStorage.forCurrentSite();
|
||||
await storage.set(CoreAutoLogoutService.TIMESTAMP_DB_KEY, date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives if auto logout can be displayed.
|
||||
*
|
||||
* @returns true if can display, false if not.
|
||||
*/
|
||||
async canShowPreference(): Promise<boolean> {
|
||||
const site = CoreSites.getCurrentSite();
|
||||
|
||||
if (!site) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
|
||||
|
||||
return autoLogoutType !== CoreAutoLogoutType.NEVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel uncompleted listeners.
|
||||
*/
|
||||
protected cancelListeners(): void {
|
||||
this.clearInterval();
|
||||
this.platformResumeSubscription?.unsubscribe();
|
||||
this.platformPauseSubscription?.unsubscribe();
|
||||
delete this.platformPauseSubscription;
|
||||
delete this.platformResumeSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set interval.
|
||||
*/
|
||||
protected setInterval(): void {
|
||||
this.interval = setInterval(async () => await this.setTimestamp(), CoreAutoLogoutService.DEFAULT_TIMESTAMP_STORE_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear interval.
|
||||
*/
|
||||
protected clearInterval(): void {
|
||||
if (!this.interval) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(this.interval);
|
||||
delete this.interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout user if his session is expired.
|
||||
*
|
||||
* @param sessionDuration Session duration.
|
||||
* @param site Current site.
|
||||
* @returns Whether site has been logged out.
|
||||
*/
|
||||
async handleSessionClosed(sessionDuration: number, site: CoreSite): Promise<boolean> {
|
||||
if (!site.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const storage = CoreStorage.forSite(site);
|
||||
const savedTimestamp = await storage.get<number>(CoreAutoLogoutService.TIMESTAMP_DB_KEY);
|
||||
|
||||
if (!savedTimestamp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get expiration time from site preferences as miliseconds.
|
||||
const expirationDate = savedTimestamp + ((sessionDuration || 0) * 1000);
|
||||
await storage.remove(CoreAutoLogoutService.TIMESTAMP_DB_KEY);
|
||||
|
||||
if (new Date().getTime() < expirationDate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.logout(site.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout if user closed the app.
|
||||
*
|
||||
* @param site Current site.
|
||||
* @returns Whether site has been logged out.
|
||||
*/
|
||||
async handleAppClosed(site: CoreSite): Promise<boolean> {
|
||||
if (!site.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.backgroundTimestamp &&
|
||||
(this.backgroundTimestamp + CoreAutoLogoutService.GRACE_PERIOD) > new Date().getTime()
|
||||
) {
|
||||
delete this.backgroundTimestamp;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.logout(site.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getConfig(): { autoLogoutType: CoreAutoLogoutType; autoLogoutTime: number } {
|
||||
const site = CoreSites.getRequiredCurrentSite();
|
||||
const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
|
||||
const autoLogoutTime = Number(site.getStoredConfig('tool_mobile_autologouttime'));
|
||||
|
||||
return { autoLogoutType, autoLogoutTime };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type CoreAutoLogoutSessionConfig = {
|
||||
type: CoreAutoLogoutType.CUSTOM;
|
||||
sessionDuration: number;
|
||||
};
|
||||
|
||||
export type CoreAutoLogoutOtherConfig = {
|
||||
type: Exclude<CoreAutoLogoutType, CoreAutoLogoutType.CUSTOM>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Possible automatic logout cases.
|
||||
*/
|
||||
export enum CoreAutoLogoutType {
|
||||
/**
|
||||
* Disabled automatic logout.
|
||||
*/
|
||||
NEVER = 0,
|
||||
|
||||
/**
|
||||
* When the user closes the app, in next login he need to login again.
|
||||
*/
|
||||
INMEDIATE = 1,
|
||||
|
||||
/**
|
||||
* This applies when session time is set. If the user closes the app more time than the specified,
|
||||
* then, the user must login again.
|
||||
*/
|
||||
CUSTOM = 2,
|
||||
};
|
||||
|
||||
export type CoreAutoLogoutConfig = CoreAutoLogoutSessionConfig | CoreAutoLogoutOtherConfig;
|
||||
|
||||
export const CoreAutoLogout = makeSingleton(CoreAutoLogoutService);
|
|
@ -158,6 +158,7 @@ import { ADDON_PRIVATEFILES_SERVICES } from '@addons/privatefiles/privatefiles.m
|
|||
import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreAutoLogoutService } from '@features/autologout/services/autologout';
|
||||
|
||||
/**
|
||||
* Service to provide functionalities regarding compiling dynamic HTML and Javascript.
|
||||
|
@ -268,6 +269,7 @@ export class CoreCompileProvider {
|
|||
injectLibraries(instance: any, extraProviders: Type<unknown>[] = []): void {
|
||||
const providers = [
|
||||
...CORE_SERVICES,
|
||||
CoreAutoLogoutService,
|
||||
...CORE_BLOCK_SERVICES,
|
||||
...CORE_COMMENTS_SERVICES,
|
||||
...CORE_CONTENTLINKS_SERVICES,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreAutoLogout } from '@features/autologout/services/autologout';
|
||||
import { CoreConfig } from '@services/config';
|
||||
import { CoreFilepool } from '@services/filepool';
|
||||
import { CoreLang } from '@services/lang';
|
||||
|
@ -31,5 +32,6 @@ export default async function(): Promise<void> {
|
|||
CoreNetwork.initialize(),
|
||||
CoreUpdateManager.initialize(),
|
||||
CoreTimeUtils.initialize(),
|
||||
CoreAutoLogout.initialize(),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest
|
|||
import { CoreLang, CoreLangFormat } from '@services/lang';
|
||||
import { CoreNative } from '@features/native/services/native';
|
||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
|
||||
import { CoreAutoLogoutType, CoreAutoLogout } from '@features/autologout/services/autologout';
|
||||
|
||||
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
|
||||
export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
|
||||
|
@ -1461,6 +1462,8 @@ export class CoreSitesProvider {
|
|||
* @returns Promise resolved if a session is restored.
|
||||
*/
|
||||
async restoreSession(): Promise<void> {
|
||||
await this.handleAutoLogout();
|
||||
|
||||
if (this.sessionRestored) {
|
||||
return Promise.reject(new CoreError('Session already restored.'));
|
||||
}
|
||||
|
@ -1477,6 +1480,30 @@ export class CoreSitesProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle auto logout by checking autologout type and time if its required.
|
||||
*/
|
||||
async handleAutoLogout(): Promise<void> {
|
||||
await CoreUtils.ignoreErrors(( async () => {
|
||||
const siteId = await this.getStoredCurrentSiteId();
|
||||
const site = await this.getSite(siteId);
|
||||
const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
|
||||
const autoLogoutTime = Number(site.getStoredConfig('tool_mobile_autologouttime'));
|
||||
|
||||
if (autoLogoutType === CoreAutoLogoutType.NEVER || !site.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoLogoutType === CoreAutoLogoutType.CUSTOM) {
|
||||
await CoreAutoLogout.handleSessionClosed(autoLogoutTime, site);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreAutoLogout.handleAppClosed(site);
|
||||
})());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a site as logged out so the user needs to authenticate again.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue