Commit 8616ba13 authored by Joram Wilander's avatar Joram Wilander Committed by Christopher Speller
Browse files

PLT-3633 EE: Add Google and Office365 SSO through OAuth2 (#3660)

* EE: Add Google and Office365 SSO through OAuth2

* Add localization strings

* Text tweaks for PLT-3633

* Added sign-up button for Office 365

* Updated some error messages and a bit of licensing

* Updated sign-in method section in user settings to include Google and Office365

* Added more localization strings
parent 0866ec90
......@@ -175,6 +175,7 @@ export default class AdminSidebar extends React.Component {
}
render() {
let oauthSettings = null;
let ldapSettings = null;
let samlSettings = null;
let complianceSettings = null;
......@@ -184,69 +185,93 @@ export default class AdminSidebar extends React.Component {
let policy = null;
if (window.mm_config.BuildEnterpriseReady === 'true') {
if (window.mm_license.IsLicensed === 'true') {
if (global.window.mm_license.LDAP === 'true') {
ldapSettings = (
<AdminSidebarSection
name='ldap'
title={
<FormattedMessage
id='admin.sidebar.ldap'
defaultMessage='LDAP'
/>
}
license = (
<AdminSidebarSection
name='license'
title={
<FormattedMessage
id='admin.sidebar.license'
defaultMessage='Edition and License'
/>
);
}
}
/>
);
}
if (global.window.mm_license.SAML === 'true') {
samlSettings = (
<AdminSidebarSection
name='saml'
title={
<FormattedMessage
id='admin.sidebar.saml'
defaultMessage='SAML'
/>
}
/>
);
}
if (window.mm_license.IsLicensed === 'true') {
if (global.window.mm_license.LDAP === 'true') {
ldapSettings = (
<AdminSidebarSection
name='ldap'
title={
<FormattedMessage
id='admin.sidebar.ldap'
defaultMessage='LDAP'
/>
}
/>
);
}
if (global.window.mm_license.Compliance === 'true') {
complianceSettings = (
<AdminSidebarSection
name='compliance'
title={
<FormattedMessage
id='admin.sidebar.compliance'
defaultMessage='Compliance'
/>
}
/>
);
}
if (global.window.mm_license.SAML === 'true') {
samlSettings = (
<AdminSidebarSection
name='saml'
title={
<FormattedMessage
id='admin.sidebar.saml'
defaultMessage='SAML'
/>
}
/>
);
}
policy = (
if (global.window.mm_license.Compliance === 'true') {
complianceSettings = (
<AdminSidebarSection
name='policy'
name='compliance'
title={
<FormattedMessage
id='admin.sidebar.policy'
defaultMessage='Policy'
id='admin.sidebar.compliance'
defaultMessage='Compliance'
/>
}
/>
);
}
license = (
oauthSettings = (
<AdminSidebarSection
name='license'
name='oauth'
title={
<FormattedMessage
id='admin.sidebar.license'
defaultMessage='Edition and License'
id='admin.sidebar.oauth'
defaultMessage='OAuth 2.0'
/>
}
/>
);
policy = (
<AdminSidebarSection
name='policy'
title={
<FormattedMessage
id='admin.sidebar.policy'
defaultMessage='Policy'
/>
}
/>
);
} else {
oauthSettings = (
<AdminSidebarSection
name='gitlab'
title={
<FormattedMessage
id='admin.sidebar.gitlab'
defaultMessage='GitLab'
/>
}
/>
......@@ -396,15 +421,7 @@ export default class AdminSidebar extends React.Component {
/>
}
/>
<AdminSidebarSection
name='gitlab'
title={
<FormattedMessage
id='admin.sidebar.gitlab'
defaultMessage='GitLab'
/>
}
/>
{oauthSettings}
{ldapSettings}
{samlSettings}
</AdminSidebarSection>
......
This diff is collapsed.
......@@ -328,6 +328,7 @@ export default class LoginController extends React.Component {
const ldapEnabled = this.state.ldapEnabled;
const gitlabSigninEnabled = global.window.mm_config.EnableSignUpWithGitLab === 'true';
const googleSigninEnabled = global.window.mm_config.EnableSignUpWithGoogle === 'true';
const office365SigninEnabled = global.window.mm_config.EnableSignUpWithOffice365 === 'true';
const samlSigninEnabled = this.state.samlEnabled;
const usernameSigninEnabled = this.state.usernameSigninEnabled;
const emailSigninEnabled = this.state.emailSigninEnabled;
......@@ -429,7 +430,7 @@ export default class LoginController extends React.Component {
);
}
if ((emailSigninEnabled || usernameSigninEnabled || ldapEnabled) && (gitlabSigninEnabled || googleSigninEnabled || samlSigninEnabled)) {
if ((emailSigninEnabled || usernameSigninEnabled || ldapEnabled) && (gitlabSigninEnabled || googleSigninEnabled || samlSigninEnabled || office365SigninEnabled)) {
loginControls.push(
<div
key='divider'
......@@ -472,10 +473,10 @@ export default class LoginController extends React.Component {
if (googleSigninEnabled) {
loginControls.push(
<Link
<a
className='btn btn-custom-login google'
key='google'
to={Client.getOAuthRoute() + '/google/login'}
href={Client.getOAuthRoute() + '/google/login' + this.props.location.search}
>
<span className='icon'/>
<span>
......@@ -484,7 +485,25 @@ export default class LoginController extends React.Component {
defaultMessage='Google Apps'
/>
</span>
</Link>
</a>
);
}
if (office365SigninEnabled) {
loginControls.push(
<a
className='btn btn-custom-login office365'
key='office365'
href={Client.getOAuthRoute() + '/office365/login' + this.props.location.search}
>
<span className='icon'/>
<span>
<FormattedMessage
id='login.office365'
defaultMessage='Office 365'
/>
</span>
</a>
);
}
......
......@@ -594,6 +594,24 @@ export default class SignupUserComplete extends React.Component {
);
}
if (global.window.mm_config.EnableSignUpWithOffice365 === 'true') {
signupMessage.push(
<a
className='btn btn-custom-login office365'
key='office365'
href={Client.getOAuthRoute() + '/office365/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
>
<span className='icon'/>
<span>
<FormattedMessage
id='signup_user_completed.office365'
defaultMessage='with Office 365'
/>
</span>
</a>
);
}
if (global.window.mm_config.EnableSaml === 'true' && global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.SAML === 'true') {
signupMessage.push(
<a
......
......@@ -419,6 +419,42 @@ class UserSettingsGeneralTab extends React.Component {
{helpText}
</div>
);
} else if (this.props.user.auth_service === Constants.GOOGLE_SERVICE) {
inputs.push(
<div
key='oauthEmailInfo'
className='form-group'
>
<div className='setting-list__hint'>
<FormattedMessage
id='user.settings.general.emailGoogleCantUpdate'
defaultMessage='Login occurs through Google Apps. Email cannot be updated. Email address used for notifications is {email}.'
values={{
email: this.state.email
}}
/>
</div>
{helpText}
</div>
);
} else if (this.props.user.auth_service === Constants.OFFICE365_SERVICE) {
inputs.push(
<div
key='oauthEmailInfo'
className='form-group'
>
<div className='setting-list__hint'>
<FormattedMessage
id='user.settings.general.emailOffice365CantUpdate'
defaultMessage='Login occurs through Office 365. Email cannot be updated. Email address used for notifications is {email}.'
values={{
email: this.state.email
}}
/>
</div>
{helpText}
</div>
);
} else if (this.props.user.auth_service === Constants.LDAP_SERVICE) {
inputs.push(
<div
......@@ -511,6 +547,26 @@ class UserSettingsGeneralTab extends React.Component {
}}
/>
);
} else if (this.props.user.auth_service === Constants.GOOGLE_SERVICE) {
describe = (
<FormattedMessage
id='user.settings.general.loginGoogle'
defaultMessage='Login done through Google Apps ({email})'
values={{
email: this.state.email
}}
/>
);
} else if (this.props.user.auth_service === Constants.OFFICE365_SERVICE) {
describe = (
<FormattedMessage
id='user.settings.general.loginOffice365'
defaultMessage='Login done through Office 365 ({email})'
values={{
email: this.state.email
}}
/>
);
} else if (this.props.user.auth_service === Constants.LDAP_SERVICE) {
describe = (
<FormattedMessage
......
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import $ from 'jquery';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import AccessHistoryModal from '../access_history_modal.jsx';
......@@ -9,13 +8,14 @@ import ActivityLogModal from '../activity_log_modal.jsx';
import ToggleModalButton from '../toggle_modal_button.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import Client from 'client/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import $ from 'jquery';
import React from 'react';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage, FormattedTime, FormattedDate} from 'react-intl';
import {Link} from 'react-router/es6';
......@@ -42,8 +42,6 @@ const holders = defineMessages({
}
});
import React from 'react';
class SecurityTab extends React.Component {
constructor(props) {
super(props);
......@@ -543,10 +541,99 @@ class SecurityTab extends React.Component {
const user = this.props.user;
if (this.props.activeSection === 'signin') {
const teamName = TeamStore.getCurrent().name;
let emailOption;
if (global.window.mm_config.EnableSignUpWithEmail === 'true' && user.auth_service !== '') {
let gitlabOption;
let googleOption;
let office365Option;
let ldapOption;
let samlOption;
if (user.auth_service === '') {
if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
gitlabOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchGitlab'
defaultMessage='Switch to using GitLab SSO'
/>
</Link>
<br/>
</div>
);
}
if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
googleOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GOOGLE_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchGoogle'
defaultMessage='Switch to using Google SSO'
/>
</Link>
<br/>
</div>
);
}
if (global.window.mm_config.EnableSignUpWithOffice365 === 'true') {
office365Option = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.OFFICE365_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchOffice365'
defaultMessage='Switch to using Office 365 SSO'
/>
</Link>
<br/>
</div>
);
}
if (global.window.mm_config.EnableLdap === 'true') {
ldapOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_ldap?email=' + encodeURIComponent(user.email)}
>
<FormattedMessage
id='user.settings.security.switchLdap'
defaultMessage='Switch to using LDAP'
/>
</Link>
<br/>
</div>
);
}
if (global.window.mm_config.EnableSaml === 'true') {
samlOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.SAML_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchSaml'
defaultMessage='Switch to using SAML SSO'
/>
</Link>
<br/>
</div>
);
}
} else if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
let link;
if (user.auth_service === Constants.LDAP_SERVICE) {
link = '/claim/ldap_to_email?email=' + encodeURIComponent(user.email);
......@@ -570,86 +657,15 @@ class SecurityTab extends React.Component {
);
}
let gitlabOption;
if (global.window.mm_config.EnableSignUpWithGitLab === 'true' && user.auth_service === '') {
gitlabOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchGitlab'
defaultMessage='Switch to using GitLab SSO'
/>
</Link>
<br/>
</div>
);
}
let googleOption;
if (global.window.mm_config.EnableSignUpWithGoogle === 'true' && user.auth_service === '') {
googleOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/' + teamName + '/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GOOGLE_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchGoogle'
defaultMessage='Switch to using Google SSO'
/>
</Link>
<br/>
</div>
);
}
let ldapOption;
if (global.window.mm_config.EnableLdap === 'true' && user.auth_service === '') {
ldapOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_ldap?email=' + encodeURIComponent(user.email)}
>
<FormattedMessage
id='user.settings.security.switchLdap'
defaultMessage='Switch to using LDAP'
/>
</Link>
<br/>
</div>
);
}
let samlOption;
if (global.window.mm_config.EnableSaml === 'true' && user.auth_service === '') {
samlOption = (
<div className='padding-bottom x2'>
<Link
className='btn btn-primary'
to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.SAML_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchSaml'
defaultMessage='Switch to using SAML SSO'
/>
</Link>
<br/>
</div>
);
}
const inputs = [];
inputs.push(
<div key='userSignInOption'>
{emailOption}
{gitlabOption}
{googleOption}
{office365Option}
{ldapOption}
{samlOption}
{googleOption}
</div>
);
......@@ -693,7 +709,21 @@ class SecurityTab extends React.Component {
describe = (
<FormattedMessage
id='user.settings.security.gitlab'
defaultMessage='GitLab SSO'
defaultMessage='GitLab'
/>
);
} else if (this.props.user.auth_service === Constants.GOOGLE_SERVICE) {
describe = (
<FormattedMessage
id='user.settings.security.google'
defaultMessage='Google'
/>
);
} else if (this.props.user.auth_service === Constants.OFFICE365_SERVICE) {
describe = (
<FormattedMessage
id='user.settings.security.office365'
defaultMessage='Office 365'
/>
);
} else if (this.props.user.auth_service === Constants.LDAP_SERVICE) {
......
......@@ -105,6 +105,7 @@
"admin.audits.title": "User Activity Logs",
"admin.authentication.email": "Email Auth",
"admin.authentication.gitlab": "GitLab",
"admin.authentication.oauth": "OAuth 2.0",
"admin.authentication.saml": "SAML",
"admin.banner.heading": "Note:",
"admin.compliance.directoryDescription": "Directory to which compliance reports are written. If blank, will be set to ./data/.",
......@@ -273,6 +274,31 @@
"admin.gitlab.userDescription": "Enter https://<your-gitlab-url>/api/v3/user. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.",
"admin.gitlab.userExample": "Ex \"https://<your-gitlab-url>/api/v3/user\"",
"admin.gitlab.userTitle": "User API Endpoint:",
"admin.google.EnableHtmlDesc": "<ol><li><a href='https://accounts.google.com/login'>Log in</a> to your Google account.</li><li>Go to <a href='https://console.developers.google.com'>https://console.developers.google.com</a>, click <strong>Credentials</strong> in the left hand sidebar and enter <strong>Mattermost - <your-company-name></strong> as the project name.</li><li>Click the <strong>OAuth consent screen</strong> header and enter <strong>Mattermost</strong> as the <strong>Product name to show users</strong>. Click <strong>Save</strong>.</li><li>Under the <strong>Credentials</strong> header, click <strong>Create credentials</strong>, choose <strong>OAuth client ID</strong> and select <strong>Web Application</strong>.</li><li>Under <strong>Restrictions</strong> and <strong>Authorized redirect URIs</strong> enter <strong><your-mattermost-url>/signup/google/complete</strong> (example: http://localhost:8065/signup/google/complete). Click <strong>Create</strong>.</li><li>Save the <strong>client ID</strong> and <strong>client secret</strong> to later complete the fields below.</li><li>Finally, go to <a href='https://console.developers.google.com/apis/api/plus/overview'>Google+ API</a> and click <strong>Enable</strong>. This might take a few minutes to propagate through Google's systems.</li><li>Complete the <strong>Client ID</strong> and <strong>Client Secret</strong> fields below.</li></ol>",
"admin.google.authTitle": "Auth Endpoint:",
"admin.google.clientIdDescription": "The Client ID you received when registering your application with Google.",
"admin.google.clientIdExample": "Ex \"7602141235235-url0fhs1mayfasbmop5qlfns8dh4.apps.googleusercontent.com\"",
"admin.google.clientIdTitle": "Client ID:",
"admin.google.clientSecretDescription": "The Client Secret you received when registering your application with Google.",
"admin.google.clientSecretExample": "Ex \"H8sz0Az-dDs2p15-7QzD231\"",
"admin.google.clientSecretTitle": "Client Secret:",
"admin.google.tokenTitle": "Token Endpoint:",
"admin.google.userTitle": "User API Endpoint:",