MOBILE-2995 login: Go to credentials if scan authentication QR fails
parent
f9df95c727
commit
2b57c9485d
|
@ -22,7 +22,7 @@ import { CoreLoggerProvider } from '@providers/logger';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
|
||||
import { CoreCustomURLSchemesProvider, CoreCustomURLSchemesHandleError } from '@providers/urlschemes';
|
||||
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
||||
import { Keyboard } from '@ionic-native/keyboard';
|
||||
import { ScreenOrientation } from '@ionic-native/screen-orientation';
|
||||
|
@ -140,7 +140,9 @@ export class MoodleMobileApp implements OnInit {
|
|||
|
||||
if (this.urlSchemesProvider.isCustomURL(url)) {
|
||||
// Close the browser if it's a valid SSO URL.
|
||||
this.urlSchemesProvider.handleCustomURL(url);
|
||||
this.urlSchemesProvider.handleCustomURL(url).catch((error: CoreCustomURLSchemesHandleError) => {
|
||||
this.urlSchemesProvider.treatHandleCustomURLError(error);
|
||||
});
|
||||
this.utils.closeInAppBrowser(false);
|
||||
|
||||
} else if (this.platform.is('android')) {
|
||||
|
@ -194,7 +196,9 @@ export class MoodleMobileApp implements OnInit {
|
|||
this.lastUrls[url] = Date.now();
|
||||
|
||||
this.eventsProvider.trigger(CoreEventsProvider.APP_LAUNCHED_URL, url);
|
||||
this.urlSchemesProvider.handleCustomURL(url);
|
||||
this.urlSchemesProvider.handleCustomURL(url).catch((error: CoreCustomURLSchemesHandleError) => {
|
||||
this.urlSchemesProvider.treatHandleCustomURLError(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1789,6 +1789,7 @@
|
|||
"core.login.usernotaddederror": "User not added - error",
|
||||
"core.login.visitchangepassword": "Do you want to visit the site to change the password?",
|
||||
"core.login.webservicesnotenabled": "Your host site may not have enabled Web services. Please contact your administrator for help.",
|
||||
"core.login.youcanstillconnectwithcredentials": "You can still connect to the site by entering your username and password.",
|
||||
"core.login.yourenteredsite": "Connect to your site",
|
||||
"core.lostconnection": "Your authentication token is invalid or has expired. You will have to reconnect to the site.",
|
||||
"core.mainmenu.changesite": "Change site",
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
"usernamerequired": "Username required",
|
||||
"usernotaddederror": "User not added - error",
|
||||
"visitchangepassword": "Do you want to visit the site to change the password?",
|
||||
"yourenteredsite": "Connect to your site",
|
||||
"webservicesnotenabled": "Your host site may not have enabled Web services. Please contact your administrator for help."
|
||||
"webservicesnotenabled": "Your host site may not have enabled Web services. Please contact your administrator for help.",
|
||||
"youcanstillconnectwithcredentials": "You can still connect to the site by entering your username and password.",
|
||||
"yourenteredsite": "Connect to your site"
|
||||
}
|
|
@ -17,10 +17,11 @@ import { IonicPage, NavController, ModalController, AlertController, NavParams }
|
|||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider, CoreSiteCheckResponse, CoreLoginSiteInfo } from '@providers/sites';
|
||||
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
|
||||
import { CoreCustomURLSchemesProvider, CoreCustomURLSchemesHandleError } from '@providers/urlschemes';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreConfigConstants } from '../../../../configconstants';
|
||||
import { CoreLoginHelperProvider } from '../../providers/helper';
|
||||
import { FormBuilder, FormGroup, ValidatorFn, AbstractControl } from '@angular/forms';
|
||||
|
@ -74,7 +75,8 @@ export class CoreLoginSitePage {
|
|||
protected eventsProvider: CoreEventsProvider,
|
||||
protected translate: TranslateService,
|
||||
protected utils: CoreUtilsProvider,
|
||||
private urlSchemesProvider: CoreCustomURLSchemesProvider) {
|
||||
protected urlSchemesProvider: CoreCustomURLSchemesProvider,
|
||||
protected textUtils: CoreTextUtilsProvider) {
|
||||
|
||||
this.showKeyboard = !!navParams.get('showKeyboard');
|
||||
this.showScanQR = this.utils.canScanQR();
|
||||
|
@ -363,19 +365,69 @@ export class CoreLoginSitePage {
|
|||
/**
|
||||
* Scan a QR code and put its text in the URL input.
|
||||
*/
|
||||
scanQR(): void {
|
||||
async scanQR(): Promise<void> {
|
||||
// Scan for a QR code.
|
||||
this.utils.scanQR().then((text) => {
|
||||
if (text) {
|
||||
if (this.urlSchemesProvider.isCustomURL(text)) {
|
||||
this.urlSchemesProvider.handleCustomURL(text);
|
||||
} else {
|
||||
// Not a custom URL scheme, put the text in the field.
|
||||
this.siteForm.controls.siteUrl.setValue(text);
|
||||
const text = await this.utils.scanQR();
|
||||
|
||||
this.connect(new Event('click'), text);
|
||||
if (text) {
|
||||
if (this.urlSchemesProvider.isCustomURL(text)) {
|
||||
try {
|
||||
await this.urlSchemesProvider.handleCustomURL(text);
|
||||
} catch (error) {
|
||||
if (error && error.data && error.data.isAuthenticationURL && error.data.siteUrl) {
|
||||
// An error ocurred, but it's an authentication URL and we have the site URL.
|
||||
this.treatErrorInAuthenticationCustomURL(text, error);
|
||||
} else {
|
||||
this.urlSchemesProvider.treatHandleCustomURLError(error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not a custom URL scheme, put the text in the field.
|
||||
this.siteForm.controls.siteUrl.setValue(text);
|
||||
|
||||
this.connect(new Event('click'), text);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat an error while handling a custom URL meant to perform an authentication.
|
||||
* If the site doesn't use SSO, the user will be sent to the credentials screen.
|
||||
*
|
||||
* @param customURL Custom URL handled.
|
||||
* @param error Error data.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async treatErrorInAuthenticationCustomURL(customURL: string, error: CoreCustomURLSchemesHandleError): Promise<void> {
|
||||
const siteUrl = error.data.siteUrl;
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
|
||||
// Set the site URL in the input.
|
||||
this.siteForm.controls.siteUrl.setValue(siteUrl);
|
||||
|
||||
try {
|
||||
// Check if site uses SSO.
|
||||
const response = await this.sitesProvider.checkSite(siteUrl);
|
||||
|
||||
await this.sitesProvider.checkRequiredMinimumVersion(response.config);
|
||||
|
||||
if (!this.loginHelper.isSSOLoginNeeded(response.code)) {
|
||||
// No SSO, go to credentials page.
|
||||
await this.navCtrl.push('CoreLoginCredentialsPage', {
|
||||
siteUrl: response.siteUrl,
|
||||
siteConfig: response.config,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors.
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
}
|
||||
|
||||
// Now display the error.
|
||||
error.error = this.textUtils.addTextToError(error.error,
|
||||
'<br><br>' + this.translate.instant('core.login.youcanstillconnectwithcredentials'));
|
||||
|
||||
this.urlSchemesProvider.treatHandleCustomURLError(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Component, OnDestroy } from '@angular/core';
|
|||
import { IonicPage, NavController } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
|
||||
import { CoreCustomURLSchemesProvider, CoreCustomURLSchemesHandleError } from '@providers/urlschemes';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/delegate';
|
||||
|
@ -179,7 +179,9 @@ export class CoreMainMenuMorePage implements OnDestroy {
|
|||
if (text) {
|
||||
if (this.urlSchemesProvider.isCustomURL(text)) {
|
||||
// Is a custom URL scheme, handle it.
|
||||
this.urlSchemesProvider.handleCustomURL(text);
|
||||
this.urlSchemesProvider.handleCustomURL(text).catch((error: CoreCustomURLSchemesHandleError) => {
|
||||
this.urlSchemesProvider.treatHandleCustomURLError(error);
|
||||
});
|
||||
} else if (/^[^:]{2,}:\/\/[^ ]+$/i.test(text)) { // Check if it's a URL.
|
||||
// Check if the app can handle the URL.
|
||||
this.linkHelper.handleLink(text, undefined, this.navCtrl, true, true).then((treated) => {
|
||||
|
|
|
@ -21,7 +21,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
|||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
|
||||
import { CoreCustomURLSchemesProvider, CoreCustomURLSchemesHandleError } from '@providers/urlschemes';
|
||||
|
||||
/**
|
||||
* Directive to open a link in external browser.
|
||||
|
@ -113,7 +113,9 @@ export class CoreLinkDirective implements OnInit {
|
|||
this.domUtils.scrollToElementBySelector(this.content, '#' + href + ', [name=\'' + href + '\']');
|
||||
}
|
||||
} else if (this.urlSchemesProvider.isCustomURL(href)) {
|
||||
this.urlSchemesProvider.handleCustomURL(href);
|
||||
this.urlSchemesProvider.handleCustomURL(href).catch((error: CoreCustomURLSchemesHandleError) => {
|
||||
this.urlSchemesProvider.treatHandleCustomURLError(error);
|
||||
});
|
||||
} else {
|
||||
|
||||
// It's an external link, we will open with browser. Check if we need to auto-login.
|
||||
|
|
|
@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { CoreAppProvider } from './app';
|
||||
import { CoreInitDelegate } from './init';
|
||||
import { CoreLoggerProvider } from './logger';
|
||||
import { CoreSitesProvider } from './sites';
|
||||
import { CoreSitesProvider, CoreSiteCheckResponse } from './sites';
|
||||
import { CoreDomUtilsProvider } from './utils/dom';
|
||||
import { CoreTextUtilsProvider } from './utils/text';
|
||||
import { CoreUrlUtilsProvider } from './utils/url';
|
||||
|
@ -44,6 +44,16 @@ export interface CoreCustomURLSchemesParams extends CoreLoginSSOData {
|
|||
* URL to open once authenticated.
|
||||
*/
|
||||
redirect?: any;
|
||||
|
||||
/**
|
||||
* Whether it's an SSO token URL.
|
||||
*/
|
||||
isSSOToken?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the URL is meant to perform an authentication.
|
||||
*/
|
||||
isAuthenticationURL?: boolean;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -70,21 +80,50 @@ export class CoreCustomURLSchemesProvider {
|
|||
this.logger = logger.getInstance('CoreCustomURLSchemesProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Given some data of a custom URL with a token, create a site if it needs to be created.
|
||||
*
|
||||
* @param data URL data.
|
||||
* @return Promise resolved with the site ID.
|
||||
*/
|
||||
protected async createSiteIfNeeded(data: CoreCustomURLSchemesParams): Promise<string> {
|
||||
if (!data.token) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSite = this.sitesProvider.getCurrentSite();
|
||||
|
||||
if (!currentSite || currentSite.getToken() != data.token) {
|
||||
// Token belongs to a different site, create it. It doesn't matter if it already exists.
|
||||
|
||||
if (!data.siteUrl.match(/^https?:\/\//)) {
|
||||
// URL doesn't have a protocol and it's required to be able to create the site. Check which one to use.
|
||||
const result = await this.sitesProvider.checkSite(data.siteUrl);
|
||||
|
||||
data.siteUrl = result.siteUrl;
|
||||
|
||||
await this.sitesProvider.checkRequiredMinimumVersion(result.config);
|
||||
}
|
||||
|
||||
return this.sitesProvider.newSite(data.siteUrl, data.token, data.privateToken, data.isSSOToken,
|
||||
this.loginHelper.getOAuthIdFromParams(data.ssoUrlParams));
|
||||
} else {
|
||||
// Token belongs to current site, no need to create it.
|
||||
return this.sitesProvider.getCurrentSiteId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an URL received by custom URL scheme.
|
||||
*
|
||||
* @param url URL to treat.
|
||||
* @return Promise resolved when done.
|
||||
* @return Promise resolved when done. If rejected, the parameter is of type CoreCustomURLSchemesHandleError.
|
||||
*/
|
||||
handleCustomURL(url: string): Promise<any> {
|
||||
async handleCustomURL(url: string): Promise<void> {
|
||||
if (!this.isCustomURL(url)) {
|
||||
return Promise.reject(null);
|
||||
throw new CoreCustomURLSchemesHandleError(null);
|
||||
}
|
||||
|
||||
let modal,
|
||||
isSSOToken = false,
|
||||
data: CoreCustomURLSchemesParams;
|
||||
|
||||
/* First check that this URL hasn't been treated a few seconds ago. The function that handles custom URL schemes already
|
||||
does this, but this function is called from other places so we need to handle it in here too. */
|
||||
if (this.lastUrls[url] && Date.now() - this.lastUrls[url] < 3000) {
|
||||
|
@ -93,69 +132,49 @@ export class CoreCustomURLSchemesProvider {
|
|||
}
|
||||
|
||||
this.lastUrls[url] = Date.now();
|
||||
url = this.textUtils.decodeURIComponent(url);
|
||||
|
||||
// Wait for app to be ready.
|
||||
return this.initDelegate.ready().then(() => {
|
||||
url = this.textUtils.decodeURIComponent(url);
|
||||
await this.initDelegate.ready();
|
||||
|
||||
// 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(/\/?#?\/?$/, '');
|
||||
// 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(/\/?#?\/?$/, '');
|
||||
|
||||
modal = this.domUtils.showModalLoading();
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
let data: CoreCustomURLSchemesParams;
|
||||
|
||||
// Get the data from the URL.
|
||||
// Get the data from the URL.
|
||||
try {
|
||||
if (this.isCustomURLToken(url)) {
|
||||
isSSOToken = true;
|
||||
|
||||
return this.getCustomURLTokenData(url);
|
||||
data = await this.getCustomURLTokenData(url);
|
||||
} else if (this.isCustomURLLink(url)) {
|
||||
// In iOS, the protocol after the scheme doesn't have ":". Add it.
|
||||
url = url.replace(/\/\/link=(https?)\/\//, '//link=$1://');
|
||||
|
||||
return this.getCustomURLLinkData(url);
|
||||
data = await this.getCustomURLLinkData(url);
|
||||
} else {
|
||||
// In iOS, the protocol after the scheme doesn't have ":". Add it.
|
||||
url = url.replace(/\/\/(https?)\/\//, '//$1://');
|
||||
|
||||
return this.getCustomURLData(url);
|
||||
data = await this.getCustomURLData(url);
|
||||
}
|
||||
}).then((result) => {
|
||||
data = result;
|
||||
} catch (error) {
|
||||
modal.dismiss();
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
if (data.redirect && data.redirect.match(/^https?:\/\//) && data.redirect.indexOf(data.siteUrl) == -1) {
|
||||
// Redirect URL must belong to the same site. Reject.
|
||||
return Promise.reject(this.translate.instant('core.contentlinks.errorredirectothersite'));
|
||||
throw this.translate.instant('core.contentlinks.errorredirectothersite');
|
||||
}
|
||||
|
||||
// First of all, authenticate the user if needed.
|
||||
const currentSite = this.sitesProvider.getCurrentSite();
|
||||
// First of all, create the site if needed.
|
||||
const siteId = await this.createSiteIfNeeded(data);
|
||||
|
||||
if (data.token) {
|
||||
if (!currentSite || currentSite.getToken() != data.token) {
|
||||
// Token belongs to a different site, create it. It doesn't matter if it already exists.
|
||||
let promise;
|
||||
|
||||
if (!data.siteUrl.match(/^https?:\/\//)) {
|
||||
// URL doesn't have a protocol and it's required to be able to create the site. Check which one to use.
|
||||
promise = this.sitesProvider.checkSite(data.siteUrl).then((result) => {
|
||||
data.siteUrl = result.siteUrl;
|
||||
});
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
return this.sitesProvider.newSite(data.siteUrl, data.token, data.privateToken, isSSOToken,
|
||||
this.loginHelper.getOAuthIdFromParams(data.ssoUrlParams));
|
||||
});
|
||||
} else {
|
||||
// Token belongs to current site, no need to create it.
|
||||
return this.sitesProvider.getCurrentSiteId();
|
||||
}
|
||||
}
|
||||
}).then((siteId) => {
|
||||
if (isSSOToken) {
|
||||
if (data.isSSOToken) {
|
||||
// Site created and authenticated, open the page to go.
|
||||
if (data.pageName) {
|
||||
// State defined, go to that state instead of site initial page.
|
||||
|
@ -172,113 +191,58 @@ export class CoreCustomURLSchemesProvider {
|
|||
data.redirect = this.textUtils.concatenatePaths(data.siteUrl, data.redirect);
|
||||
}
|
||||
|
||||
let promise;
|
||||
let siteIds = [siteId];
|
||||
|
||||
if (siteId) {
|
||||
// Site created, we know the site to use.
|
||||
promise = Promise.resolve([siteId]);
|
||||
} else {
|
||||
// Check if the site is stored.
|
||||
promise = this.sitesProvider.getSiteIdsFromUrl(data.siteUrl, true, data.username);
|
||||
if (!siteId) {
|
||||
// No site created, check if the site is stored (to know which one to use).
|
||||
siteIds = await this.sitesProvider.getSiteIdsFromUrl(data.siteUrl, true, data.username);
|
||||
}
|
||||
|
||||
return promise.then((siteIds) => {
|
||||
if (siteIds.length > 1) {
|
||||
// More than one site to treat the URL, let the user choose.
|
||||
this.linksHelper.goToChooseSite(data.redirect || data.siteUrl);
|
||||
if (siteIds.length > 1) {
|
||||
// More than one site to treat the URL, let the user choose.
|
||||
this.linksHelper.goToChooseSite(data.redirect || data.siteUrl);
|
||||
|
||||
} else if (siteIds.length == 1) {
|
||||
// Only one site, handle the link.
|
||||
return this.sitesProvider.getSite(siteIds[0]).then((site) => {
|
||||
if (!data.redirect) {
|
||||
// No redirect, go to the root URL if needed.
|
||||
|
||||
return this.linksHelper.handleRootURL(site, false, true);
|
||||
} else {
|
||||
// Handle the redirect link.
|
||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||
|
||||
/* Always use the username from the site in this case. If the link has a username and a token,
|
||||
this will make sure that the link is opened with the user the token belongs to. */
|
||||
const username = site.getInfo().username || data.username;
|
||||
|
||||
return this.linksHelper.handleLink(data.redirect, username).then((treated) => {
|
||||
if (!treated) {
|
||||
this.domUtils.showErrorModal('core.contentlinks.errornoactions', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (siteIds.length == 1) {
|
||||
// Only one site, handle the link.
|
||||
const site = await this.sitesProvider.getSite(siteIds[0]);
|
||||
|
||||
if (!data.redirect) {
|
||||
// No redirect, go to the root URL if needed.
|
||||
await this.linksHelper.handleRootURL(site, false, true);
|
||||
} else {
|
||||
// Site not stored. Try to add the site.
|
||||
return this.sitesProvider.checkSite(data.siteUrl).then((result) => {
|
||||
// Site exists. We'll allow to add it.
|
||||
const ssoNeeded = this.loginHelper.isSSOLoginNeeded(result.code),
|
||||
pageName = 'CoreLoginCredentialsPage',
|
||||
pageParams = {
|
||||
siteUrl: result.siteUrl,
|
||||
username: data.username,
|
||||
urlToOpen: data.redirect,
|
||||
siteConfig: result.config
|
||||
};
|
||||
let promise,
|
||||
hasSitePluginsLoaded = false;
|
||||
// Handle the redirect link.
|
||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||
|
||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||
/* Always use the username from the site in this case. If the link has a username and a token,
|
||||
this will make sure that the link is opened with the user the token belongs to. */
|
||||
const username = site.getInfo().username || data.username;
|
||||
|
||||
if (!this.sitesProvider.isLoggedIn()) {
|
||||
// Not logged in, no need to confirm. If SSO the confirm will be shown later.
|
||||
promise = Promise.resolve();
|
||||
} else {
|
||||
// Ask the user before changing site.
|
||||
const confirmMsg = this.translate.instant('core.contentlinks.confirmurlothersite');
|
||||
promise = this.domUtils.showConfirm(confirmMsg).then(() => {
|
||||
if (!ssoNeeded) {
|
||||
hasSitePluginsLoaded = this.sitePluginsProvider.hasSitePluginsLoaded;
|
||||
if (hasSitePluginsLoaded) {
|
||||
// Store the redirect since logout will restart the app.
|
||||
this.appProvider.storeRedirect(CoreConstants.NO_SITE_ID, pageName, pageParams);
|
||||
}
|
||||
const treated = await this.linksHelper.handleLink(data.redirect, username);
|
||||
|
||||
return this.sitesProvider.logout().catch(() => {
|
||||
// Ignore errors (shouldn't happen).
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
if (ssoNeeded) {
|
||||
this.loginHelper.confirmAndOpenBrowserForSSOLogin(
|
||||
result.siteUrl, result.code, result.service, result.config && result.config.launchurl);
|
||||
} else if (!hasSitePluginsLoaded) {
|
||||
return this.loginHelper.goToNoSitePage(undefined, pageName, pageParams);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
if (!treated) {
|
||||
this.domUtils.showErrorModal('core.contentlinks.errornoactions', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}).catch((error) => {
|
||||
if (error == 'Duplicated') {
|
||||
// Duplicated request
|
||||
} else if (error && isSSOToken) {
|
||||
// An error occurred, display the error and logout the user.
|
||||
this.loginHelper.treatUserTokenError(data.siteUrl, error);
|
||||
this.sitesProvider.logout();
|
||||
} else {
|
||||
this.domUtils.showErrorModalDefault(error, this.translate.instant('core.login.invalidsite'));
|
||||
// Site not stored. Try to add the site.
|
||||
const result = await this.sitesProvider.checkSite(data.siteUrl);
|
||||
|
||||
// Site exists. We'll allow to add it.
|
||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||
|
||||
await this.goToAddSite(data, result);
|
||||
}
|
||||
}).finally(() => {
|
||||
|
||||
} catch (error) {
|
||||
throw new CoreCustomURLSchemesHandleError(error, data);
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
|
||||
if (isSSOToken) {
|
||||
if (data.isSSOToken) {
|
||||
this.appProvider.finishSSOAuthentication();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,9 +252,9 @@ export class CoreCustomURLSchemesProvider {
|
|||
* @param url URL to treat.
|
||||
* @return Promise resolved with the data.
|
||||
*/
|
||||
protected getCustomURLData(url: string): Promise<CoreCustomURLSchemesParams> {
|
||||
protected async getCustomURLData(url: string): Promise<CoreCustomURLSchemesParams> {
|
||||
if (!this.isCustomURL(url)) {
|
||||
return Promise.reject(null);
|
||||
throw new CoreCustomURLSchemesHandleError(null);
|
||||
}
|
||||
|
||||
// App opened using custom URL scheme.
|
||||
|
@ -313,34 +277,26 @@ export class CoreCustomURLSchemesProvider {
|
|||
url = url.substr(0, url.indexOf('?'));
|
||||
}
|
||||
|
||||
let promise;
|
||||
|
||||
if (!url.match(/https?:\/\//)) {
|
||||
// Url doesn't have a protocol. Check if the site is stored in the app to be able to determine the protocol.
|
||||
promise = this.sitesProvider.getSiteIdsFromUrl(url, true, username).then((siteIds) => {
|
||||
if (siteIds.length) {
|
||||
// There is at least 1 site with this URL. Use it to know the full URL.
|
||||
return this.sitesProvider.getSite(siteIds[0]).then((site) => {
|
||||
return site.getURL();
|
||||
});
|
||||
} else {
|
||||
// No site stored with this URL, just use the URL as it is.
|
||||
return url;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise = Promise.resolve(url);
|
||||
const siteIds = await this.sitesProvider.getSiteIdsFromUrl(url, true, username);
|
||||
|
||||
if (siteIds.length) {
|
||||
// There is at least 1 site with this URL. Use it to know the full URL.
|
||||
const site = await this.sitesProvider.getSite(siteIds[0]);
|
||||
|
||||
url = site.getURL();
|
||||
}
|
||||
}
|
||||
|
||||
return promise.then((url) => {
|
||||
return {
|
||||
siteUrl: url,
|
||||
username: username,
|
||||
token: params.token,
|
||||
privateToken: params.privateToken,
|
||||
redirect: params.redirect
|
||||
};
|
||||
});
|
||||
return {
|
||||
siteUrl: url,
|
||||
username: username,
|
||||
token: params.token,
|
||||
privateToken: params.privateToken,
|
||||
redirect: params.redirect,
|
||||
isAuthenticationURL: !!params.token,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,9 +305,9 @@ export class CoreCustomURLSchemesProvider {
|
|||
* @param url URL to treat.
|
||||
* @return Promise resolved with the data.
|
||||
*/
|
||||
protected getCustomURLLinkData(url: string): Promise<CoreCustomURLSchemesParams> {
|
||||
protected async getCustomURLLinkData(url: string): Promise<CoreCustomURLSchemesParams> {
|
||||
if (!this.isCustomURLLink(url)) {
|
||||
return Promise.reject(null);
|
||||
throw new CoreCustomURLSchemesHandleError(null);
|
||||
}
|
||||
|
||||
// App opened using custom URL scheme.
|
||||
|
@ -367,43 +323,42 @@ export class CoreCustomURLSchemesProvider {
|
|||
}
|
||||
|
||||
// First of all, check if it's the root URL of a site.
|
||||
return this.sitesProvider.isStoredRootURL(url, username).then((data): any => {
|
||||
const data = await this.sitesProvider.isStoredRootURL(url, username);
|
||||
|
||||
if (data.site) {
|
||||
// Root URL.
|
||||
return {
|
||||
siteUrl: data.site.getURL(),
|
||||
username: username
|
||||
};
|
||||
if (data.site) {
|
||||
// Root URL.
|
||||
return {
|
||||
siteUrl: data.site.getURL(),
|
||||
username: username
|
||||
};
|
||||
|
||||
} else if (data.siteIds.length > 0) {
|
||||
// Not the root URL, but at least 1 site supports the URL. Get the site URL from the list of sites.
|
||||
return this.sitesProvider.getSite(data.siteIds[0]).then((site) => {
|
||||
return {
|
||||
siteUrl: site.getURL(),
|
||||
username: username,
|
||||
redirect: url
|
||||
};
|
||||
});
|
||||
} else if (data.siteIds.length > 0) {
|
||||
// Not the root URL, but at least 1 site supports the URL. Get the site URL from the list of sites.
|
||||
const site = await this.sitesProvider.getSite(data.siteIds[0]);
|
||||
|
||||
} else {
|
||||
// Get the site URL.
|
||||
let siteUrl = this.linksDelegate.getSiteUrl(url),
|
||||
redirect = url;
|
||||
return {
|
||||
siteUrl: site.getURL(),
|
||||
username: username,
|
||||
redirect: url
|
||||
};
|
||||
|
||||
if (!siteUrl) {
|
||||
// Site URL not found, use the original URL since it could be the root URL of the site.
|
||||
siteUrl = url;
|
||||
redirect = undefined;
|
||||
}
|
||||
} else {
|
||||
// Get the site URL.
|
||||
let siteUrl = this.linksDelegate.getSiteUrl(url);
|
||||
let redirect = url;
|
||||
|
||||
return {
|
||||
siteUrl: siteUrl,
|
||||
username: username,
|
||||
redirect: redirect
|
||||
};
|
||||
if (!siteUrl) {
|
||||
// Site URL not found, use the original URL since it could be the root URL of the site.
|
||||
siteUrl = url;
|
||||
redirect = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
siteUrl: siteUrl,
|
||||
username: username,
|
||||
redirect: redirect
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -412,14 +367,14 @@ export class CoreCustomURLSchemesProvider {
|
|||
* @param url URL to treat.
|
||||
* @return Promise resolved with the data.
|
||||
*/
|
||||
protected getCustomURLTokenData(url: string): Promise<CoreCustomURLSchemesParams> {
|
||||
protected async getCustomURLTokenData(url: string): Promise<CoreCustomURLSchemesParams> {
|
||||
if (!this.isCustomURLToken(url)) {
|
||||
return Promise.reject(null);
|
||||
throw new CoreCustomURLSchemesHandleError(null);
|
||||
}
|
||||
|
||||
if (this.appProvider.isSSOAuthenticationOngoing()) {
|
||||
// Authentication ongoing, probably duplicated request.
|
||||
return Promise.reject('Duplicated');
|
||||
throw new CoreCustomURLSchemesHandleError('Duplicated');
|
||||
}
|
||||
|
||||
if (this.appProvider.isDesktop()) {
|
||||
|
@ -445,10 +400,56 @@ export class CoreCustomURLSchemesProvider {
|
|||
// Error decoding the parameter.
|
||||
this.logger.error('Error decoding parameter received for login SSO');
|
||||
|
||||
return Promise.reject(null);
|
||||
throw new CoreCustomURLSchemesHandleError(null);
|
||||
}
|
||||
|
||||
return this.loginHelper.validateBrowserSSOLogin(url);
|
||||
const data: CoreCustomURLSchemesParams = await this.loginHelper.validateBrowserSSOLogin(url);
|
||||
|
||||
data.isSSOToken = true;
|
||||
data.isAuthenticationURL = true;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to page to add a site, or open a browser if SSO.
|
||||
*
|
||||
* @param data URL data.
|
||||
* @param checkResponse Result of checkSite.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async goToAddSite(data: CoreCustomURLSchemesParams, checkResponse: CoreSiteCheckResponse): Promise<void> {
|
||||
const ssoNeeded = this.loginHelper.isSSOLoginNeeded(checkResponse.code);
|
||||
const pageName = 'CoreLoginCredentialsPage';
|
||||
const pageParams = {
|
||||
siteUrl: checkResponse.siteUrl,
|
||||
username: data.username,
|
||||
urlToOpen: data.redirect,
|
||||
siteConfig: checkResponse.config
|
||||
};
|
||||
let hasSitePluginsLoaded = false;
|
||||
|
||||
if (this.sitesProvider.isLoggedIn()) {
|
||||
// Ask the user before changing site.
|
||||
await this.domUtils.showConfirm(this.translate.instant('core.contentlinks.confirmurlothersite'));
|
||||
|
||||
if (!ssoNeeded) {
|
||||
hasSitePluginsLoaded = this.sitePluginsProvider.hasSitePluginsLoaded;
|
||||
if (hasSitePluginsLoaded) {
|
||||
// Store the redirect since logout will restart the app.
|
||||
this.appProvider.storeRedirect(CoreConstants.NO_SITE_ID, pageName, pageParams);
|
||||
}
|
||||
|
||||
await this.sitesProvider.logout();
|
||||
}
|
||||
}
|
||||
|
||||
if (ssoNeeded) {
|
||||
this.loginHelper.confirmAndOpenBrowserForSSOLogin(checkResponse.siteUrl, checkResponse.code, checkResponse.service,
|
||||
checkResponse.config && checkResponse.config.launchurl);
|
||||
} else if (!hasSitePluginsLoaded) {
|
||||
await this.loginHelper.goToNoSitePage(undefined, pageName, pageParams);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -522,6 +523,37 @@ export class CoreCustomURLSchemesProvider {
|
|||
removeCustomURLTokenScheme(url: string): string {
|
||||
return url.replace(CoreConfigConstants.customurlscheme + '://token=', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat error returned by handleCustomURL.
|
||||
*
|
||||
* @param error Error data.
|
||||
*/
|
||||
treatHandleCustomURLError(error: CoreCustomURLSchemesHandleError): void {
|
||||
if (error.error == 'Duplicated') {
|
||||
// Duplicated request
|
||||
} else if (error.error && error.data && error.data.isSSOToken) {
|
||||
// An error occurred, display the error and logout the user.
|
||||
this.loginHelper.treatUserTokenError(error.data.siteUrl, error.error);
|
||||
this.sitesProvider.logout();
|
||||
} else {
|
||||
this.domUtils.showErrorModalDefault(error.error, this.translate.instant('core.login.invalidsite'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error returned by handleCustomURL.
|
||||
*/
|
||||
export class CoreCustomURLSchemesHandleError {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param error The error message or object.
|
||||
* @param data Data obtained from the URL (if any).
|
||||
*/
|
||||
constructor(public error: any, public data?: CoreCustomURLSchemesParams) { }
|
||||
}
|
||||
|
||||
export class CoreCustomURLSchemes extends makeSingleton(CoreCustomURLSchemesProvider) {}
|
||||
|
|
|
@ -104,6 +104,33 @@ export class CoreTextUtilsProvider {
|
|||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some text to an error message.
|
||||
*
|
||||
* @param error Error message or object.
|
||||
* @param text Text to add.
|
||||
* @return Modified error.
|
||||
*/
|
||||
addTextToError(error: string | CoreTextErrorObject, text: string): string | CoreTextErrorObject {
|
||||
if (typeof error == 'string') {
|
||||
return error + text;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (typeof error.message == 'string') {
|
||||
error.message += text;
|
||||
} else if (typeof error.error == 'string') {
|
||||
error.error += text;
|
||||
} else if (typeof error.content == 'string') {
|
||||
error.content += text;
|
||||
} else if (typeof error.body == 'string') {
|
||||
error.body += text;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an address as a string, return a URL to open the address in maps.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue