[v1] Skip LoginOptions if only one SSO method is enabled (#6097)
* Skip LoginOptions if only one SSO method is enabled * feedback review
This commit is contained in:
parent
734675978d
commit
30a16d0bc1
10 changed files with 89 additions and 96 deletions
|
|
@ -9,6 +9,7 @@ import DeepLinkTypes from './deep_linking';
|
|||
import DeviceTypes from './device';
|
||||
import ListTypes from './list';
|
||||
import NavigationTypes from './navigation';
|
||||
import Sso from './sso';
|
||||
import Types from './types';
|
||||
import ViewTypes from './view';
|
||||
import WebsocketEvents from './websocket';
|
||||
|
|
@ -21,6 +22,7 @@ export {
|
|||
DateTypes,
|
||||
ListTypes,
|
||||
NavigationTypes,
|
||||
Sso,
|
||||
Types,
|
||||
ViewTypes,
|
||||
WebsocketEvents,
|
||||
|
|
|
|||
21
app/constants/sso.ts
Normal file
21
app/constants/sso.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import keyMirror from '@mm-redux/utils/key_mirror';
|
||||
|
||||
export const REDIRECT_URL_SCHEME = 'mmauth://';
|
||||
export const REDIRECT_URL_SCHEME_DEV = 'mmauthbeta://';
|
||||
|
||||
const constants = keyMirror({
|
||||
SAML: null,
|
||||
GITLAB: null,
|
||||
GOOGLE: null,
|
||||
OFFICE365: null,
|
||||
OPENID: null,
|
||||
});
|
||||
|
||||
export default {
|
||||
...constants,
|
||||
REDIRECT_URL_SCHEME,
|
||||
REDIRECT_URL_SCHEME_DEV,
|
||||
};
|
||||
|
|
@ -20,9 +20,8 @@ import gitlab from '@assets/images/gitlab.png';
|
|||
import google from '@assets/images/google.png';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import StatusBar from '@components/status_bar';
|
||||
import {ViewTypes} from '@constants';
|
||||
import {Sso} from '@constants';
|
||||
import globalEventHandler from '@init/global_event_handler';
|
||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
|
|
@ -172,7 +171,7 @@ export default class LoginOptions extends PureComponent {
|
|||
return (
|
||||
<Button
|
||||
key='gitlab'
|
||||
onPress={preventDoubleTap(() => this.goToSSO(ViewTypes.GITLAB))}
|
||||
onPress={preventDoubleTap(() => this.goToSSO(Sso.GITLAB))}
|
||||
containerStyle={[GlobalStyles.signupButton, additionalButtonStyle]}
|
||||
>
|
||||
<Image
|
||||
|
|
@ -211,7 +210,7 @@ export default class LoginOptions extends PureComponent {
|
|||
return (
|
||||
<Button
|
||||
key='google'
|
||||
onPress={preventDoubleTap(() => this.goToSSO(ViewTypes.GOOGLE))}
|
||||
onPress={preventDoubleTap(() => this.goToSSO(Sso.GOOGLE))}
|
||||
containerStyle={[GlobalStyles.signupButton, additionalButtonStyle]}
|
||||
>
|
||||
<Image
|
||||
|
|
@ -247,7 +246,7 @@ export default class LoginOptions extends PureComponent {
|
|||
return (
|
||||
<Button
|
||||
key='o365'
|
||||
onPress={preventDoubleTap(() => this.goToSSO(ViewTypes.OFFICE365))}
|
||||
onPress={preventDoubleTap(() => this.goToSSO(Sso.OFFICE365))}
|
||||
containerStyle={[GlobalStyles.signupButton, additionalButtonStyle]}
|
||||
>
|
||||
<FormattedText
|
||||
|
|
@ -264,7 +263,7 @@ export default class LoginOptions extends PureComponent {
|
|||
|
||||
renderOpenIdOption = () => {
|
||||
const {config, license} = this.props;
|
||||
const openIdEnabled = config.EnableSignUpWithOpenId === 'true' && license.IsLicensed === 'true' && isMinimumServerVersion(config.Version, 5, 33, 0);
|
||||
const openIdEnabled = config.EnableSignUpWithOpenId === 'true' && license.IsLicensed === 'true';
|
||||
|
||||
if (openIdEnabled) {
|
||||
const additionalButtonStyle = {
|
||||
|
|
@ -278,7 +277,7 @@ export default class LoginOptions extends PureComponent {
|
|||
return (
|
||||
<Button
|
||||
key='openId'
|
||||
onPress={preventDoubleTap(() => this.goToSSO(ViewTypes.OPENID))}
|
||||
onPress={preventDoubleTap(() => this.goToSSO(Sso.OPENID))}
|
||||
containerStyle={[GlobalStyles.signupButton, additionalButtonStyle]}
|
||||
>
|
||||
<FormattedText
|
||||
|
|
@ -315,7 +314,7 @@ export default class LoginOptions extends PureComponent {
|
|||
return (
|
||||
<Button
|
||||
key='saml'
|
||||
onPress={preventDoubleTap(() => this.goToSSO(ViewTypes.SAML))}
|
||||
onPress={preventDoubleTap(() => this.goToSSO(Sso.SAML))}
|
||||
containerStyle={[GlobalStyles.signupButton, additionalStyle]}
|
||||
>
|
||||
<Text
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ describe('Login options', () => {
|
|||
expect(configuredWrapper.find(FormattedText).find({id: 'signup.google'}).exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('should show open id button only when enabled and from version 5.33', () => {
|
||||
test('should show open id button only when enabled', () => {
|
||||
const basicWrapper = shallowWithIntl(<LoginOptions {...baseProps}/>);
|
||||
expect(basicWrapper.find(FormattedText).find({id: 'signup.openid'}).exists()).toBe(false);
|
||||
|
||||
|
|
@ -42,21 +42,9 @@ describe('Login options', () => {
|
|||
config: {
|
||||
...baseProps.config,
|
||||
EnableSignUpWithOpenId: 'true',
|
||||
Version: '5.33.0',
|
||||
},
|
||||
};
|
||||
const newVersionWrapper = shallowWithIntl(<LoginOptions {...newVersionProps}/>);
|
||||
expect(newVersionWrapper.find(FormattedText).find({id: 'signup.openid'}).exists()).toBe(true);
|
||||
|
||||
const oldVersionProps = {
|
||||
...baseProps,
|
||||
config: {
|
||||
...baseProps.config,
|
||||
EnableSignUpWithOpenId: 'true',
|
||||
Version: '5.30.0',
|
||||
},
|
||||
};
|
||||
const oldVersionWrapper = shallowWithIntl(<LoginOptions {...oldVersionProps}/>);
|
||||
expect(oldVersionWrapper.find(FormattedText).find({id: 'signup.openid'}).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ import {Client4} from '@client/rest';
|
|||
import AppVersion from '@components/app_version';
|
||||
import ErrorText from '@components/error_text';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {Sso} from '@constants';
|
||||
import fetchConfig from '@init/fetch';
|
||||
import globalEventHandler from '@init/global_event_handler';
|
||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||
import {t} from '@utils/i18n';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
|
|
@ -233,20 +233,33 @@ export default class SelectServer extends PureComponent {
|
|||
handleLoginOptions = async () => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {config, license} = this.props;
|
||||
const samlEnabled = config.EnableSaml === 'true' && license.IsLicensed === 'true' && license.SAML === 'true';
|
||||
const isLicensed = license.IsLicensed === 'true';
|
||||
const samlEnabled = isLicensed && config.EnableSaml === 'true' && license.SAML === 'true';
|
||||
const googleEnabled = isLicensed && config.EnableSignUpWithGoogle === 'true';
|
||||
const o365Enabled = isLicensed && config.EnableSignUpWithOffice365 === 'true' && license.Office365OAuth === 'true';
|
||||
const openIdEnabled = isLicensed && config.EnableSignUpWithOpenId === 'true';
|
||||
const gitlabEnabled = config.EnableSignUpWithGitLab === 'true';
|
||||
const googleEnabled = config.EnableSignUpWithGoogle === 'true' && license.IsLicensed === 'true';
|
||||
const o365Enabled = config.EnableSignUpWithOffice365 === 'true' && license.IsLicensed === 'true' && license.Office365OAuth === 'true';
|
||||
const openIdEnabled = config.EnableSignUpWithOpenId === 'true' && license.IsLicensed === 'true' && isMinimumServerVersion(config.Version, 5, 33, 0);
|
||||
|
||||
let options = 0;
|
||||
if (samlEnabled || gitlabEnabled || googleEnabled || o365Enabled || openIdEnabled) {
|
||||
options += 1;
|
||||
}
|
||||
const ldapEnabled = isLicensed && config.EnableLdap === 'true' && license.LDAP === 'true';
|
||||
const hasLoginForm = config.EnableSignInWithEmail === 'true' || config.EnableSignInWithUsername === 'true' || ldapEnabled;
|
||||
const ssoOptions = {
|
||||
[Sso.SAML]: samlEnabled,
|
||||
[Sso.GITLAB]: gitlabEnabled,
|
||||
[Sso.GOOGLE]: googleEnabled,
|
||||
[Sso.OFFICE365]: o365Enabled,
|
||||
[Sso.OPENID]: openIdEnabled,
|
||||
};
|
||||
const enabledSSOs = Object.keys(ssoOptions).filter((key) => ssoOptions[key]);
|
||||
const numberSSOs = enabledSSOs.length;
|
||||
const redirectSSO = !hasLoginForm && numberSSOs === 1;
|
||||
|
||||
let screen;
|
||||
let title;
|
||||
if (options) {
|
||||
let props;
|
||||
if (redirectSSO) {
|
||||
screen = 'SSO';
|
||||
title = formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'});
|
||||
props = {ssoType: enabledSSOs[0]};
|
||||
} else if (hasLoginForm && numberSSOs > 0) {
|
||||
screen = 'LoginOptions';
|
||||
title = formatMessage({id: 'mobile.routes.loginOptions', defaultMessage: 'Login Chooser'});
|
||||
} else {
|
||||
|
|
@ -256,6 +269,7 @@ export default class SelectServer extends PureComponent {
|
|||
|
||||
this.props.actions.resetPing();
|
||||
await globalEventHandler.configureAnalytics();
|
||||
globalEventHandler.clearCookiesAndWebData();
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
if (config.ExperimentalClientSideCertEnable === 'true' && config.ExperimentalClientSideCertCheck === 'primary') {
|
||||
|
|
@ -265,10 +279,10 @@ export default class SelectServer extends PureComponent {
|
|||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.goToNextScreen(screen, title);
|
||||
this.goToNextScreen(screen, title, props);
|
||||
}, 350);
|
||||
} else {
|
||||
this.goToNextScreen(screen, title);
|
||||
this.goToNextScreen(screen, title, props);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,11 @@ import {resetToChannel} from '@actions/navigation';
|
|||
import {scheduleExpiredNotification} from '@actions/views/session';
|
||||
import {ssoLogin} from '@actions/views/user';
|
||||
import {Client4} from '@client/rest';
|
||||
import {ViewTypes} from '@constants';
|
||||
import {Sso} from '@constants';
|
||||
import emmProvider from '@init/emm_provider';
|
||||
import {getConfig} from '@mm-redux/selectors/entities/general';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {DispatchFunc} from '@mm-redux/types/actions';
|
||||
import {ErrorApi} from '@mm-redux/types/client4';
|
||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||
|
||||
import SSOWithRedirectURL from './sso_with_redirect_url';
|
||||
import SSOWithWebView from './sso_with_webview';
|
||||
|
|
@ -28,8 +26,7 @@ interface SSOProps {
|
|||
}
|
||||
|
||||
function SSO({intl, ssoType}: SSOProps) {
|
||||
const [config, serverUrl, theme] = useSelector((state: GlobalState) => ([
|
||||
getConfig(state),
|
||||
const [serverUrl, theme] = useSelector((state: GlobalState) => ([
|
||||
state.views.selectServer.serverUrl,
|
||||
getTheme(state),
|
||||
]), shallowEqual);
|
||||
|
|
@ -42,27 +39,27 @@ function SSO({intl, ssoType}: SSOProps) {
|
|||
let completeUrlPath = '';
|
||||
let loginUrl = '';
|
||||
switch (ssoType) {
|
||||
case ViewTypes.GOOGLE: {
|
||||
case Sso.GOOGLE: {
|
||||
completeUrlPath = '/signup/google/complete';
|
||||
loginUrl = `${serverUrl}/oauth/google/mobile_login`;
|
||||
break;
|
||||
}
|
||||
case ViewTypes.GITLAB: {
|
||||
case Sso.GITLAB: {
|
||||
completeUrlPath = '/signup/gitlab/complete';
|
||||
loginUrl = `${serverUrl}/oauth/gitlab/mobile_login`;
|
||||
break;
|
||||
}
|
||||
case ViewTypes.SAML: {
|
||||
case Sso.SAML: {
|
||||
completeUrlPath = '/login/sso/saml';
|
||||
loginUrl = `${serverUrl}/login/sso/saml?action=mobile`;
|
||||
break;
|
||||
}
|
||||
case ViewTypes.OFFICE365: {
|
||||
case Sso.OFFICE365: {
|
||||
completeUrlPath = '/signup/office365/complete';
|
||||
loginUrl = `${serverUrl}/oauth/office365/mobile_login`;
|
||||
break;
|
||||
}
|
||||
case ViewTypes.OPENID: {
|
||||
case Sso.OPENID: {
|
||||
completeUrlPath = '/signup/openid/complete';
|
||||
loginUrl = `${serverUrl}/oauth/openid/mobile_login`;
|
||||
break;
|
||||
|
|
@ -100,8 +97,6 @@ function SSO({intl, ssoType}: SSOProps) {
|
|||
dispatch(scheduleExpiredNotification(intl));
|
||||
};
|
||||
|
||||
const isSSOWithRedirectURLAvailable = isMinimumServerVersion(config.Version, 5, 33, 0);
|
||||
|
||||
const props = {
|
||||
intl,
|
||||
loginError,
|
||||
|
|
@ -112,7 +107,7 @@ function SSO({intl, ssoType}: SSOProps) {
|
|||
theme,
|
||||
};
|
||||
|
||||
if (!isSSOWithRedirectURLAvailable || emmProvider.inAppSessionAuth === true) {
|
||||
if (emmProvider.inAppSessionAuth === true) {
|
||||
return (
|
||||
<SSOWithWebView
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import merge from 'deepmerge';
|
||||
import React from 'react';
|
||||
import {Linking} from 'react-native';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
|
|
@ -19,38 +18,10 @@ describe('SSO', () => {
|
|||
},
|
||||
};
|
||||
|
||||
test('implement with webview when version is less than 5.32 version', async () => {
|
||||
const mockStore = configureMockStore();
|
||||
const store = mockStore(
|
||||
merge(initialState, {
|
||||
entities: {
|
||||
general: {
|
||||
config: {
|
||||
Version: '5.30.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const basicWrapper = renderWithReduxIntl(<SSOComponent {...baseProps}/>, store);
|
||||
expect(basicWrapper.queryByTestId('sso.webview')).toBeTruthy();
|
||||
expect(basicWrapper.queryByTestId('sso.redirect_url')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('implement with OS browser & redirect url from version 5.33', async () => {
|
||||
test('implement with OS browser & redirect url', async () => {
|
||||
(Linking.openURL as jest.Mock).mockResolvedValueOnce('');
|
||||
const mockStore = configureMockStore();
|
||||
const store = mockStore(
|
||||
merge(initialState, {
|
||||
entities: {
|
||||
general: {
|
||||
config: {
|
||||
Version: '5.33.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const store = mockStore(initialState);
|
||||
const basicWrapper = renderWithReduxIntl(<SSOComponent {...baseProps}/>, store);
|
||||
expect(basicWrapper.queryByTestId('sso.webview')).toBeFalsy();
|
||||
expect(basicWrapper.queryByTestId('sso.redirect_url')).toBeTruthy();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import urlParse from 'url-parse';
|
|||
import {setDeepLinkURL} from '@actions/views/root';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import Loading from '@components/loading';
|
||||
import {Sso} from '@constants';
|
||||
import {Theme} from '@mm-redux/types/theme';
|
||||
import Store from '@store/store';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
|
@ -39,9 +40,9 @@ function SSOWithRedirectURL({
|
|||
const [error, setError] = React.useState<string>('');
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
let customUrlScheme = 'mmauth://';
|
||||
let customUrlScheme = Sso.REDIRECT_URL_SCHEME;
|
||||
if (DeviceInfo.getBundleId && DeviceInfo.getBundleId().includes('rnbeta')) {
|
||||
customUrlScheme = 'mmauthbeta://';
|
||||
customUrlScheme = Sso.REDIRECT_URL_SCHEME_DEV;
|
||||
}
|
||||
|
||||
const redirectUrl = customUrlScheme + 'callback';
|
||||
|
|
@ -97,10 +98,10 @@ function SSOWithRedirectURL({
|
|||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
Linking.addEventListener('url', onURLChange);
|
||||
const listener = Linking.addEventListener('url', onURLChange);
|
||||
init(false);
|
||||
return () => {
|
||||
Linking.removeEventListener('url', onURLChange);
|
||||
listener.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
26
package-lock.json
generated
26
package-lock.json
generated
|
|
@ -6,7 +6,7 @@
|
|||
"packages": {
|
||||
"": {
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.50.0",
|
||||
"version": "1.50.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -16934,9 +16934,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
|
|
@ -18781,9 +18781,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/plist": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz",
|
||||
"integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"xmlbuilder": "^9.0.7"
|
||||
|
|
@ -36821,9 +36821,9 @@
|
|||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
|
|
@ -38218,9 +38218,9 @@
|
|||
}
|
||||
},
|
||||
"plist": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz",
|
||||
"integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==",
|
||||
"requires": {
|
||||
"base64-js": "^1.5.1",
|
||||
"xmlbuilder": "^9.0.7"
|
||||
|
|
|
|||
|
|
@ -101,7 +101,9 @@ jest.doMock('react-native', () => {
|
|||
|
||||
const Linking = {
|
||||
...RNLinking,
|
||||
openURL: jest.fn(),
|
||||
openURL: jest.fn().mockImplementation(
|
||||
() => Promise.resolve(''),
|
||||
),
|
||||
};
|
||||
|
||||
return Object.setPrototypeOf({
|
||||
|
|
|
|||
Loading…
Reference in a new issue