commit
4b21c45d73
|
@ -0,0 +1,122 @@
|
||||||
|
// (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.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parts contained within a url.
|
||||||
|
*/
|
||||||
|
interface UrlParts {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url protocol.
|
||||||
|
*/
|
||||||
|
protocol?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url domain.
|
||||||
|
*/
|
||||||
|
domain?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url port.
|
||||||
|
*/
|
||||||
|
port?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url path.
|
||||||
|
*/
|
||||||
|
path?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url query.
|
||||||
|
*/
|
||||||
|
query?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url fragment.
|
||||||
|
*/
|
||||||
|
fragment?: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton with helper functions for urls.
|
||||||
|
*/
|
||||||
|
export class CoreUrl {
|
||||||
|
|
||||||
|
// Avoid creating singleton instances.
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse parts of a url, using an implicit protocol if it is missing from the url.
|
||||||
|
*
|
||||||
|
* @param url Url.
|
||||||
|
* @return Url parts.
|
||||||
|
*/
|
||||||
|
static parse(url: string): UrlParts | null {
|
||||||
|
// Parse url with regular expression taken from RFC 3986: https://tools.ietf.org/html/rfc3986#appendix-B.
|
||||||
|
const match = url.trim().match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split host into domain and port.
|
||||||
|
const host = match[4] || '';
|
||||||
|
const [domain, port]: string[] = host.indexOf(':') === -1 ? [host] : host.split(':');
|
||||||
|
|
||||||
|
// Prepare parts replacing empty strings with undefined.
|
||||||
|
return {
|
||||||
|
protocol: match[2] || undefined,
|
||||||
|
domain: domain || undefined,
|
||||||
|
port: port || undefined,
|
||||||
|
path: match[5] || undefined,
|
||||||
|
query: match[7] || undefined,
|
||||||
|
fragment: match[9] || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guess the Moodle domain from a site url.
|
||||||
|
*
|
||||||
|
* @param url Site url.
|
||||||
|
* @return Guessed Moodle domain.
|
||||||
|
*/
|
||||||
|
static guessMoodleDomain(url: string): string | null {
|
||||||
|
// Add protocol if it was missing. Moodle can only be served through http or https, so this is a fair assumption to make.
|
||||||
|
if (!url.match(/^https?:\/\//)) {
|
||||||
|
url = `https://${url}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match using common suffixes.
|
||||||
|
const knownSuffixes = [
|
||||||
|
'\/my\/?',
|
||||||
|
'\/\\\?redirect=0',
|
||||||
|
'\/index\\\.php',
|
||||||
|
'\/course\/view\\\.php',
|
||||||
|
'\/login\/index\\\.php',
|
||||||
|
'\/mod\/page\/view\\\.php',
|
||||||
|
];
|
||||||
|
const match = url.match(new RegExp(`^https?:\/\/(.*?)(${knownSuffixes.join('|')})`));
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing else worked, parse the domain.
|
||||||
|
const urlParts = CoreUrl.parse(url);
|
||||||
|
|
||||||
|
return urlParts && urlParts.domain ? urlParts.domain : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -55,6 +55,7 @@ import { Md5 } from 'ts-md5/dist/md5';
|
||||||
|
|
||||||
// Import core classes that can be useful for site plugins.
|
// Import core classes that can be useful for site plugins.
|
||||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||||
|
import { CoreUrl } from '@classes/utils/url';
|
||||||
import { CoreCache } from '@classes/cache';
|
import { CoreCache } from '@classes/cache';
|
||||||
import { CoreDelegate } from '@classes/delegate';
|
import { CoreDelegate } from '@classes/delegate';
|
||||||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||||
|
@ -263,6 +264,7 @@ export class CoreCompileProvider {
|
||||||
instance['moment'] = moment;
|
instance['moment'] = moment;
|
||||||
instance['Md5'] = Md5;
|
instance['Md5'] = Md5;
|
||||||
instance['CoreSyncBaseProvider'] = CoreSyncBaseProvider;
|
instance['CoreSyncBaseProvider'] = CoreSyncBaseProvider;
|
||||||
|
instance['CoreUrl'] = CoreUrl;
|
||||||
instance['CoreCache'] = CoreCache;
|
instance['CoreCache'] = CoreCache;
|
||||||
instance['CoreDelegate'] = CoreDelegate;
|
instance['CoreDelegate'] = CoreDelegate;
|
||||||
instance['CoreContentLinksHandlerBase'] = CoreContentLinksHandlerBase;
|
instance['CoreContentLinksHandlerBase'] = CoreContentLinksHandlerBase;
|
||||||
|
|
|
@ -15,11 +15,12 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { IonicPage, NavController, ModalController, NavParams } from 'ionic-angular';
|
import { IonicPage, NavController, ModalController, NavParams } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider, CoreSiteCheckResponse } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreConfigConstants } from '../../../../configconstants';
|
import { CoreConfigConstants } from '../../../../configconstants';
|
||||||
import { CoreLoginHelperProvider } from '../../providers/helper';
|
import { CoreLoginHelperProvider } from '../../providers/helper';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { CoreUrl } from '@classes/utils/url';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page to enter or select the site URL to connect to.
|
* Page to enter or select the site URL to connect to.
|
||||||
|
@ -86,6 +87,8 @@ export class CoreLoginSitePage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url = url.trim();
|
||||||
|
|
||||||
const modal = this.domUtils.showModalLoading(),
|
const modal = this.domUtils.showModalLoading(),
|
||||||
siteData = this.sitesProvider.getDemoSiteData(url);
|
siteData = this.sitesProvider.getDemoSiteData(url);
|
||||||
|
|
||||||
|
@ -111,27 +114,16 @@ export class CoreLoginSitePage {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Not a demo site.
|
// Not a demo site.
|
||||||
this.sitesProvider.checkSite(url).then((result) => {
|
this.sitesProvider.checkSite(url)
|
||||||
return this.sitesProvider.checkRequiredMinimumVersion(result.config).then(() => {
|
.catch((error) => {
|
||||||
if (result.warning) {
|
// Attempt guessing the domain if the initial check failed
|
||||||
this.domUtils.showErrorModal(result.warning, true, 4000);
|
const domain = CoreUrl.guessMoodleDomain(url);
|
||||||
}
|
|
||||||
|
|
||||||
if (this.loginHelper.isSSOLoginNeeded(result.code)) {
|
return domain ? this.sitesProvider.checkSite(domain) : Promise.reject(error);
|
||||||
// SSO. User needs to authenticate in a browser.
|
})
|
||||||
this.loginHelper.confirmAndOpenBrowserForSSOLogin(
|
.then((result) => this.login(result))
|
||||||
result.siteUrl, result.code, result.service, result.config && result.config.launchurl);
|
.catch((error) => this.showLoginIssue(url, error))
|
||||||
} else {
|
.finally(() => modal.dismiss());
|
||||||
this.navCtrl.push('CoreLoginCredentialsPage', { siteUrl: result.siteUrl, siteConfig: result.config });
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
// Ignore errors.
|
|
||||||
});
|
|
||||||
}, (error) => {
|
|
||||||
this.showLoginIssue(url, error);
|
|
||||||
}).finally(() => {
|
|
||||||
modal.dismiss();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,4 +165,30 @@ export class CoreLoginSitePage {
|
||||||
|
|
||||||
modal.present();
|
modal.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process login to a site.
|
||||||
|
*
|
||||||
|
* @param response Response obtained from the site check request.
|
||||||
|
*
|
||||||
|
* @return Promise resolved after logging in.
|
||||||
|
*/
|
||||||
|
protected async login(response: CoreSiteCheckResponse): Promise<void> {
|
||||||
|
return this.sitesProvider.checkRequiredMinimumVersion(response.config).then(() => {
|
||||||
|
if (response.warning) {
|
||||||
|
this.domUtils.showErrorModal(response.warning, true, 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.loginHelper.isSSOLoginNeeded(response.code)) {
|
||||||
|
// SSO. User needs to authenticate in a browser.
|
||||||
|
this.loginHelper.confirmAndOpenBrowserForSSOLogin(
|
||||||
|
response.siteUrl, response.code, response.service, response.config && response.config.launchurl);
|
||||||
|
} else {
|
||||||
|
this.navCtrl.push('CoreLoginCredentialsPage', { siteUrl: response.siteUrl, siteConfig: response.config });
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
// Ignore errors.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue