From 30a10d35a8406f4af96fcc8200c4e2173856837d Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 12 Dec 2016 08:16:10 -0500 Subject: 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 --- .../components/claim/components/email_to_ldap.jsx | 81 +++++++++++++++++----- .../components/claim/components/email_to_oauth.jsx | 73 ++++++++++++++----- .../components/claim/components/ldap_to_email.jsx | 64 +++++++++++++---- 3 files changed, 170 insertions(+), 48 deletions(-) (limited to 'webapp/components/claim') diff --git a/webapp/components/claim/components/email_to_ldap.jsx b/webapp/components/claim/components/email_to_ldap.jsx index a0b0b10e9..890512803 100644 --- a/webapp/components/claim/components/email_to_ldap.jsx +++ b/webapp/components/claim/components/email_to_ldap.jsx @@ -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 ( -
-

- -

+ let content; + if (this.state.showMfa) { + content = ( + + ); + } else { + content = (

@@ -202,6 +233,18 @@ export default class EmailToLDAP extends React.Component { {serverError}

+ ); + } + + return ( +
+

+ +

+ {content}
); } diff --git a/webapp/components/claim/components/email_to_oauth.jsx b/webapp/components/claim/components/email_to_oauth.jsx index d7c4956a6..3cede15a3 100644 --- a/webapp/components/claim/components/email_to_oauth.jsx +++ b/webapp/components/claim/components/email_to_oauth.jsx @@ -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,12 +34,31 @@ export default class EmailToOAuth extends React.Component { return; } + this.setState({password}); + state.error = null; this.setState(state); - Client.emailToOAuth( + checkMfa( this.props.email, + (requiresMfa) => { + if (requiresMfa) { + this.setState({showMfa: true}); + } else { + this.submit(this.props.email, password, ''); + } + }, + (err) => { + this.setState({error: err.message}); + } + ); + } + + submit(loginId, password, token) { + Client.emailToOAuth( + loginId, password, + token, this.props.newType, (data) => { if (data.follow_link) { @@ -41,10 +66,11 @@ export default class EmailToOAuth extends React.Component { } }, (err) => { - this.setState({error: err.message}); + 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 ( -
-

- -

-
+ let content; + if (this.state.showMfa) { + content = ( + + ); + } else { + content = ( +

+ ); + } + + return ( +

+

+ +

+ {content}
); } diff --git a/webapp/components/claim/components/ldap_to_email.jsx b/webapp/components/claim/components/ldap_to_email.jsx index b7ff93b59..39056cd0d 100644 --- a/webapp/components/claim/components/ldap_to_email.jsx +++ b/webapp/components/claim/components/ldap_to_email.jsx @@ -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 ( -
-

- -

+ let content; + if (this.state.showMfa) { + content = ( + + ); + } else { + content = (

@@ -194,6 +220,18 @@ export default class LDAPToEmail extends React.Component { {serverError}

+ ); + } + + return ( +
+

+ +

+ {content}
); } -- cgit v1.2.3-1-g7c22