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
Этот коммит содержится в:
коммит произвёл
enahum
родитель
f0d71d8789
Коммит
30a10d35a8
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user