MOBILE-2253 login: Restore session in init, handle SSO and other events
This commit is contained in:
		
							parent
							
								
									7893d718a2
								
							
						
					
					
						commit
						26d5690c36
					
				| @ -12,24 +12,82 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component } from '@angular/core'; | ||||
| import { Platform } from 'ionic-angular'; | ||||
| import { Component, ViewChild, AfterViewInit } from '@angular/core'; | ||||
| import { Platform, Nav } from 'ionic-angular'; | ||||
| import { StatusBar } from '@ionic-native/status-bar'; | ||||
| import { SplashScreen } from '@ionic-native/splash-screen'; | ||||
| import { CoreEventsProvider } from '../providers/events'; | ||||
| import { CoreLoginHelperProvider } from '../core/login/providers/helper'; | ||||
| 
 | ||||
| @Component({ | ||||
|     templateUrl: 'app.html' | ||||
| }) | ||||
| export class MyApp { | ||||
| export class MyApp implements AfterViewInit { | ||||
|     @ViewChild(Nav) navCtrl; | ||||
|     rootPage:any = 'CoreLoginInitPage'; | ||||
| 
 | ||||
|     constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) { | ||||
|     constructor(private platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, | ||||
|             private eventsProvider: CoreEventsProvider, private loginHelper: CoreLoginHelperProvider) { | ||||
|         platform.ready().then(() => { | ||||
|             // Okay, so the platform is ready and our plugins are available.
 | ||||
|             // Here you can do any higher level native things you might need.
 | ||||
|             statusBar.styleDefault(); | ||||
|             splashScreen.hide(); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * View has been initialized. | ||||
|      */ | ||||
|     ngAfterViewInit() { | ||||
|         this.loginHelper.setNavCtrl(this.navCtrl); | ||||
| 
 | ||||
|         // Go to sites page when user is logged out.
 | ||||
|         this.eventsProvider.on(CoreEventsProvider.LOGOUT, () => { | ||||
|             this.navCtrl.setRoot('CoreLoginSitesPage'); | ||||
|         }); | ||||
| 
 | ||||
|         // Listen for session expired events.
 | ||||
|         this.eventsProvider.on(CoreEventsProvider.SESSION_EXPIRED, (data) => { | ||||
|             this.loginHelper.sessionExpired(data); | ||||
|         }); | ||||
| 
 | ||||
|         // Listen for passwordchange and usernotfullysetup events to open InAppBrowser.
 | ||||
|         this.eventsProvider.on(CoreEventsProvider.PASSWORD_CHANGE_FORCED, (data) => { | ||||
|             this.loginHelper.openInAppForEdit(data.siteId, '/login/change_password.php', 'core.forcepasswordchangenotice', true); | ||||
|         }); | ||||
|         this.eventsProvider.on(CoreEventsProvider.USER_NOT_FULLY_SETUP, (data) => { | ||||
|             this.loginHelper.openInAppForEdit(data.siteId, '/user/edit.php', 'core.usernotfullysetup'); | ||||
|         }); | ||||
| 
 | ||||
|         // Listen for sitepolicynotagreed event to accept the site policy.
 | ||||
|         this.eventsProvider.on(CoreEventsProvider.SITE_POLICY_NOT_AGREED, (data) => { | ||||
|             this.loginHelper.sitePolicyNotAgreed(data.siteId); | ||||
|         }); | ||||
| 
 | ||||
|         // Check URLs loaded in any InAppBrowser.
 | ||||
|         this.eventsProvider.on(CoreEventsProvider.IAB_LOAD_START, (event) => { | ||||
|             this.loginHelper.inAppBrowserLoadStart(event.url); | ||||
|         }); | ||||
| 
 | ||||
|         // Check InAppBrowser closed.
 | ||||
|         this.eventsProvider.on(CoreEventsProvider.IAB_EXIT, () => { | ||||
|             this.loginHelper.waitingForBrowser = false; | ||||
|             this.loginHelper.lastInAppUrl = ''; | ||||
|             this.loginHelper.checkLogout(); | ||||
|         }); | ||||
| 
 | ||||
|         this.platform.resume.subscribe(() => { | ||||
|             // Wait a second before setting it to false since in iOS there could be some frozen WS calls.
 | ||||
|             setTimeout(() => { | ||||
|                 this.loginHelper.waitingForBrowser = false; | ||||
|                 this.loginHelper.checkLogout(); | ||||
|             }, 1000); | ||||
|         }); | ||||
| 
 | ||||
|         // @todo: Register observer to check if the app was launched via URL scheme.
 | ||||
|         // $mmURLDelegate.register('mmLoginSSO', appLaunchedByURL);
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -53,6 +53,8 @@ import { CoreFilepoolProvider } from '../providers/filepool'; | ||||
| import { CoreUpdateManagerProvider } from '../providers/update-manager'; | ||||
| import { CorePluginFileDelegate } from '../providers/plugin-file-delegate'; | ||||
| 
 | ||||
| import { CoreLoginModule } from '../core/login/login.module'; | ||||
| 
 | ||||
| // For translate loader. AoT requires an exported function for factories.
 | ||||
| export function createTranslateLoader(http: HttpClient) { | ||||
|     return new TranslateHttpLoader(http, './assets/lang/', '.json'); | ||||
| @ -76,7 +78,8 @@ export function createTranslateLoader(http: HttpClient) { | ||||
|                 deps: [HttpClient] | ||||
|             } | ||||
|         }), | ||||
|         CoreEmulatorModule | ||||
|         CoreEmulatorModule, | ||||
|         CoreLoginModule | ||||
|     ], | ||||
|     bootstrap: [IonicApp], | ||||
|     entryComponents: [ | ||||
| @ -119,19 +122,27 @@ export function createTranslateLoader(http: HttpClient) { | ||||
|     ] | ||||
| }) | ||||
| export class AppModule { | ||||
|     constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider) { | ||||
|         // Create a handler for platform ready and register it in the init delegate.
 | ||||
|         let handler = { | ||||
|     constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider, | ||||
|             sitesProvider: CoreSitesProvider) { | ||||
|         // Register a handler for platform ready.
 | ||||
|         initDelegate.registerProcess({ | ||||
|             name: 'CorePlatformReady', | ||||
|             priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 400, | ||||
|             blocking: true, | ||||
|             load: platform.ready | ||||
|         }; | ||||
|         initDelegate.registerProcess(handler); | ||||
|         }); | ||||
| 
 | ||||
|         // Register the update manager as an init process.
 | ||||
|         initDelegate.registerProcess(updateManager); | ||||
| 
 | ||||
|         // Restore the user's session during the init process.
 | ||||
|         initDelegate.registerProcess({ | ||||
|             name: 'CoreRestoreSession', | ||||
|             priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 200, | ||||
|             blocking: false, | ||||
|             load: sitesProvider.restoreSession.bind(sitesProvider) | ||||
|         }); | ||||
| 
 | ||||
|         // Execute the init processes.
 | ||||
|         initDelegate.executeInitProcesses(); | ||||
|     } | ||||
|  | ||||
| @ -30,6 +30,7 @@ import { CoreUtilsProvider } from '../providers/utils/utils'; | ||||
| import { CoreConstants } from '../core/constants'; | ||||
| import { CoreConfigConstants } from '../configconstants'; | ||||
| import { Md5 } from 'ts-md5/dist/md5'; | ||||
| import { InAppBrowserObject } from '@ionic-native/in-app-browser'; | ||||
| 
 | ||||
| export interface CoreSiteWSPreSets { | ||||
|     getFromCache?: boolean; // Get the value from the cache if it's still valid.
 | ||||
| @ -1017,9 +1018,9 @@ export class CoreSite { | ||||
|      * @param {string} url The URL to open. | ||||
|      * @param {any} [options] Override default options passed to InAppBrowser. | ||||
|      * @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser. | ||||
|      * @return {Promise<any>} Promise resolved when done, rejected otherwise. | ||||
|      * @return {Promise<InAppBrowserObject>} Promise resolved when done. | ||||
|      */ | ||||
|     openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string) : Promise<any> { | ||||
|     openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { | ||||
|         return this.openWithAutoLogin(true, url, options, alertMessage); | ||||
|     } | ||||
| 
 | ||||
| @ -1029,9 +1030,9 @@ export class CoreSite { | ||||
|      * @param {string} url The URL to open. | ||||
|      * @param {object} [options] Override default options passed to inappbrowser. | ||||
|      * @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser. | ||||
|      * @return {Promise<any>} Promise resolved when done, rejected otherwise. | ||||
|      * @return {Promise<InAppBrowserObject>} Promise resolved when done. | ||||
|      */ | ||||
|     openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string) : Promise<any> { | ||||
|     openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { | ||||
|         return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage); | ||||
|     } | ||||
| 
 | ||||
| @ -1042,11 +1043,12 @@ export class CoreSite { | ||||
|      * @param {string} url The URL to open. | ||||
|      * @param {object} [options] Override default options passed to $cordovaInAppBrowser#open. | ||||
|      * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser. | ||||
|      * @return {Promise<any>}               Promise resolved when done, rejected otherwise. | ||||
|      * @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true. | ||||
|      */ | ||||
|     openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<any> { | ||||
|     openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { | ||||
|         // Convenience function to open the URL.
 | ||||
|         let open = (url) => { | ||||
|             return new Promise<InAppBrowserObject>((resolve, reject) => { | ||||
|                 if (modal) { | ||||
|                     modal.dismiss(); | ||||
|                 } | ||||
| @ -1055,26 +1057,26 @@ export class CoreSite { | ||||
|                     let alert = this.domUtils.showAlert('mm.core.notice', alertMessage, null, 3000); | ||||
|                     alert.onDidDismiss(() => { | ||||
|                         if (inApp) { | ||||
|                         this.utils.openInApp(url, options); | ||||
|                             resolve(this.utils.openInApp(url, options)); | ||||
|                         } else { | ||||
|                         this.utils.openInBrowser(url); | ||||
|                             resolve(this.utils.openInBrowser(url)); | ||||
|                         } | ||||
|                     }); | ||||
|                 } else { | ||||
|                     if (inApp) { | ||||
|                     this.utils.openInApp(url, options); | ||||
|                         resolve(this.utils.openInApp(url, options)); | ||||
|                     } else { | ||||
|                     this.utils.openInBrowser(url); | ||||
|                         resolve(this.utils.openInBrowser(url)); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         }; | ||||
| 
 | ||||
|         if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') || | ||||
|                 (this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < 6 * CoreConstants.secondsMinute)) { | ||||
|             // No private token, WS not available or last auto-login was less than 6 minutes ago.
 | ||||
|             // Open the final URL without auto-login.
 | ||||
|             open(url); | ||||
|             return Promise.resolve(); | ||||
|             return Promise.resolve(open(url)); | ||||
|         } | ||||
| 
 | ||||
|         const userId = this.getUserId(), | ||||
| @ -1087,16 +1089,15 @@ export class CoreSite { | ||||
|         return this.write('tool_mobile_get_autologin_key', params).then((data) => { | ||||
|             if (!data.autologinurl || !data.key) { | ||||
|                 // Not valid data, open the final URL without auto-login.
 | ||||
|                 open(url); | ||||
|                 return; | ||||
|                 return open(url); | ||||
|             } | ||||
| 
 | ||||
|             this.lastAutoLogin = this.timeUtils.timestamp(); | ||||
| 
 | ||||
|             open(data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + url); | ||||
|             return open(data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + url); | ||||
|         }).catch(() => { | ||||
|             // Couldn't get autologin key, open the final URL without auto-login.
 | ||||
|             open(url); | ||||
|             return open(url); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -1107,9 +1108,9 @@ export class CoreSite { | ||||
|      * @param {string} url The URL to open. | ||||
|      * @param {object} [options] Override default options passed to inappbrowser. | ||||
|      * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser. | ||||
|      * @return {Promise<any>} Promise resolved when done, rejected otherwise. | ||||
|      * @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true. | ||||
|      */ | ||||
|     openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<any> { | ||||
|     openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { | ||||
|         if (this.containsUrl(url)) { | ||||
|             return this.openWithAutoLogin(inApp, url, options, alertMessage); | ||||
|         } else { | ||||
| @ -1118,7 +1119,7 @@ export class CoreSite { | ||||
|             } else { | ||||
|                 this.utils.openInBrowser(url); | ||||
|             } | ||||
|             return Promise.resolve(); | ||||
|             return Promise.resolve(null); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -13,11 +13,12 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { NavController } from 'ionic-angular'; | ||||
| import { NavController, Platform } from 'ionic-angular'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreAppProvider } from '../../../providers/app'; | ||||
| import { CoreConfigProvider } from '../../../providers/config'; | ||||
| import { CoreEventsProvider } from '../../../providers/events'; | ||||
| import { CoreInitDelegate } from '../../../providers/init'; | ||||
| import { CoreLoggerProvider } from '../../../providers/logger'; | ||||
| import { CoreSitesProvider } from '../../../providers/sites'; | ||||
| import { CoreWSProvider } from '../../../providers/ws'; | ||||
| @ -44,12 +45,17 @@ export interface CoreLoginSSOData { | ||||
| @Injectable() | ||||
| export class CoreLoginHelperProvider { | ||||
|     protected logger; | ||||
|     protected isSSOConfirmShown = false; | ||||
|     protected isOpenEditAlertShown = false; | ||||
|     protected navCtrl: NavController; | ||||
|     lastInAppUrl: string; | ||||
|     waitingForBrowser = false; | ||||
| 
 | ||||
|     constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, | ||||
|             private wsProvider: CoreWSProvider, private translate: TranslateService, private textUtils: CoreTextUtilsProvider, | ||||
|             private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, | ||||
|             private urlUtils: CoreUrlUtilsProvider,private configProvider: CoreConfigProvider, | ||||
|             private emulatorHelper: CoreEmulatorHelperProvider) { | ||||
|             private urlUtils: CoreUrlUtilsProvider,private configProvider: CoreConfigProvider, private platform: Platform, | ||||
|             private emulatorHelper: CoreEmulatorHelperProvider, private initDelegate: CoreInitDelegate) { | ||||
|         this.logger = logger.getInstance('CoreLoginHelper'); | ||||
|     } | ||||
| 
 | ||||
| @ -84,6 +90,75 @@ export class CoreLoginHelperProvider { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function to handle URL received by Custom URL Scheme. If it's a SSO login, perform authentication. | ||||
|      * | ||||
|      * @param {string} url URL received. | ||||
|      * @return {boolean} True if it's a SSO URL, false otherwise. | ||||
|      */ | ||||
|     appLaunchedByURL(url: string) : boolean { | ||||
|         let ssoScheme = CoreConfigConstants.customurlscheme + '://token='; | ||||
|         if (url.indexOf(ssoScheme) == -1) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (this.appProvider.isSSOAuthenticationOngoing()) { | ||||
|             // Authentication ongoing, probably duplicated request.
 | ||||
|             return true; | ||||
|         } | ||||
|         if (this.appProvider.isDesktop()) { | ||||
|             // In desktop, make sure InAppBrowser is closed.
 | ||||
|             this.utils.closeInAppBrowser(true); | ||||
|         } | ||||
| 
 | ||||
|         // App opened using custom URL scheme. Probably an SSO authentication.
 | ||||
|         this.appProvider.startSSOAuthentication(); | ||||
|         this.logger.debug('App launched by URL'); | ||||
| 
 | ||||
|         // Delete the sso scheme from the URL.
 | ||||
|         url = url.replace(ssoScheme, ''); | ||||
| 
 | ||||
|         // Some platforms like Windows add a slash at the end. Remove it.
 | ||||
|         // Some sites add a # at the end of the URL. If it's there, remove it.
 | ||||
|         url = url.replace(/\/?#?\/?$/, ''); | ||||
| 
 | ||||
|         // Decode from base64.
 | ||||
|         try { | ||||
|             url = atob(url); | ||||
|         } catch(err) { | ||||
|             // Error decoding the parameter.
 | ||||
|             this.logger.error('Error decoding parameter received for login SSO'); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         let modal = this.domUtils.showModalLoading('mm.login.authenticating', true), | ||||
|             siteData: CoreLoginSSOData; | ||||
| 
 | ||||
|         // Wait for app to be ready.
 | ||||
|         this.initDelegate.ready().then(() => { | ||||
|             return this.validateBrowserSSOLogin(url); | ||||
|         }).then((data) => { | ||||
|             siteData = data; | ||||
|             return this.handleSSOLoginAuthentication(siteData.siteUrl, siteData.token, siteData.privateToken); | ||||
|         }).then(() => { | ||||
|             if (siteData.pageName) { | ||||
|                 // State defined, go to that state instead of site initial page.
 | ||||
|                 this.navCtrl.push(siteData.pageName, siteData.pageParams); | ||||
|             } else { | ||||
|                 this.goToSiteInitialPage(this.navCtrl, true); | ||||
|             } | ||||
|         }).catch((errorMessage) => { | ||||
|             if (typeof errorMessage == 'string' && errorMessage != '') { | ||||
|                 this.domUtils.showErrorModal(errorMessage); | ||||
|             } | ||||
|         }).finally(() => { | ||||
|             modal.dismiss(); | ||||
|             this.appProvider.finishSSOAuthentication(); | ||||
|         }); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if a site allows requesting a password reset through the app. | ||||
|      * | ||||
| @ -98,6 +173,17 @@ export class CoreLoginHelperProvider { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function called when an SSO InAppBrowser is closed or the app is resumed. Check if user needs to be logged out. | ||||
|      */ | ||||
|     checkLogout() { | ||||
|         if (!this.appProvider.isSSOAuthenticationOngoing() && this.sitesProvider.isLoggedIn() && | ||||
|                 this.sitesProvider.getCurrentSite().isLoggedOut() && this.navCtrl.getActive().name == 'CoreLoginReconnectPage') { | ||||
|             // User must reauthenticate but he closed the InAppBrowser without doing so, logout him.
 | ||||
|             this.sitesProvider.logout(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show a confirm modal if needed and open a browser to perform SSO login. | ||||
|      * | ||||
| @ -374,6 +460,39 @@ export class CoreLoginHelperProvider { | ||||
|                 CoreConfigConstants.siteurl.length > 1; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function called when a page starts loading in any InAppBrowser window. | ||||
|      * | ||||
|      * @param {string} url Loaded url. | ||||
|      */ | ||||
|     inAppBrowserLoadStart(url: string) : void { | ||||
|         // URLs with a custom scheme can be prefixed with "http://" or "https://", we need to remove this.
 | ||||
|         url = url.replace(/^https?:\/\//, ''); | ||||
| 
 | ||||
|         if (this.appLaunchedByURL(url)) { | ||||
|             // Close the browser if it's a valid SSO URL.
 | ||||
|             this.utils.closeInAppBrowser(false); | ||||
|         } else if (this.platform.is('android')) { | ||||
|             // Check if the URL has a custom URL scheme. In Android they need to be opened manually.
 | ||||
|             const urlScheme = this.urlUtils.getUrlProtocol(url); | ||||
|             if (urlScheme && urlScheme !== 'file' && urlScheme !== 'cdvfile') { | ||||
|                 // Open in browser should launch the right app if found and do nothing if not found.
 | ||||
|                 this.utils.openInBrowser(url); | ||||
| 
 | ||||
|                 // At this point the InAppBrowser is showing a "Webpage not available" error message.
 | ||||
|                 // Try to navigate to last loaded URL so this error message isn't found.
 | ||||
|                 if (this.lastInAppUrl) { | ||||
|                     this.utils.openInApp(this.lastInAppUrl); | ||||
|                 } else { | ||||
|                     // No last URL loaded, close the InAppBrowser.
 | ||||
|                     this.utils.closeInAppBrowser(false); | ||||
|                 } | ||||
|             } else { | ||||
|                 this.lastInAppUrl = url; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Given a site public config, check if email signup is disabled. | ||||
|      * | ||||
| @ -580,6 +699,43 @@ export class CoreLoginHelperProvider { | ||||
|         this.utils.openInApp(siteUrl + '/login/forgot_password.php'); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Function to open in app browser to change password or complete user profile. | ||||
|      * | ||||
|      * @param {string} siteId The site ID. | ||||
|      * @param {string} path The relative path of the URL to open. | ||||
|      * @param {string} alertMessage The key of the message to display before opening the in app browser. | ||||
|      * @param {boolean} [invalidateCache] Whether to invalidate site's cache (e.g. when the user is forced to change password). | ||||
|      */ | ||||
|     openInAppForEdit(siteId: string, path: string, alertMessage: string, invalidateCache?: boolean) : void { | ||||
|         if (!siteId || siteId !== this.sitesProvider.getCurrentSiteId()) { | ||||
|             // Site that triggered the event is not current site, nothing to do.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         let currentSite = this.sitesProvider.getCurrentSite(), | ||||
|             siteUrl = currentSite && currentSite.getURL(); | ||||
|         if (!currentSite || !siteUrl) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!this.isOpenEditAlertShown && !this.waitingForBrowser) { | ||||
|             this.isOpenEditAlertShown = true; | ||||
| 
 | ||||
|             if (invalidateCache) { | ||||
|                 currentSite.invalidateWsCache(); | ||||
|             } | ||||
| 
 | ||||
|             // Open change password.
 | ||||
|             alertMessage = this.translate.instant(alertMessage) + '<br>' + this.translate.instant('mm.core.redirectingtosite'); | ||||
|             currentSite.openInAppWithAutoLogin(siteUrl + path, undefined, alertMessage).then(() => { | ||||
|                 this.waitingForBrowser = true; | ||||
|             }).finally(() => { | ||||
|                 this.isOpenEditAlertShown = false; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Prepare the app to perform SSO login. | ||||
|      * | ||||
| @ -633,6 +789,88 @@ export class CoreLoginHelperProvider { | ||||
|         return this.wsProvider.callAjax('core_auth_request_password_reset', params, {siteUrl: siteUrl}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function that should be called when the session expires. Reserved for core use. | ||||
|      * | ||||
|      * @param {any} data Data received by the SESSION_EXPIRED event. | ||||
|      */ | ||||
|     sessionExpired(data: any) : void { | ||||
|         let siteId = data && data.siteId, | ||||
|             currentSite = this.sitesProvider.getCurrentSite(), | ||||
|             siteUrl = currentSite && currentSite.getURL(), | ||||
|             promise; | ||||
| 
 | ||||
|         if (!currentSite || !siteUrl) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (siteId && siteId !== currentSite.getId()) { | ||||
|             return; // Site that triggered the event is not current site.
 | ||||
|         } | ||||
| 
 | ||||
|         // Check authentication method.
 | ||||
|         this.sitesProvider.checkSite(siteUrl).then((result) => { | ||||
| 
 | ||||
|             if (result.warning) { | ||||
|                 this.domUtils.showErrorModal(result.warning, true, 4000); | ||||
|             } | ||||
| 
 | ||||
|             if (this.isSSOLoginNeeded(result.code)) { | ||||
|                 // SSO. User needs to authenticate in a browser. Prevent showing the message several times
 | ||||
|                 // or show it again if the user is already authenticating using SSO.
 | ||||
|                 if (!this.appProvider.isSSOAuthenticationOngoing() && !this.isSSOConfirmShown && !this.waitingForBrowser) { | ||||
|                     this.isSSOConfirmShown = true; | ||||
| 
 | ||||
|                     if (this.shouldShowSSOConfirm(result.code)) { | ||||
|                         promise = this.domUtils.showConfirm(this.translate.instant('mm.login.' + | ||||
|                                 (currentSite.isLoggedOut() ? 'loggedoutssodescription' : 'reconnectssodescription'))); | ||||
|                     } else { | ||||
|                         promise = Promise.resolve(); | ||||
|                     } | ||||
| 
 | ||||
|                     promise.then(() => { | ||||
|                         this.waitingForBrowser = true; | ||||
|                         this.openBrowserForSSOLogin(result.siteUrl, result.code, result.service, | ||||
|                                 result.config && result.config.launchurl, data.pageName, data.params); | ||||
|                     }).catch(() => { | ||||
|                         // User cancelled, logout him.
 | ||||
|                         this.sitesProvider.logout(); | ||||
|                     }).finally(() => { | ||||
|                         this.isSSOConfirmShown = false; | ||||
|                     }); | ||||
|                 } | ||||
|             } else { | ||||
|                 let info = currentSite.getInfo(); | ||||
|                 if (typeof info != 'undefined' && typeof info.username != 'undefined') { | ||||
|                     this.navCtrl.setRoot('CoreLoginReconnectPage', { | ||||
|                         infoSiteUrl: info.siteurl, | ||||
|                         siteUrl: result.siteUrl, | ||||
|                         siteId: siteId, | ||||
|                         pageName: data.pageName, | ||||
|                         pageParams: data.params, | ||||
|                         siteConfig: result.config | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         }).catch((error) => { | ||||
|             // Error checking site.
 | ||||
|             if (currentSite.isLoggedOut()) { | ||||
|                 // Site is logged out, show error and logout the user.
 | ||||
|                 this.domUtils.showErrorModalDefault(error, 'mm.core.networkerrormsg', true); | ||||
|                 this.sitesProvider.logout(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set a NavController to use. | ||||
|      * | ||||
|      * @param {NavController} navCtrl Nav controller. | ||||
|      */ | ||||
|     setNavCtrl(navCtrl: NavController) : void { | ||||
|         this.navCtrl = navCtrl; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if a confirm should be shown to open a SSO authentication. | ||||
|      * | ||||
| @ -644,6 +882,26 @@ export class CoreLoginHelperProvider { | ||||
|                     (!CoreConfigConstants.skipssoconfirmation || String(CoreConfigConstants.skipssoconfirmation) === 'false'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function called when site policy is not agreed. Reserved for core use. | ||||
|      * | ||||
|      * @param {string} [siteId] Site ID. If not defined, current site. | ||||
|      */ | ||||
|     sitePolicyNotAgreed(siteId?: string) : void { | ||||
|         siteId = siteId || this.sitesProvider.getCurrentSiteId(); | ||||
|         if (!siteId || siteId != this.sitesProvider.getCurrentSiteId()) { | ||||
|             // Only current site allowed.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!this.sitesProvider.getCurrentSite().wsAvailable('core_user_agree_site_policy')) { | ||||
|             // WS not available, stop.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.navCtrl.setRoot('CoreLoginSitePolicyPage', {siteId: siteId}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Convenient helper to handle get User Token error. It redirects to change password page if forcepassword is set. | ||||
|      * | ||||
|  | ||||
| @ -43,6 +43,8 @@ export class CoreEventsProvider { | ||||
|     public static REMOTE_ADDONS_LOADED = 'remote_addons_loaded'; | ||||
|     public static LOGIN_SITE_CHECKED = 'login_site_checked'; | ||||
|     public static LOGIN_SITE_UNCHECKED = 'login_site_unchecked'; | ||||
|     public static IAB_LOAD_START = 'inappbrowser_load_start'; | ||||
|     public static IAB_EXIT = 'inappbrowser_exit'; | ||||
| 
 | ||||
|     logger; | ||||
|     observables = {}; | ||||
|  | ||||
| @ -19,6 +19,7 @@ import { InAppBrowser, InAppBrowserObject } from '@ionic-native/in-app-browser'; | ||||
| import { Clipboard } from '@ionic-native/clipboard'; | ||||
| import { CoreAppProvider } from '../app'; | ||||
| import { CoreDomUtilsProvider } from './dom'; | ||||
| import { CoreEventsProvider } from '../events'; | ||||
| import { CoreLoggerProvider } from '../logger'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreLangProvider } from '../lang'; | ||||
| @ -39,7 +40,7 @@ export class CoreUtilsProvider { | ||||
| 
 | ||||
|     constructor(private iab: InAppBrowser, private appProvider: CoreAppProvider, private clipboard: Clipboard, | ||||
|             private domUtils: CoreDomUtilsProvider, logger: CoreLoggerProvider, private translate: TranslateService, | ||||
|             private platform: Platform, private langProvider: CoreLangProvider) { | ||||
|             private platform: Platform, private langProvider: CoreLangProvider, private eventsProvider: CoreEventsProvider) { | ||||
|         this.logger = logger.getInstance('CoreUtilsProvider'); | ||||
|     } | ||||
| 
 | ||||
| @ -249,7 +250,7 @@ export class CoreUtilsProvider { | ||||
|      * | ||||
|      * @param {boolean} [closeAll] Desktop only. True to close all secondary windows, false to close only the "current" one. | ||||
|      */ | ||||
|     closeInAppBrowser(closeAll: boolean) : void { | ||||
|     closeInAppBrowser(closeAll?: boolean) : void { | ||||
|         if (this.iabInstance) { | ||||
|             this.iabInstance.close(); | ||||
|             if (closeAll && this.appProvider.isDesktop()) { | ||||
| @ -790,6 +791,17 @@ export class CoreUtilsProvider { | ||||
|         } | ||||
| 
 | ||||
|         this.iabInstance = this.iab.create(url, '_blank', options); | ||||
| 
 | ||||
|         // Trigger global events when a url is loaded or the window is closed. This is to make it work like in Ionic 1.
 | ||||
|         let loadStartSubscription = this.iabInstance.on('loadstart').subscribe((event) => { | ||||
|             this.eventsProvider.trigger(CoreEventsProvider.IAB_LOAD_START, event); | ||||
|         }); | ||||
|         let exitSubscription = this.iabInstance.on('exit').subscribe((event) => { | ||||
|             loadStartSubscription.unsubscribe(); | ||||
|             exitSubscription.unsubscribe(); | ||||
|             this.eventsProvider.trigger(CoreEventsProvider.IAB_EXIT, event); | ||||
|         }); | ||||
| 
 | ||||
|         return this.iabInstance; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user