PLT-4767 Implement MFA Enforcement (#4662)

* Create MFA setup page and remove MFA setup from account settings modal

* Add enforce MFA to system console and force redirect

* Lockdown mfa required API routes, add localization, other changes

* Minor fixes

* Fix typo

* Fix some unit tests

* Fix more unit tests

* Minor fix

* Updating UI for MFA screen (#4670)

* Updating UI for MFA screen

* Updating styles for MFA page

* Add the ability to switch between email/sso with MFA enabled

* Added mfa change email

* Minor UI updates for MFA enforcement

* Fix unit test

* Fix client unit test

* Allow switching email to ldap and back when MFA is enabled

* Fix unit test

* Revert config.json
Этот коммит содержится в:
Joram Wilander
2016-12-12 08:16:10 -05:00
коммит произвёл enahum
родитель f0d71d8789
Коммит 30a10d35a8
34 изменённых файлов: 1001 добавлений и 231 удалений

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

@@ -1,11 +1,14 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import LoginMfa from 'components/login/components/login_mfa.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'client/web_client.jsx';
import {checkMfa} from 'actions/user_actions.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
export default class EmailToLDAP extends React.Component {
@@ -13,16 +16,20 @@ export default class EmailToLDAP extends React.Component {
super(props);
this.submit = this.submit.bind(this);
this.preSubmit = this.preSubmit.bind(this);
this.state = {
passwordError: '',
ldapError: '',
ldapPasswordError: '',
serverError: ''
serverError: '',
showMfa: false
};
}
submit(e) {
preSubmit(e) {
e.preventDefault();
var state = {
passwordError: '',
ldapError: '',
@@ -30,44 +37,65 @@ export default class EmailToLDAP extends React.Component {
serverError: ''
};
const password = ReactDOM.findDOMNode(this.refs.emailpassword).value;
const password = this.refs.emailpassword.value;
if (!password) {
state.passwordError = Utils.localizeMessage('claim.email_to_ldap.pwdError', 'Please enter your password.');
this.setState(state);
return;
}
const ldapId = ReactDOM.findDOMNode(this.refs.ldapid).value.trim();
const ldapId = this.refs.ldapid.value.trim();
if (!ldapId) {
state.ldapError = Utils.localizeMessage('claim.email_to_ldap.ldapIdError', 'Please enter your AD/LDAP ID.');
this.setState(state);
return;
}
const ldapPassword = ReactDOM.findDOMNode(this.refs.ldappassword).value;
const ldapPassword = this.refs.ldappassword.value;
if (!ldapPassword) {
state.ldapPasswordError = Utils.localizeMessage('claim.email_to_ldap.ldapPasswordError', 'Please enter your AD/LDAP password.');
this.setState(state);
return;
}
state.password = password;
state.ldapId = ldapId;
state.ldapPassword = ldapPassword;
this.setState(state);
Client.emailToLdap(
checkMfa(
this.props.email,
(requiresMfa) => {
if (requiresMfa) {
this.setState({showMfa: true});
} else {
this.submit(this.props.email, password, '', ldapId, ldapPassword);
}
},
(err) => {
this.setState({error: err.message});
}
);
}
submit(loginId, password, token, ldapId, ldapPassword) {
Client.emailToLdap(
loginId,
password,
ldapId,
ldapPassword,
token,
ldapId || this.state.ldapId,
ldapPassword || this.state.ldapPassword,
(data) => {
if (data.follow_link) {
window.location.href = data.follow_link;
}
},
(err) => {
this.setState({serverError: err.message});
this.setState({serverError: err.message, showMfa: false});
}
);
}
render() {
let serverError = null;
let formClass = 'form-group';
@@ -111,16 +139,19 @@ export default class EmailToLDAP extends React.Component {
passwordPlaceholder = Utils.localizeMessage('claim.email_to_ldap.ldapPwd', 'AD/LDAP Password');
}
return (
<div>
<h3>
<FormattedMessage
id='claim.email_to_ldap.title'
defaultMessage='Switch Email/Password Account to AD/LDAP'
/>
</h3>
let content;
if (this.state.showMfa) {
content = (
<LoginMfa
loginId={this.props.email}
password={this.state.password}
submit={this.submit}
/>
);
} else {
content = (
<form
onSubmit={this.submit}
onSubmit={this.preSubmit}
className={formClass}
>
<p>
@@ -202,6 +233,18 @@ export default class EmailToLDAP extends React.Component {
</button>
{serverError}
</form>
);
}
return (
<div>
<h3>
<FormattedMessage
id='claim.email_to_ldap.title'
defaultMessage='Switch Email/Password Account to AD/LDAP'
/>
</h3>
{content}
</div>
);
}

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

@@ -1,10 +1,14 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import LoginMfa from 'components/login/components/login_mfa.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'client/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {checkMfa} from 'actions/user_actions.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
@@ -14,10 +18,12 @@ export default class EmailToOAuth extends React.Component {
super(props);
this.submit = this.submit.bind(this);
this.preSubmit = this.preSubmit.bind(this);
this.state = {};
this.state = {showMfa: false, password: ''};
}
submit(e) {
preSubmit(e) {
e.preventDefault();
var state = {};
@@ -28,16 +34,18 @@ export default class EmailToOAuth extends React.Component {
return;
}
this.setState({password});
state.error = null;
this.setState(state);
Client.emailToOAuth(
checkMfa(
this.props.email,
password,
this.props.newType,
(data) => {
if (data.follow_link) {
window.location.href = data.follow_link;
(requiresMfa) => {
if (requiresMfa) {
this.setState({showMfa: true});
} else {
this.submit(this.props.email, password, '');
}
},
(err) => {
@@ -45,6 +53,24 @@ export default class EmailToOAuth extends React.Component {
}
);
}
submit(loginId, password, token) {
Client.emailToOAuth(
loginId,
password,
token,
this.props.newType,
(data) => {
if (data.follow_link) {
window.location.href = data.follow_link;
}
},
(err) => {
this.setState({error: err.message, showMfa: false});
}
);
}
render() {
var error = null;
if (this.state.error) {
@@ -59,18 +85,18 @@ export default class EmailToOAuth extends React.Component {
const type = (this.props.newType === Constants.SAML_SERVICE ? Constants.SAML_SERVICE.toUpperCase() : Utils.toTitleCase(this.props.newType));
const uiType = `${type} SSO`;
return (
<div>
<h3>
<FormattedMessage
id='claim.email_to_oauth.title'
defaultMessage='Switch Email/Password Account to {uiType}'
values={{
uiType
}}
/>
</h3>
<form onSubmit={this.submit}>
let content;
if (this.state.showMfa) {
content = (
<LoginMfa
loginId={this.props.email}
password={this.state.password}
submit={this.submit}
/>
);
} else {
content = (
<form onSubmit={this.preSubmit}>
<p>
<FormattedMessage
id='claim.email_to_oauth.ssoType'
@@ -122,6 +148,21 @@ export default class EmailToOAuth extends React.Component {
/>
</button>
</form>
);
}
return (
<div>
<h3>
<FormattedMessage
id='claim.email_to_oauth.title'
defaultMessage='Switch Email/Password Account to {uiType}'
values={{
uiType
}}
/>
</h3>
{content}
</div>
);
}

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

@@ -1,9 +1,11 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import LoginMfa from 'components/login/components/login_mfa.jsx';
import * as Utils from 'utils/utils.jsx';
import {switchFromLdapToEmail} from 'actions/user_actions.jsx';
import {checkMfa, switchFromLdapToEmail} from 'actions/user_actions.jsx';
import React from 'react';
import {FormattedMessage} from 'react-intl';
@@ -13,6 +15,7 @@ export default class LDAPToEmail extends React.Component {
super(props);
this.submit = this.submit.bind(this);
this.preSubmit = this.preSubmit.bind(this);
this.state = {
passwordError: '',
@@ -22,8 +25,9 @@ export default class LDAPToEmail extends React.Component {
};
}
submit(e) {
preSubmit(e) {
e.preventDefault();
var state = {
passwordError: '',
confirmError: '',
@@ -60,14 +64,33 @@ export default class LDAPToEmail extends React.Component {
return;
}
state.password = password;
state.ldapPassword = ldapPassword;
this.setState(state);
checkMfa(
this.props.email,
(requiresMfa) => {
if (requiresMfa) {
this.setState({showMfa: true});
} else {
this.submit(this.props.email, password, '', ldapPassword);
}
},
(err) => {
this.setState({error: err.message});
}
);
}
submit(loginId, password, token, ldapPassword) {
switchFromLdapToEmail(
this.props.email,
password,
ldapPassword,
token,
ldapPassword || this.state.ldapPassword,
null,
(err) => this.setState({serverError: err.message})
(err) => this.setState({serverError: err.message, showMfa: false})
);
}
@@ -107,16 +130,19 @@ export default class LDAPToEmail extends React.Component {
passwordPlaceholder = Utils.localizeMessage('claim.ldap_to_email.ldapPwd', 'AD/LDAP Password');
}
return (
<div>
<h3>
<FormattedMessage
id='claim.ldap_to_email.title'
defaultMessage='Switch AD/LDAP Account to Email/Password'
/>
</h3>
let content;
if (this.state.showMfa) {
content = (
<LoginMfa
loginId={this.props.email}
password={this.state.password}
submit={this.submit}
/>
);
} else {
content = (
<form
onSubmit={this.submit}
onSubmit={this.preSubmit}
className={formClass}
>
<p>
@@ -194,6 +220,18 @@ export default class LDAPToEmail extends React.Component {
</button>
{serverError}
</form>
);
}
return (
<div>
<h3>
<FormattedMessage
id='claim.ldap_to_email.title'
defaultMessage='Switch AD/LDAP Account to Email/Password'
/>
</h3>
{content}
</div>
);
}