Этот коммит содержится в:
JoramWilander
2016-03-30 12:49:29 -04:00
родитель 2aa0d9b8fc
Коммит f9a3a4b394
49 изменённых файлов: 5845 добавлений и 361 удалений

Просмотреть файл

@@ -84,6 +84,7 @@ class ServiceSettings extends React.Component {
config.ServiceSettings.EnableDeveloper = ReactDOM.findDOMNode(this.refs.EnableDeveloper).checked;
config.ServiceSettings.EnableSecurityFixAlert = ReactDOM.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
config.ServiceSettings.EnableInsecureOutgoingConnections = ReactDOM.findDOMNode(this.refs.EnableInsecureOutgoingConnections).checked;
config.ServiceSettings.EnableMultifactorAuthentication = ReactDOM.findDOMNode(this.refs.EnableMultifactorAuthentication).checked;
config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked;
config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked;
@@ -173,6 +174,58 @@ class ServiceSettings extends React.Component {
saveClass = 'btn btn-primary';
}
let mfaSetting;
if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true') {
mfaSetting = (
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='EnableMultifactorAuthentication'
>
<FormattedMessage
id='admin.service.mfaTitle'
defaultMessage='Enable Multi-factor Authentication:'
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
<input
type='radio'
name='EnableMultifactorAuthentication'
value='true'
ref='EnableMultifactorAuthentication'
defaultChecked={this.props.config.ServiceSettings.EnableMultifactorAuthentication}
onChange={this.handleChange}
/>
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
type='radio'
name='EnableMultifactorAuthentication'
value='false'
defaultChecked={!this.props.config.ServiceSettings.EnableMultifactorAuthentication}
onChange={this.handleChange}
/>
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>
<FormattedMessage
id='admin.service.mfaDesc'
defaultMessage='When true, users will be given the option to add multi-factor authentication to their account. They will need a smartphone and an authenticator app such as Google Authenticator.'
/>
</p>
</div>
</div>
);
}
return (
<div className='wrapper--fixed'>
@@ -773,6 +826,8 @@ class ServiceSettings extends React.Component {
</div>
</div>
{mfaSetting}
<div className='form-group'>
<label
className='control-label col-sm-4'

Просмотреть файл

@@ -2,69 +2,40 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import UserStore from 'stores/user_store.jsx';
import {browserHistory} from 'react-router';
import Constants from 'utils/constants.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
var holders = defineMessages({
badTeam: {
id: 'login_email.badTeam',
defaultMessage: 'Bad team name'
},
emailReq: {
id: 'login_email.emailReq',
defaultMessage: 'An email is required'
},
pwdReq: {
id: 'login_email.pwdReq',
defaultMessage: 'A password is required'
},
email: {
id: 'login_email.email',
defaultMessage: 'Email'
},
pwd: {
id: 'login_email.pwd',
defaultMessage: 'Password'
}
});
import {FormattedMessage} from 'react-intl';
import React from 'react';
class LoginEmail extends React.Component {
export default class LoginEmail extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: ''
serverError: props.serverError
};
}
componentWillReceiveProps(nextProps) {
this.setState({serverError: nextProps.serverError});
}
handleSubmit(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
var state = {};
const name = this.props.teamName;
if (!name) {
state.serverError = formatMessage(holders.badTeam);
this.setState(state);
return;
}
const email = this.refs.email.value.trim();
if (!email) {
state.serverError = formatMessage(holders.emailReq);
state.serverError = Utils.localizeMessage('login_email.emailReq', 'An email is required');
this.setState(state);
return;
}
const password = this.refs.password.value.trim();
if (!password) {
state.serverError = formatMessage(holders.pwdReq);
state.serverError = Utils.localizeMessage('login_email.pwdReq', 'A password is required');
this.setState(state);
return;
}
@@ -72,21 +43,7 @@ class LoginEmail extends React.Component {
state.serverError = '';
this.setState(state);
Client.loginByEmail(name, email, password,
() => {
UserStore.setLastEmail(email);
browserHistory.push('/' + name + '/channels/town-square');
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
browserHistory.push('/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email));
return;
}
state.serverError = err.message;
this.valid = false;
this.setState(state);
}
);
this.props.submit(Constants.EMAIL_SERVICE, email, password);
}
render() {
let serverError;
@@ -110,7 +67,6 @@ class LoginEmail extends React.Component {
priorEmail = decodeURIComponent(emailParam);
}
const {formatMessage} = this.props.intl;
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
@@ -125,7 +81,7 @@ class LoginEmail extends React.Component {
name='email'
defaultValue={priorEmail}
ref='email'
placeholder={formatMessage(holders.email)}
placeholder={Utils.localizeMessage('login_email.email', 'Email')}
spellCheck='false'
/>
</div>
@@ -136,7 +92,7 @@ class LoginEmail extends React.Component {
className='form-control'
name='password'
ref='password'
placeholder={formatMessage(holders.pwd)}
placeholder={Utils.localizeMessage('login_email.pwd', 'Password')}
spellCheck='false'
/>
</div>
@@ -160,8 +116,6 @@ LoginEmail.defaultProps = {
};
LoginEmail.propTypes = {
intl: intlShape.isRequired,
teamName: React.PropTypes.string.isRequired
submit: React.PropTypes.func.isRequired,
serverError: React.PropTypes.string
};
export default injectIntl(LoginEmail);

Просмотреть файл

@@ -2,68 +2,39 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Constants from 'utils/constants.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
const holders = defineMessages({
badTeam: {
id: 'login_ldap.badTeam',
defaultMessage: 'Bad team name'
},
idReq: {
id: 'login_ldap.idlReq',
defaultMessage: 'An LDAP ID is required'
},
pwdReq: {
id: 'login_ldap.pwdReq',
defaultMessage: 'An LDAP password is required'
},
username: {
id: 'login_ldap.username',
defaultMessage: 'LDAP Username'
},
pwd: {
id: 'login_ldap.pwd',
defaultMessage: 'LDAP Password'
}
});
import {FormattedMessage} from 'react-intl';
import React from 'react';
class LoginLdap extends React.Component {
export default class LoginLdap extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: ''
serverError: props.serverError
};
}
componentWillReceiveProps(nextProps) {
this.setState({serverError: nextProps.serverError});
}
handleSubmit(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
var state = {};
const teamName = this.props.teamName;
if (!teamName) {
state.serverError = formatMessage(holders.badTeam);
this.setState(state);
return;
}
const state = {};
const id = this.refs.id.value.trim();
if (!id) {
state.serverError = formatMessage(holders.idReq);
state.serverError = Utils.localizeMessage('login_ldap.idlReq', 'An LDAP ID is required');
this.setState(state);
return;
}
const password = this.refs.password.value.trim();
if (!password) {
state.serverError = formatMessage(holders.pwdReq);
state.serverError = Utils.localizeMessage('login_ldap.pwdReq', 'An LDAP password is required');
this.setState(state);
return;
}
@@ -71,20 +42,7 @@ class LoginLdap extends React.Component {
state.serverError = '';
this.setState(state);
Client.loginByLdap(teamName, id, password,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + teamName + '/channels/town-square');
}
},
(err) => {
state.serverError = err.message;
this.setState(state);
}
);
this.props.submit(Constants.LDAP_SERVICE, id, password);
}
render() {
let serverError;
@@ -93,7 +51,7 @@ class LoginLdap extends React.Component {
serverError = <label className='control-label'>{this.state.serverError}</label>;
errorClass = ' has-error';
}
const {formatMessage} = this.props.intl;
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
@@ -105,7 +63,7 @@ class LoginLdap extends React.Component {
autoFocus={true}
className='form-control'
ref='id'
placeholder={formatMessage(holders.username)}
placeholder={Utils.localizeMessage('login_ldap.username', 'LDAP Username')}
spellCheck='false'
/>
</div>
@@ -114,7 +72,7 @@ class LoginLdap extends React.Component {
type='password'
className='form-control'
ref='password'
placeholder={formatMessage(holders.pwd)}
placeholder={Utils.localizeMessage('login_ldap.pwd', 'LDAP Password')}
spellCheck='false'
/>
</div>
@@ -138,8 +96,6 @@ LoginLdap.defaultProps = {
};
LoginLdap.propTypes = {
intl: intlShape.isRequired,
teamName: React.PropTypes.string.isRequired
serverError: React.PropTypes.string,
submit: React.PropTypes.func.isRequired
};
export default injectIntl(LoginLdap);

Просмотреть файл

@@ -0,0 +1,92 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react';
export default class LoginMfa extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: ''
};
}
handleSubmit(e) {
e.preventDefault();
const state = {};
const token = this.refs.token.value.trim();
if (!token) {
state.serverError = Utils.localizeMessage('login_mfa.tokenReq', 'Please enter an MFA token');
this.setState(state);
return;
}
state.serverError = '';
this.setState(state);
this.props.submit(this.props.method, this.props.loginId, this.props.password, token);
}
render() {
let serverError;
let errorClass = '';
if (this.state.serverError) {
serverError = <label className='control-label'>{this.state.serverError}</label>;
errorClass = ' has-error';
}
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
<p>
<FormattedMessage
id='login_mfa.enterToken'
defaultMessage="To complete the sign in process, please enter a token from your smartphone's authenticator"
/>
</p>
<div className={'form-group' + errorClass}>
{serverError}
</div>
<div className={'form-group' + errorClass}>
<input
type='text'
className='form-control'
name='token'
ref='token'
placeholder={Utils.localizeMessage('login_mfa.token', 'MFA Token')}
spellCheck='false'
autoComplete='off'
autoFocus={true}
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary'
>
<FormattedMessage
id='login_mfa.submit'
defaultMessage='Submit'
/>
</button>
</div>
</div>
</form>
);
}
}
LoginMfa.defaultProps = {
};
LoginMfa.propTypes = {
method: React.PropTypes.string.isRequired,
loginId: React.PropTypes.string.isRequired,
password: React.PropTypes.string.isRequired,
submit: React.PropTypes.func.isRequired
};

Просмотреть файл

@@ -2,42 +2,10 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
var holders = defineMessages({
badTeam: {
id: 'login_username.badTeam',
defaultMessage: 'Bad team name'
},
usernameReq: {
id: 'login_username.usernameReq',
defaultMessage: 'A username is required'
},
pwdReq: {
id: 'login_username.pwdReq',
defaultMessage: 'A password is required'
},
verifyEmailError: {
id: 'login_username.verifyEmailError',
defaultMessage: 'Please verify your email address. Check your inbox for an email.'
},
userNotFoundError: {
id: 'login_username.userNotFoundError',
defaultMessage: "We couldn't find an existing account matching your username for this team."
},
username: {
id: 'login_username.username',
defaultMessage: 'Username'
},
pwd: {
id: 'login_username.pwd',
defaultMessage: 'Password'
}
});
import {FormattedMessage} from 'react-intl';
import React from 'react';
@@ -48,31 +16,26 @@ export default class LoginUsername extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: ''
serverError: props.serverError
};
}
componentWillReceiveProps(nextProps) {
this.setState({serverError: nextProps.serverError});
}
handleSubmit(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
var state = {};
const name = this.props.teamName;
if (!name) {
state.serverError = formatMessage(holders.badTeam);
this.setState(state);
return;
}
const state = {};
const username = this.refs.username.value.trim();
if (!username) {
state.serverError = formatMessage(holders.usernameReq);
state.serverError = Utils.localizeMessage('login_username.usernameReq', 'A username is required');
this.setState(state);
return;
}
const password = this.refs.password.value.trim();
if (!password) {
state.serverError = formatMessage(holders.pwdReq);
state.serverError = Utils.localizeMessage('login_username.pwdReq', 'A password is required');
this.setState(state);
return;
}
@@ -80,30 +43,7 @@ export default class LoginUsername extends React.Component {
state.serverError = '';
this.setState(state);
Client.loginByUsername(name, username, password,
() => {
UserStore.setLastUsername(username);
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + name + '/channels/town-square');
}
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
state.serverError = formatMessage(holders.verifyEmailError);
} else if (err.id === 'store.sql_user.get_by_username.app_error') {
state.serverError = formatMessage(holders.userNotFoundError);
} else {
state.serverError = err.message;
}
this.valid = false;
this.setState(state);
}
);
this.props.submit(Constants.USERNAME_SERVICE, username, password);
}
render() {
let serverError;
@@ -127,7 +67,6 @@ export default class LoginUsername extends React.Component {
priorUsername = decodeURIComponent(emailParam);
}
const {formatMessage} = this.props.intl;
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
@@ -142,7 +81,7 @@ export default class LoginUsername extends React.Component {
name='username'
defaultValue={priorUsername}
ref='username'
placeholder={formatMessage(holders.username)}
placeholder={Utils.localizeMessage('login_username.username', 'Username')}
spellCheck='false'
/>
</div>
@@ -153,7 +92,7 @@ export default class LoginUsername extends React.Component {
className='form-control'
name='password'
ref='password'
placeholder={formatMessage(holders.pwd)}
placeholder={Utils.localizeMessage('login_username.pwd', 'Password')}
spellCheck='false'
/>
</div>
@@ -177,8 +116,6 @@ LoginUsername.defaultProps = {
};
LoginUsername.propTypes = {
intl: intlShape.isRequired,
teamName: React.PropTypes.string.isRequired
serverError: React.PropTypes.string,
submit: React.PropTypes.func.isRequired
};
export default injectIntl(LoginUsername);

Просмотреть файл

@@ -1,14 +1,17 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import LoginEmail from './login_email.jsx';
import LoginUsername from './login_username.jsx';
import LoginLdap from './login_ldap.jsx';
import LoginEmail from './components/login_email.jsx';
import LoginUsername from './components/login_username.jsx';
import LoginLdap from './components/login_ldap.jsx';
import LoginMfa from './components/login_mfa.jsx';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Constants from 'utils/constants.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Client from 'utils/client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
import {browserHistory, Link} from 'react-router';
@@ -21,6 +24,8 @@ export default class Login extends React.Component {
this.getStateFromStores = this.getStateFromStores.bind(this);
this.onTeamChange = this.onTeamChange.bind(this);
this.preSubmit = this.preSubmit.bind(this);
this.submit = this.submit.bind(this);
this.state = this.getStateFromStores();
}
@@ -46,54 +51,89 @@ export default class Login extends React.Component {
onTeamChange() {
this.setState(this.getStateFromStores());
}
render() {
const currentTeam = this.state.currentTeam;
if (currentTeam == null || !this.state.doneCheckLogin) {
return <div/>;
preSubmit(method, loginId, password) {
if (global.window.mm_config.EnableMultifactorAuthentication !== 'true') {
this.submit(method, loginId, password, '');
return;
}
const teamDisplayName = currentTeam.display_name;
const teamName = currentTeam.name;
const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
const usernameSigninEnabled = global.window.mm_config.EnableSignInWithUsername === 'true';
Client.checkMfa(method, this.state.currentTeam.name, loginId,
(data) => {
if (data.mfa_required === 'true') {
this.setState({showMfa: true, method, loginId, password});
} else {
this.submit(method, loginId, password, '');
}
},
(err) => {
if (method === Constants.EMAIL_SERVICE) {
this.setState({serverEmailError: err.message});
} else if (method === Constants.USERNAME_SERVICE) {
this.setState({serverUsernameError: err.message});
} else if (method === Constants.LDAP_SERVICE) {
this.setState({serverLdapError: err.message});
}
}
);
}
submit(method, loginId, password, token) {
this.setState({showMfa: false, serverEmailError: null, serverUsernameError: null, serverLdapError: null});
let loginMessage = [];
if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
loginMessage.push(
<a
className='btn btn-custom-login gitlab'
key='gitlab'
href={'/api/v1/oauth/gitlab/login?team=' + encodeURIComponent(teamName)}
>
<span className='icon'/>
<span>
<FormattedMessage
id='login.gitlab'
defaultMessage='with GitLab'
/>
</span>
</a>
const team = this.state.currentTeam.name;
if (method === Constants.EMAIL_SERVICE) {
Client.loginByEmail(team, loginId, password, token,
() => {
UserStore.setLastEmail(loginId);
browserHistory.push('/' + team + '/channels/town-square');
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
browserHistory.push('/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(loginId));
return;
}
this.setState({serverEmailError: err.message});
}
);
} else if (method === Constants.USERNAME_SERVICE) {
Client.loginByUsername(team, loginId, password, token,
() => {
UserStore.setLastUsername(loginId);
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + team + '/channels/town-square');
}
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
this.setState({serverUsernameError: Utils.localizeMessage('login_username.verifyEmailError', 'Please verify your email address. Check your inbox for an email.')});
} else if (err.id === 'store.sql_user.get_by_username.app_error') {
this.setState({serverUsernameError: Utils.localizeMessage('login_username.userNotFoundError', 'We couldn\'t find an existing account matching your username for this team.')});
} else {
this.setState({serverUsernameError: err.message});
}
}
);
} else if (method === Constants.LDAP_SERVICE) {
Client.loginByLdap(team, loginId, password, token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + team + '/channels/town-square');
}
},
(err) => {
this.setState({serverLdapError: err.message});
}
);
}
if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
loginMessage.push(
<a
className='btn btn-custom-login google'
key='google'
href={'/api/v1/oauth/google/login?team=' + encodeURIComponent(teamName)}
>
<span className='icon'/>
<span>
<FormattedMessage
id='login.google'
defaultMessage='with Google Apps'
/>
</span>
</a>
);
}
}
createLoginOptions(currentTeam) {
const extraParam = Utils.getUrlParameter('extra');
let extraBox = '';
if (extraParam) {
@@ -130,44 +170,126 @@ export default class Login extends React.Component {
}
}
let emailSignup;
if (global.window.mm_config.EnableSignInWithEmail === 'true') {
emailSignup = (
const teamName = currentTeam.name;
const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
const gitlabSigninEnabled = global.window.mm_config.EnableSignUpWithGitLab === 'true';
const googleSigninEnabled = global.window.mm_config.EnableSignUpWithGoogle === 'true';
const usernameSigninEnabled = global.window.mm_config.EnableSignInWithUsername === 'true';
const emailSigninEnabled = global.window.mm_config.EnableSignInWithEmail === 'true';
const oauthLogins = [];
if (gitlabSigninEnabled) {
oauthLogins.push(
<Link
className='btn btn-custom-login gitlab'
key='gitlab'
to={'/api/v1/oauth/gitlab/login?team=' + encodeURIComponent(teamName)}
>
<span className='icon'/>
<span>
<FormattedMessage
id='login.gitlab'
defaultMessage='with GitLab'
/>
</span>
</Link>
);
}
if (googleSigninEnabled) {
oauthLogins.push(
<Link
className='btn btn-custom-login google'
key='google'
to={'/api/v1/oauth/google/login?team=' + encodeURIComponent(teamName)}
>
<span className='icon'/>
<span>
<FormattedMessage
id='login.google'
defaultMessage='with Google Apps'
/>
</span>
</Link>
);
}
let emailLogin;
if (emailSigninEnabled) {
emailLogin = (
<LoginEmail
teamName={teamName}
serverError={this.state.serverEmailError}
submit={this.preSubmit}
/>
);
}
if (loginMessage.length > 0 && emailSignup) {
loginMessage = (
<div>
{loginMessage}
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
if (oauthLogins.length > 0) {
emailLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
{emailLogin}
</div>
</div>
);
);
}
}
let forgotPassword;
if (emailSignup) {
forgotPassword = (
<div className='form-group'>
<Link to={'/' + teamName + '/reset_password'}>
<FormattedMessage
id='login.forgot'
defaultMessage='I forgot my password'
/>
</Link>
</div>
let usernameLogin;
if (usernameSigninEnabled) {
usernameLogin = (
<LoginUsername
teamName={teamName}
serverError={this.state.serverUsernameError}
submit={this.preSubmit}
/>
);
if (emailSigninEnabled || oauthLogins.length > 0) {
usernameLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
{usernameLogin}
</div>
);
}
}
let userSignUp = null;
let ldapLogin;
if (ldapEnabled) {
ldapLogin = (
<LoginLdap
teamName={teamName}
serverError={this.state.serverLdapError}
submit={this.preSubmit}
/>
);
if (emailSigninEnabled || usernameSigninEnabled || oauthLogins.length > 0) {
ldapLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
{ldapLogin}
</div>
);
}
}
let userSignUp;
if (currentTeam.allow_open_invite) {
userSignUp = (
<div>
@@ -190,7 +312,21 @@ export default class Login extends React.Component {
);
}
let teamSignUp = null;
let forgotPassword;
if (usernameSigninEnabled || emailSigninEnabled) {
forgotPassword = (
<div className='form-group'>
<Link to={'/' + teamName + '/reset_password'}>
<FormattedMessage
id='login.forgot'
defaultMessage='I forgot my password'
/>
</Link>
</div>
);
}
let teamSignUp;
if (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp()) {
teamSignUp = (
<div className='margin--extra'>
@@ -207,54 +343,37 @@ export default class Login extends React.Component {
);
}
let ldapLogin = null;
if (global.window.mm_config.EnableLdap === 'true') {
ldapLogin = (
<LoginLdap
teamName={teamName}
return (
<div>
{extraBox}
{oauthLogins}
{emailLogin}
{usernameLogin}
{ldapLogin}
{userSignUp}
{forgotPassword}
{teamSignUp}
</div>
);
}
render() {
const currentTeam = this.state.currentTeam;
if (currentTeam == null || !this.state.doneCheckLogin) {
return <div/>;
}
let content;
if (this.state.showMfa) {
content = (
<LoginMfa
method={this.state.method}
loginId={this.state.loginId}
password={this.state.password}
submit={this.submit}
/>
);
}
if (ldapEnabled && (loginMessage.length > 0 || emailSignup || usernameSigninEnabled)) {
ldapLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
<LoginLdap
teamName={teamName}
/>
</div>
);
}
let usernameLogin = null;
if (global.window.mm_config.EnableSignInWithUsername === 'true') {
usernameLogin = (
<LoginUsername
teamName={teamName}
/>
);
}
if (usernameSigninEnabled && (loginMessage.length > 0 || emailSignup || ldapEnabled)) {
usernameLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
<LoginUsername
teamName={teamName}
/>
</div>
);
} else {
content = this.createLoginOptions(currentTeam);
}
return (
@@ -275,7 +394,7 @@ export default class Login extends React.Component {
defaultMessage='Sign in to:'
/>
</h5>
<h2 className='signup-team__name'>{teamDisplayName}</h2>
<h2 className='signup-team__name'>{currentTeam.display_name}</h2>
<h2 className='signup-team__subdomain'>
<FormattedMessage
id='login.on'
@@ -285,14 +404,7 @@ export default class Login extends React.Component {
}}
/>
</h2>
{extraBox}
{loginMessage}
{emailSignup}
{usernameLogin}
{ldapLogin}
{userSignUp}
{forgotPassword}
{teamSignUp}
{content}
</div>
</div>
</div>

Просмотреть файл

@@ -2,7 +2,7 @@
// See License.txt for license information.
import LoadingScreen from 'components/loading_screen.jsx';
import LoginLdap from 'components/login_ldap.jsx';
import LoginLdap from 'components/login/components/login_ldap.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import UserStore from 'stores/user_store.jsx';

Просмотреть файл

@@ -47,12 +47,16 @@ class SecurityTab extends React.Component {
super(props);
this.submitPassword = this.submitPassword.bind(this);
this.activateMfa = this.activateMfa.bind(this);
this.deactivateMfa = this.deactivateMfa.bind(this);
this.updateCurrentPassword = this.updateCurrentPassword.bind(this);
this.updateNewPassword = this.updateNewPassword.bind(this);
this.updateConfirmPassword = this.updateConfirmPassword.bind(this);
this.updateMfaToken = this.updateMfaToken.bind(this);
this.getDefaultState = this.getDefaultState.bind(this);
this.createPasswordSection = this.createPasswordSection.bind(this);
this.createSignInSection = this.createSignInSection.bind(this);
this.showQrCode = this.showQrCode.bind(this);
this.state = this.getDefaultState();
}
@@ -61,7 +65,9 @@ class SecurityTab extends React.Component {
currentPassword: '',
newPassword: '',
confirmPassword: '',
authService: this.props.user.auth_service
authService: this.props.user.auth_service,
mfaShowQr: false,
mfaToken: ''
};
}
submitPassword(e) {
@@ -112,6 +118,51 @@ class SecurityTab extends React.Component {
}
);
}
activateMfa() {
const data = {};
data.activate = true;
data.token = this.state.mfaToken;
Client.updateMfa(data,
() => {
this.props.updateSection('');
AsyncClient.getMe();
this.setState(this.getDefaultState());
},
(err) => {
const state = this.getDefaultState();
if (err.message) {
state.serverError = err.message;
} else {
state.serverError = err;
}
state.mfaError = '';
this.setState(state);
}
);
}
deactivateMfa() {
const data = {};
data.activate = false;
Client.updateMfa(data,
() => {
this.props.updateSection('');
AsyncClient.getMe();
this.setState(this.getDefaultState());
},
(err) => {
const state = this.getDefaultState();
if (err.message) {
state.serverError = err.message;
} else {
state.serverError = err;
}
state.mfaError = '';
this.setState(state);
}
);
}
updateCurrentPassword(e) {
this.setState({currentPassword: e.target.value});
}
@@ -121,6 +172,163 @@ class SecurityTab extends React.Component {
updateConfirmPassword(e) {
this.setState({confirmPassword: e.target.value});
}
updateMfaToken(e) {
this.setState({mfaToken: e.target.value});
}
showQrCode(e) {
e.preventDefault();
this.setState({mfaShowQr: true});
}
createMfaSection() {
let updateSectionStatus;
let submit;
if (this.props.activeSection === 'mfa') {
let content;
let extraInfo;
if (this.props.user.mfa_active) {
content = (
<div key='mfaQrCode'>
<a
className='btn btn-primary'
href='#'
onClick={this.deactivateMfa}
>
<FormattedMessage
id='user.settings.mfa.remove'
defaultMessage='Remove MFA from your account'
/>
</a>
<br/>
</div>
);
extraInfo = (
<span>
<FormattedMessage
id='user.settings.mfa.removeHelp'
defaultMessage='Removing multi-factor authentication will make your account more vulnerable to attacks.'
/>
</span>
);
} else if (this.state.mfaShowQr) {
content = (
<div key='mfaButton'>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='user.settings.mfa.qrCode'
defaultMessage='QR Code'
/>
</label>
<div className='col-sm-7'>
<img
className='qr-code-img'
src={'/api/v1/users/generate_mfa_qr?time=' + this.props.user.update_at}
/>
</div>
<br/>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='user.settings.mfa.enterToken'
defaultMessage='Token'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
type='text'
onChange={this.updateMfaToken}
value={this.state.mfaToken}
/>
</div>
</div>
);
extraInfo = (
<span>
<FormattedMessage
id='user.settings.mfa.addHelp'
defaultMessage='Please scan the QR code with the Google Authenticator app on your smartphone and fill in the token with one provided by the app.'
/>
</span>
);
submit = this.activateMfa;
} else {
content = (
<div key='mfaQrCode'>
<a
className='btn btn-primary'
href='#'
onClick={this.showQrCode}
>
<FormattedMessage
id='user.settings.mfa.add'
defaultMessage='Add MFA to your account'
/>
</a>
<br/>
</div>
);
extraInfo = (
<span>
<FormattedMessage
id='user.settings.mfa.addHelp'
defaultMessage='To add multi-factor authentication to your account you must have a smartphone with Google Authenticator installed.'
/>
</span>
);
}
const inputs = [];
inputs.push(
<div
key='mfaSetting'
className='form-group'
>
{content}
</div>
);
updateSectionStatus = function resetSection(e) {
this.props.updateSection('');
this.setState({mfaToken: '', mfaShowQr: false, mfaError: null});
e.preventDefault();
}.bind(this);
return (
<SettingItemMax
title={Utils.localizeMessage('user.settings.mfa.title', 'Multi-factor Authentication')}
inputs={inputs}
extraInfo={extraInfo}
submit={submit}
server_error={this.state.serverError}
client_error={this.state.mfaError}
updateSection={updateSectionStatus}
/>
);
}
let describe;
if (this.props.user.mfa_active) {
describe = Utils.localizeMessage('user.settings.security.active', 'Active');
} else {
describe = Utils.localizeMessage('user.settings.security.inactive', 'Inactive');
}
updateSectionStatus = function updateSection() {
this.props.updateSection('mfa');
}.bind(this);
return (
<SettingItemMin
title={Utils.localizeMessage('user.settings.mfa.title', 'Multi-factor Authentication')}
describe={describe}
updateSection={updateSectionStatus}
/>
);
}
createPasswordSection() {
let updateSectionStatus;
@@ -316,7 +524,6 @@ class SecurityTab extends React.Component {
const user = this.props.user;
if (this.props.activeSection === 'signin') {
const inputs = [];
const teamName = TeamStore.getCurrent().name;
let emailOption;
@@ -398,6 +605,7 @@ class SecurityTab extends React.Component {
);
}
const inputs = [];
inputs.push(
<div key='userSignInOption'>
{emailOption}
@@ -463,17 +671,22 @@ class SecurityTab extends React.Component {
}
render() {
const passwordSection = this.createPasswordSection();
let signInSection;
let numMethods = 0;
numMethods = global.window.mm_config.EnableSignUpWithGitLab === 'true' ? numMethods + 1 : numMethods;
numMethods = global.window.mm_config.EnableSignUpWithGoogle === 'true' ? numMethods + 1 : numMethods;
numMethods = global.window.mm_config.EnableLdap === 'true' ? numMethods + 1 : numMethods;
if (global.window.mm_config.EnableSignUpWithEmail && numMethods > 0) {
let signInSection;
if (global.window.mm_config.EnableSignUpWithEmail === 'true' && numMethods > 0) {
signInSection = this.createSignInSection();
}
let mfaSection;
if (global.window.mm_config.EnableMultifactorAuthentication === 'true' && global.window.mm_license.IsLicensed === 'true') {
mfaSection = this.createMfaSection();
}
return (
<div>
<div className='modal-header'>
@@ -512,6 +725,8 @@ class SecurityTab extends React.Component {
<div className='divider-dark first'/>
{passwordSection}
<div className='divider-light'/>
{mfaSection}
<div className='divider-light'/>
{signInSection}
<div className='divider-dark'/>
<br></br>