From fe6521521a8ed2453dd672f71124fde078574727 Mon Sep 17 00:00:00 2001 From: Mauro Bartolomeoli Date: Fri, 12 May 2017 18:24:14 +0200 Subject: [PATCH] Fixes #1761: feedback for password changing --- .../security/forms/PasswordReset.jsx | 14 ++++++++++++- .../forms/__tests__/PasswordReset-test.jsx | 14 +++++++++++++ .../security/modals/PasswordResetModal.jsx | 5 ++++- web/client/plugins/login/index.js | 4 +++- .../reducers/__tests__/security-test.js | 18 ++++++++++++++++- web/client/reducers/security.js | 20 +++++++++++++++++-- web/client/translations/data.de-DE | 2 ++ web/client/translations/data.en-US | 2 ++ web/client/translations/data.fr-FR | 2 ++ web/client/translations/data.it-IT | 2 ++ 10 files changed, 77 insertions(+), 6 deletions(-) diff --git a/web/client/components/security/forms/PasswordReset.jsx b/web/client/components/security/forms/PasswordReset.jsx index 14335817d6..0563cf47ed 100644 --- a/web/client/components/security/forms/PasswordReset.jsx +++ b/web/client/components/security/forms/PasswordReset.jsx @@ -22,7 +22,9 @@ const PasswordReset = React.createClass({ // I18N newPasswordText: React.PropTypes.node, - passwordCheckText: React.PropTypes.node + passwordCheckText: React.PropTypes.node, + changed: React.PropTypes.bool, + error: React.PropTypes.object }, contextTypes: { messages: React.PropTypes.object @@ -69,6 +71,15 @@ const PasswordReset = React.createClass({ } return null; }, + renderStatus() { + if (this.props.changed) { + return ; + } + if (this.props.error) { + return ; + } + return null; + }, render() { return (
@@ -91,6 +102,7 @@ const PasswordReset = React.createClass({ placeholder={LocaleUtils.getMessageById(this.context.messages, "user.retypePwd")} /> {this.renderWarning()} + {this.renderStatus()}
); }, isValid(password, passwordcheck) { diff --git a/web/client/components/security/forms/__tests__/PasswordReset-test.jsx b/web/client/components/security/forms/__tests__/PasswordReset-test.jsx index 738b2b5fd8..b23f8bf370 100644 --- a/web/client/components/security/forms/__tests__/PasswordReset-test.jsx +++ b/web/client/components/security/forms/__tests__/PasswordReset-test.jsx @@ -30,6 +30,20 @@ describe("Test the password reset form component", () => { expect(cmp).toExist(); }); + it('alert for success', () => { + const cmp = ReactDOM.render(, document.getElementById("container")); + expect(cmp).toExist(); + let alert = ReactDOM.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithClass(cmp, "alert-success")[0]); + expect(alert).toExist(); + }); + + it('alert for error', () => { + const cmp = ReactDOM.render(, document.getElementById("container")); + expect(cmp).toExist(); + let alert = ReactDOM.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithClass(cmp, "alert-danger")[0]); + expect(alert).toExist(); + }); + it('test component validity', () => { const cmp = ReactDOM.render(, document.getElementById("container")); expect(cmp).toExist(); diff --git a/web/client/components/security/modals/PasswordResetModal.jsx b/web/client/components/security/modals/PasswordResetModal.jsx index 01478268a7..3977210891 100644 --- a/web/client/components/security/modals/PasswordResetModal.jsx +++ b/web/client/components/security/modals/PasswordResetModal.jsx @@ -32,7 +32,9 @@ const PasswordResetModal = React.createClass({ closeGlyph: React.PropTypes.string, style: React.PropTypes.object, buttonSize: React.PropTypes.string, - includeCloseButton: React.PropTypes.bool + includeCloseButton: React.PropTypes.bool, + changed: React.PropTypes.bool, + error: React.PropTypes.object }, getDefaultProps() { return { @@ -92,6 +94,7 @@ const PasswordResetModal = React.createClass({ }, getBody() { return ( { this.setState({passwordValid: valid, password}); }} />); diff --git a/web/client/plugins/login/index.js b/web/client/plugins/login/index.js index 94d85bc743..cfdf16cf4a 100644 --- a/web/client/plugins/login/index.js +++ b/web/client/plugins/login/index.js @@ -36,7 +36,9 @@ const UserDetails = connect((state) => ({ const PasswordReset = connect((state) => ({ user: state.security && state.security.user, - show: state.controls.ResetPassword && state.controls.ResetPassword.enabled + show: state.controls.ResetPassword && state.controls.ResetPassword.enabled, + changed: state.security && state.security.passwordChanged && true || false, + error: state.security && state.security.passwordError }), { onPasswordChange: (user, pass) => { return geoStoreChangePassword(user, pass); }, onClose: setControlProperty.bind(null, "ResetPassword", "enabled", false, false) diff --git a/web/client/reducers/__tests__/security-test.js b/web/client/reducers/__tests__/security-test.js index 9c673cf950..52a004a0cd 100644 --- a/web/client/reducers/__tests__/security-test.js +++ b/web/client/reducers/__tests__/security-test.js @@ -7,7 +7,7 @@ */ var expect = require('expect'); var security = require('../security'); -var {LOGIN_SUCCESS, LOGIN_FAIL, RESET_ERROR, LOGOUT} = require('../../actions/security'); +var {LOGIN_SUCCESS, LOGIN_FAIL, RESET_ERROR, LOGOUT, CHANGE_PASSWORD_SUCCESS, CHANGE_PASSWORD_FAIL} = require('../../actions/security'); var {USERMANAGER_UPDATE_USER} = require('../../actions/users'); describe('Test the security reducer', () => { @@ -87,4 +87,20 @@ describe('Test the security reducer', () => { expect(state).toExist(); expect(state.user.name).toBe("user"); }); + + it('change password success', () => { + let state = security({user: testUser.User}, {type: CHANGE_PASSWORD_SUCCESS, user: { + id: 6, + password: "newpassword" + }}); + expect(state).toExist(); + expect(state.user.password).toBe("newpassword"); + expect(state.passwordChanged).toBe(true); + }); + + it('change password fail', () => { + let state = security({user: testUser.User}, {type: CHANGE_PASSWORD_FAIL, error: {message: 'error'}}); + expect(state).toExist(); + expect(state.passwordError).toExist(); + }); }); diff --git a/web/client/reducers/security.js b/web/client/reducers/security.js index 9045ac2961..2597d31ecd 100644 --- a/web/client/reducers/security.js +++ b/web/client/reducers/security.js @@ -6,7 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -const { LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT, CHANGE_PASSWORD_SUCCESS, RESET_ERROR } = require('../actions/security'); +const { LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT, CHANGE_PASSWORD_SUCCESS, CHANGE_PASSWORD_FAIL, RESET_ERROR } = require('../actions/security'); +const { SET_CONTROL_PROPERTY } = require('../actions/controls'); const { USERMANAGER_UPDATE_USER } = require('../actions/users'); const SecurityUtils = require('../utils/SecurityUtils'); @@ -23,6 +24,14 @@ function security(state = {user: null, errorCause: null}, action) { }); } return state; + case SET_CONTROL_PROPERTY: + if (action.control === 'ResetPassword' && action.property === 'enabled') { + return assign({}, state, { + passwordChanged: false, + passwordError: null + }); + } + return state; case LOGIN_SUCCESS: const userAttributes = SecurityUtils.getUserAttributes(action.userDetails.User); const userUuid = head(userAttributes.filter(attribute => attribute.name.toLowerCase() === 'uuid')); @@ -50,7 +59,14 @@ function security(state = {user: null, errorCause: null}, action) { case CHANGE_PASSWORD_SUCCESS: return assign({}, state, { user: assign({}, state.user, assign({}, action.user, {date: new Date().getUTCMilliseconds()})), - authHeader: action.authHeader + authHeader: action.authHeader, + passwordChanged: true, + passwordError: null + }); + case CHANGE_PASSWORD_FAIL: + return assign({}, state, { + passwordError: action.error, + passwordChanged: false }); default: return state; diff --git a/web/client/translations/data.de-DE b/web/client/translations/data.de-DE index a0b8d1ca7a..7eff6553a5 100644 --- a/web/client/translations/data.de-DE +++ b/web/client/translations/data.de-DE @@ -483,6 +483,8 @@ "passwordCheckFail": "Passwörter sind nicht identisch!", "username": "Benutzername", "password": "Passwort", + "passwordChanged": "Passwort geändert", + "passwordError": "Fehler beim Ändern des Passworts", "signIn":"Anmelden", "loginFail":"Anmeldung gescheitert", "loginFailedStatusMessages": { diff --git a/web/client/translations/data.en-US b/web/client/translations/data.en-US index 334ff92a7a..86fca979ba 100644 --- a/web/client/translations/data.en-US +++ b/web/client/translations/data.en-US @@ -483,6 +483,8 @@ "passwordCheckFail": "Passwords do not match!", "username": "Username", "password": "Password", + "passwordChanged": "Password changed", + "passwordError": "Error changing password", "signIn":"Sign-in", "loginFail":"Login Fail", "loginFailedStatusMessages": { diff --git a/web/client/translations/data.fr-FR b/web/client/translations/data.fr-FR index 0f9a53b6cc..60807514d6 100644 --- a/web/client/translations/data.fr-FR +++ b/web/client/translations/data.fr-FR @@ -485,6 +485,8 @@ "passwordCheckFail": "Les deux mots de passe ne correspondent pas!", "username": "Nom d'utilisateur", "password": "Mot de passe", + "passwordChanged": "Mot de passe changé", + "passwordError": "Erreur de changement de mot de passe", "signIn":"S'authentifier", "loginFail":"Erreur d'authentification", "loginFailedStatusMessages": { diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index 22af284f36..0e481f1f5c 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -483,6 +483,8 @@ "passwordCheckFail": "Le due password non corrispondono", "username": "Username", "password": "Password", + "passwordChanged": "La password è stata cambiata", + "passwordError": "Errore nel cambio della password", "signIn":"Accedi", "loginFail":"Login Fallito", "loginFailedStatusMessages": {