GitLab login (#552)
This commit is contained in:
parent
04d63a3e93
commit
009a07e83a
12 changed files with 101 additions and 28 deletions
|
|
@ -35,7 +35,10 @@ const ViewTypes = keyMirror({
|
|||
|
||||
SET_CHANNEL_LOADER: null,
|
||||
|
||||
SET_LAST_CHANNEL_FOR_TEAM: null
|
||||
SET_LAST_CHANNEL_FOR_TEAM: null,
|
||||
|
||||
GITLAB: null,
|
||||
SAML: null
|
||||
});
|
||||
|
||||
export default ViewTypes;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
import DeviceInfo from 'react-native-device-info';
|
||||
import semver from 'semver';
|
||||
|
||||
import {setAppState, setDeviceToken} from 'mattermost-redux/actions/general';
|
||||
import {setAppState, setDeviceToken, setServerVersion} from 'mattermost-redux/actions/general';
|
||||
import {logout} from 'mattermost-redux/actions/users';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
|
@ -64,14 +64,17 @@ export default class Mattermost {
|
|||
}]
|
||||
);
|
||||
} else {
|
||||
setServerVersion('')(dispatch, getState);
|
||||
loadConfigAndLicense(serverVersion)(dispatch, getState);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleReset = () => {
|
||||
const {dispatch, getState} = store;
|
||||
Client4.serverVersion = '';
|
||||
PushNotification.cancelAllLocalNotifications();
|
||||
setServerVersion('')(dispatch, getState);
|
||||
this.startApp('fade');
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
|||
export default function(state = '', action) {
|
||||
switch (action.type) {
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_RESET);
|
||||
setTimeout(() => {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_RESET);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import MoreDirectMessages from 'app/screens/more_dms';
|
|||
import Notification from 'app/screens/notification';
|
||||
import OptionsModal from 'app/screens/options_modal';
|
||||
import Root from 'app/screens/root';
|
||||
import Saml from 'app/screens/saml';
|
||||
import SSO from 'app/screens/sso';
|
||||
import SelectServer from 'app/screens/select_server';
|
||||
import SelectTeam from 'app/screens/select_team';
|
||||
import Settings from 'app/screens/settings';
|
||||
|
|
@ -63,10 +63,10 @@ export function registerScreens(store, Provider) {
|
|||
Navigation.registerComponent('OptionsModal', () => wrapWithContextProvider(OptionsModal), store, Provider);
|
||||
Navigation.registerComponent('Notification', () => Notification, store, Provider);
|
||||
Navigation.registerComponent('Root', () => Root, store, Provider);
|
||||
Navigation.registerComponent('SAML', () => wrapWithContextProvider(Saml), store, Provider);
|
||||
Navigation.registerComponent('SelectServer', () => wrapWithContextProvider(SelectServer), store, Provider);
|
||||
Navigation.registerComponent('SelectTeam', () => wrapWithContextProvider(SelectTeam), store, Provider);
|
||||
Navigation.registerComponent('Settings', () => wrapWithContextProvider(Settings), store, Provider);
|
||||
Navigation.registerComponent('SSO', () => wrapWithContextProvider(SSO), store, Provider);
|
||||
Navigation.registerComponent('Thread', () => wrapWithContextProvider(Thread), store, Provider);
|
||||
Navigation.registerComponent('UserProfile', () => wrapWithContextProvider(UserProfile), store, Provider);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ import {getTheme} from 'app/selectors/preferences';
|
|||
import LoginOptions from './login_options';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const {config, license} = state.entities.general;
|
||||
const {config, license, serverVersion} = state.entities.general;
|
||||
return {
|
||||
...ownProps,
|
||||
config,
|
||||
license,
|
||||
serverVersion,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,15 @@ import {
|
|||
Text,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import Button from 'react-native-button';
|
||||
import semver from 'semver';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
|
||||
import gitlab from 'assets/images/gitlab.png';
|
||||
import logo from 'assets/images/logo.png';
|
||||
|
||||
class LoginOptions extends PureComponent {
|
||||
|
|
@ -24,6 +27,7 @@ class LoginOptions extends PureComponent {
|
|||
navigator: PropTypes.object,
|
||||
config: PropTypes.object.isRequired,
|
||||
license: PropTypes.object.isRequired,
|
||||
serverVersion: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
|
|
@ -43,11 +47,11 @@ class LoginOptions extends PureComponent {
|
|||
});
|
||||
};
|
||||
|
||||
goToSaml = () => {
|
||||
goToSSO = (ssoType) => {
|
||||
const {intl, navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
screen: 'SAML',
|
||||
title: intl.formatMessage({id: 'mobile.routes.saml', defaultMessage: 'Single SignOn'}),
|
||||
screen: 'SSO',
|
||||
title: intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
|
|
@ -55,6 +59,9 @@ class LoginOptions extends PureComponent {
|
|||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
ssoType
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -80,13 +87,39 @@ class LoginOptions extends PureComponent {
|
|||
return null;
|
||||
};
|
||||
|
||||
renderGitlabOption = () => {
|
||||
const {config, serverVersion} = this.props;
|
||||
const version = serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g)[0];
|
||||
if (config.EnableSignUpWithGitLab === 'true' && semver.valid(version) && semver.gte(version, 'v3.10.0')) {
|
||||
return (
|
||||
<Button
|
||||
key='saml'
|
||||
onPress={() => preventDoubleTap(this.goToSSO, this, ViewTypes.GITLAB)}
|
||||
containerStyle={[GlobalStyles.signupButton, {backgroundColor: '#548'}]}
|
||||
>
|
||||
<Image
|
||||
source={gitlab}
|
||||
style={{height: 18, marginRight: 5, width: 18}}
|
||||
/>
|
||||
<Text
|
||||
style={[GlobalStyles.signupButtonText, {color: 'white'}]}
|
||||
>
|
||||
{'GitLab'}
|
||||
</Text>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
renderSamlOption = () => {
|
||||
const {config, license} = this.props;
|
||||
if (config.EnableSaml === 'true' && license.IsLicensed === 'true' && license.SAML === 'true') {
|
||||
return (
|
||||
<Button
|
||||
key='saml'
|
||||
onPress={() => preventDoubleTap(this.goToSaml, this)}
|
||||
onPress={() => preventDoubleTap(this.goToSSO, this, ViewTypes.SAML)}
|
||||
containerStyle={[GlobalStyles.signupButton, {backgroundColor: '#34a28b'}]}
|
||||
>
|
||||
<Text
|
||||
|
|
@ -122,6 +155,7 @@ class LoginOptions extends PureComponent {
|
|||
defaultMessage='Choose your login method'
|
||||
/>
|
||||
{this.renderEmailOption()}
|
||||
{this.renderGitlabOption()}
|
||||
{this.renderSamlOption()}
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import SelectServer from './select_server';
|
|||
|
||||
function mapStateToProps(state) {
|
||||
const {config: configRequest, license: licenseRequest, server: pingRequest} = state.requests.general;
|
||||
const {config, license} = state.entities.general;
|
||||
const {config, license, serverVersion} = state.entities.general;
|
||||
|
||||
const success = RequestStatus.SUCCESS;
|
||||
const transition = (pingRequest.status === success && configRequest.status === success && licenseRequest.status === success);
|
||||
|
|
@ -26,6 +26,7 @@ function mapStateToProps(state) {
|
|||
licenseRequest,
|
||||
config,
|
||||
license,
|
||||
serverVersion,
|
||||
transition,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import {
|
|||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import Button from 'react-native-button';
|
||||
import semver from 'semver';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
import {Client, Client4} from 'mattermost-redux/client';
|
||||
|
|
@ -43,6 +43,7 @@ class SelectServer extends PureComponent {
|
|||
pingRequest: PropTypes.object.isRequired,
|
||||
configRequest: PropTypes.object.isRequired,
|
||||
licenseRequest: PropTypes.object.isRequired,
|
||||
serverVersion: PropTypes.string.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
getPing: PropTypes.func.isRequired,
|
||||
resetPing: PropTypes.func.isRequired,
|
||||
|
|
@ -77,11 +78,13 @@ class SelectServer extends PureComponent {
|
|||
}
|
||||
|
||||
handleLoginOptions = () => {
|
||||
const {config, intl, license, theme} = this.props;
|
||||
const {config, intl, license, serverVersion, theme} = this.props;
|
||||
const version = serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g)[0];
|
||||
const samlEnabled = config.EnableSaml === 'true' && license.IsLicensed === 'true' && license.SAML === 'true';
|
||||
const gitlabEnabled = config.EnableSignUpWithGitLab === 'true' && semver.valid(version) && semver.gte(version, 'v3.10.0');
|
||||
|
||||
let options = 0;
|
||||
if (samlEnabled) {
|
||||
if (samlEnabled || gitlabEnabled) {
|
||||
options += 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {getTheme} from 'app/selectors/preferences';
|
|||
|
||||
import {setStoreFromLocalData} from 'mattermost-redux/actions/general';
|
||||
|
||||
import Saml from './saml';
|
||||
import SSO from './sso';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
|
|
@ -28,4 +28,4 @@ function mapDispatchToProps(dispatch) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Saml);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SSO);
|
||||
|
|
@ -15,18 +15,37 @@ import PushNotification from 'react-native-push-notification';
|
|||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
class Saml extends PureComponent {
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import Loading from 'app/components/loading';
|
||||
|
||||
class SSO extends PureComponent {
|
||||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object,
|
||||
serverUrl: PropTypes.string.isRequired,
|
||||
ssoType: PropTypes.string.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
handleSuccessfulLogin: PropTypes.func.isRequired,
|
||||
setStoreFromLocalData: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
switch (props.ssoType) {
|
||||
case ViewTypes.GITLAB:
|
||||
this.loginUrl = `${props.serverUrl}/oauth/gitlab/mobile_login`;
|
||||
this.completedUrl = `${props.serverUrl}/signup/gitlab/complete`;
|
||||
break;
|
||||
case ViewTypes.SAML:
|
||||
this.loginUrl = `${props.serverUrl}/login/sso/saml?action=mobile`;
|
||||
this.completedUrl = '/login/sso/saml';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.setState({renderWebview: true});
|
||||
|
|
@ -69,22 +88,21 @@ class Saml extends PureComponent {
|
|||
onNavigationStateChange = (navState) => {
|
||||
const {url} = navState;
|
||||
|
||||
if (url.includes('/login/sso/saml')) {
|
||||
if (url.includes(this.completedUrl)) {
|
||||
CookieManager.get(this.props.serverUrl, (err, res) => {
|
||||
const token = res.MMAUTHTOKEN;
|
||||
|
||||
if (token) {
|
||||
this.setState({renderWebview: false});
|
||||
const {
|
||||
handleSuccessfulLogin,
|
||||
setStoreFromLocalData
|
||||
} = this.props.actions;
|
||||
|
||||
Client4.setToken(token);
|
||||
handleSuccessfulLogin().
|
||||
then(async (expiresAt) => {
|
||||
await setStoreFromLocalData({url: this.props.serverUrl, token});
|
||||
this.goToLoadTeam(expiresAt);
|
||||
});
|
||||
setStoreFromLocalData({url: this.props.serverUrl, token}).
|
||||
then(handleSuccessfulLogin).
|
||||
then(this.goToLoadTeam);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -92,22 +110,30 @@ class Saml extends PureComponent {
|
|||
|
||||
render() {
|
||||
if (!this.state || !this.state.renderWebview) {
|
||||
return null;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<StatusBar barStyle='light-content'/>
|
||||
<Loading/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<StatusBar barStyle='light-content'/>
|
||||
<WebView
|
||||
source={{uri: `${this.props.serverUrl}/login/sso/saml?action=mobile`}}
|
||||
source={{uri: this.loginUrl}}
|
||||
javaScriptEnabledAndroid={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
scalesPageToFit={true}
|
||||
startInLoadingState={true}
|
||||
onNavigationStateChange={this.onNavigationStateChange}
|
||||
onShouldStartLoadWithRequest={() => true}
|
||||
renderLoading={() => (<Loading/>)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(Saml);
|
||||
export default injectIntl(SSO);
|
||||
|
|
@ -1736,7 +1736,7 @@
|
|||
"mobile.routes.loginOptions": "Login Chooser",
|
||||
"mobile.routes.mfa": "Multi-factor Authentication",
|
||||
"mobile.routes.postsList": "Posts List",
|
||||
"mobile.routes.saml": "Single SignOn",
|
||||
"mobile.routes.sso": "Single Sign-On",
|
||||
"mobile.routes.selectTeam": "Select Team",
|
||||
"mobile.routes.settings": "Settings",
|
||||
"mobile.routes.thread": "{channelName} Thread",
|
||||
|
|
|
|||
BIN
assets/base/images/gitlab.png
Normal file
BIN
assets/base/images/gitlab.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 665 B |
Loading…
Reference in a new issue