Automated cherry pick of #4372 (#4376)

* Handle SSO redirecting to a different URL than specified by the user

* Set Server URL based on the last redirect

* Improve last redirect condition

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Mattermost Build
2020-06-01 22:22:28 +02:00
committed by GitHub
parent 6e239d5566
commit fb03a88304
2 changed files with 58 additions and 28 deletions

View File

@@ -147,19 +147,19 @@ export default class SelectServer extends PureComponent {
}
};
getUrl = () => {
const urlParse = require('url-parse');
let preUrl = urlParse(this.state.url, true);
getUrl = async (serverUrl, useHttp = false) => {
let url = this.sanitizeUrl(serverUrl, useHttp);
if (!preUrl.host || preUrl.protocol === 'file:') {
preUrl = urlParse('https://' + stripTrailingSlashes(this.state.url), true);
try {
const resp = await fetch(url, {method: 'HEAD'});
if (resp?.rnfbRespInfo?.redirects?.length) {
url = resp.rnfbRespInfo.redirects[resp.rnfbRespInfo.redirects.length - 1];
}
} catch {
// do nothing
}
if (preUrl.protocol === 'http:') {
preUrl.protocol = 'https:';
}
return stripTrailingSlashes(preUrl.protocol + '//' + preUrl.host + preUrl.pathname);
return this.sanitizeUrl(url, useHttp);
};
goToNextScreen = (screen, title, passProps = {}, navOptions = {}) => {
@@ -187,8 +187,6 @@ export default class SelectServer extends PureComponent {
};
handleConnect = preventDoubleTap(async () => {
const url = this.getUrl();
Keyboard.dismiss();
if (this.state.connecting || this.state.connected) {
@@ -197,7 +195,7 @@ export default class SelectServer extends PureComponent {
return;
}
if (!isValidUrl(url)) {
if (!isValidUrl(this.sanitizeUrl(this.state.url))) {
this.setState({
error: {
intl: {
@@ -219,11 +217,11 @@ export default class SelectServer extends PureComponent {
auto: true,
certificate,
}).build();
this.pingServer(url);
this.pingServer(this.state.url);
}
});
} else {
this.pingServer(url);
this.pingServer(this.state.url);
}
});
@@ -301,7 +299,7 @@ export default class SelectServer extends PureComponent {
resetToChannel();
};
pingServer = (url, retryWithHttp = true) => {
pingServer = async (url, retryWithHttp = true) => {
const {
getPing,
handleServerUrlChanged,
@@ -315,8 +313,9 @@ export default class SelectServer extends PureComponent {
error: null,
});
Client4.setUrl(url);
handleServerUrlChanged(url);
const serverUrl = await this.getUrl(url, !retryWithHttp);
Client4.setUrl(serverUrl);
handleServerUrlChanged(serverUrl);
let cancel = false;
this.cancelPing = () => {
@@ -330,13 +329,16 @@ export default class SelectServer extends PureComponent {
this.cancelPing = null;
};
getPing().then((result) => {
try {
const result = await getPing();
if (cancel) {
return;
}
if (result.error && retryWithHttp) {
this.pingServer(url.replace('https:', 'http:'), false);
const nurl = serverUrl.replace('https:', 'http:');
this.pingServer(nurl, false);
return;
}
@@ -350,7 +352,7 @@ export default class SelectServer extends PureComponent {
connecting: false,
error: result.error,
});
}).catch(() => {
} catch {
if (cancel) {
return;
}
@@ -358,9 +360,23 @@ export default class SelectServer extends PureComponent {
this.setState({
connecting: false,
});
});
}
};
sanitizeUrl = (url, useHttp = false) => {
const urlParse = require('url-parse');
let preUrl = urlParse(url, true);
if (!preUrl.host || preUrl.protocol === 'file:') {
preUrl = urlParse('https://' + stripTrailingSlashes(url), true);
}
if (preUrl.protocol === 'http:' && !useHttp) {
preUrl.protocol = 'https:';
}
return stripTrailingSlashes(preUrl.protocol + '//' + preUrl.host + preUrl.pathname);
}
scheduleSessionExpiredNotification = () => {
const {intl} = this.context;
const {actions} = this.props;

View File

@@ -87,15 +87,15 @@ class SSO extends PureComponent {
switch (props.ssoType) {
case ViewTypes.GITLAB:
this.loginUrl = `${props.serverUrl}/oauth/gitlab/mobile_login`;
this.completedUrl = '/signup/gitlab/complete';
this.completeUrlPath = '/signup/gitlab/complete';
break;
case ViewTypes.SAML:
this.loginUrl = `${props.serverUrl}/login/sso/saml?action=mobile`;
this.completedUrl = '/login/sso/saml';
this.completeUrlPath = '/login/sso/saml';
break;
case ViewTypes.OFFICE365:
this.loginUrl = `${props.serverUrl}/oauth/office365/mobile_login`;
this.completedUrl = '/signup/office365/complete';
this.completeUrlPath = '/signup/office365/complete';
break;
}
@@ -139,7 +139,7 @@ class SSO extends PureComponent {
if (parsed.host.includes('.onelogin.com')) {
nextState.jsCode = oneLoginFormScalingJS;
} else if (parsed.pathname === this.completedUrl) {
} else if (parsed.pathname === this.completeUrlPath) {
// To avoid `window.postMessage` conflicts in any of the SSO flows
// we enable the onMessage handler only When the webView navigates to the final SSO URL.
nextState.messagingEnabled = true;
@@ -150,8 +150,15 @@ class SSO extends PureComponent {
onLoadEnd = (event) => {
const url = event.nativeEvent.url;
if (url.includes(this.completedUrl)) {
CookieManager.get(this.props.serverUrl, this.useWebkit).then((res) => {
const parsed = urlParse(url);
let isLastRedirect = url.includes(this.completeUrlPath);
if (this.props.ssoType === ViewTypes.SAML) {
isLastRedirect = isLastRedirect && !parsed.query;
}
if (isLastRedirect) {
CookieManager.get(parsed.origin, this.useWebkit).then((res) => {
const mmtoken = res.MMAUTHTOKEN;
const token = typeof mmtoken === 'object' ? mmtoken.value : mmtoken;
@@ -162,6 +169,13 @@ class SSO extends PureComponent {
} = this.props.actions;
Client4.setToken(token);
if (this.props.serverUrl !== parsed.origin) {
const original = urlParse(this.props.serverUrl);
// Check whether we need to set a sub-path
parsed.set('pathname', original.pathname || '');
Client4.setUrl(parsed.href);
}
ssoLogin(token).then((result) => {
if (result.error) {
this.onLoadEndError(result.error);