Skip to content

Commit

Permalink
Fix #1882 - Do not allow bad characters in password fields (#1891)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gnafu authored and Tobia Di Pisa committed May 29, 2017
1 parent c1c0d2a commit 553146b
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 67 deletions.
7 changes: 0 additions & 7 deletions web/client/components/manager/users/GroupDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const React = require('react');
const UsersTable = require('./UsersTable');
Expand Down
54 changes: 23 additions & 31 deletions web/client/components/manager/users/UserDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const React = require('react');

Expand Down Expand Up @@ -43,7 +36,8 @@ const UserDialog = React.createClass({
style: React.PropTypes.object,
buttonSize: React.PropTypes.string,
inputStyle: React.PropTypes.object,
attributes: React.PropTypes.array
attributes: React.PropTypes.array,
minPasswordSize: React.PropTypes.number
},
getDefaultProps() {
return {
Expand Down Expand Up @@ -71,7 +65,8 @@ const UserDialog = React.createClass({
marginBottom: "20px",
padding: "5px",
border: "1px solid #078AA3"
}
},
minPasswordSize: 6
};
},
getAttributeValue(name) {
Expand All @@ -89,7 +84,7 @@ const UserDialog = React.createClass({
if (pw.length === 0) {
return null;
}
return pw.length > 5 ? "success" : "warning";
return this.isMainPasswordValid(pw) ? "success" : "warning";

},
renderGeneral() {
Expand All @@ -103,7 +98,7 @@ const UserDialog = React.createClass({
readOnly={this.props.user && this.props.user.id}
style={this.props.inputStyle}
onChange={this.handleChange}
value={this.props.user && this.props.user.name}/>
value={this.props.user && this.props.user.name || ""}/>
</FormGroup>
<FormGroup validationState={this.getPwStyle()}>
<ControlLabel><Message msgId="user.password"/></ControlLabel>
Expand All @@ -115,7 +110,7 @@ const UserDialog = React.createClass({
style={this.props.inputStyle}
onChange={this.handleChange} />
</FormGroup>
<FormGroup validationState={ (this.props.user && this.props.user.newPassword && (this.isValidPassword() ? "success" : "error")) || null}>
<FormGroup validationState={ (this.isValidPassword() ? "success" : "error") || null}>
<ControlLabel><Message msgId="user.retypePwd"/></ControlLabel>
<FormControl ref="confirmPassword"
key="confirmPassword"
Expand All @@ -125,14 +120,14 @@ const UserDialog = React.createClass({
style={this.props.inputStyle}
onChange={this.handleChange} />
</FormGroup>
<select name="role" style={this.props.inputStyle} onChange={this.handleChange} value={this.props.user && this.props.user.role}>
<select name="role" style={this.props.inputStyle} onChange={this.handleChange} value={this.props.user && this.props.user.role || ""}>
<option value="ADMIN">ADMIN</option>
<option value="USER">USER</option>
</select>
<FormGroup>
<ControlLabel><Message msgId="users.enabled"/></ControlLabel>
<Checkbox
checked={this.props.user && (this.props.user.enabled === undefined ? false : this.props.user.enabled)}
defaultChecked={this.props.user && (this.props.user.enabled === undefined ? false : this.props.user.enabled)}
type="checkbox"
key={"enabled" + (this.props.user ? this.props.user.enabled : "missing")}
name="enabled"
Expand All @@ -141,16 +136,16 @@ const UserDialog = React.createClass({
</div>);
},
renderAttributes() {
return this.props.attributes.map((attr) => {
return (<FormGroup>
return this.props.attributes.map((attr, index) => {
return (<FormGroup key={"form-n-" + index}>
<ControlLabel>{attr.name}</ControlLabel>
<FormControl ref={"attribute." + attr.name}
key={"attribute." + attr.name}
name={"attribute." + attr.name}
type="text"
style={this.props.inputStyle}
onChange={this.handleChange}
value={this.getAttributeValue(attr.name)} /></FormGroup>);
value={this.getAttributeValue(attr.name) || ""} /></FormGroup>);
});
},
renderSaveButtonContent() {
Expand Down Expand Up @@ -201,7 +196,7 @@ const UserDialog = React.createClass({
</button>
</span>
<div role="body">
<Tabs defaultActiveKey={1} key="tab-panel">
<Tabs defaultActiveKey={1} key="tab-panel" id="userDetails-tabs">
<Tab eventKey={1} title={<Button className="square-button" bsSize={this.props.buttonSize} bsStyle="primary"><Glyphicon glyph="user"/></Button>} >
{this.renderGeneral()}
</Tab>
Expand All @@ -219,30 +214,27 @@ const UserDialog = React.createClass({
</div>
</Dialog>);
},
isMainPasswordValid(password) {
let p = password || this.props.user.newPassword || "";
// Empty password field will signal the GeoStoreDAO not to change the password
if (p === "") {
return true;
}
return (p.length >= this.props.minPasswordSize) && !(/[^a-zA-Z0-9\!\@\#\$\%\&\*]/.test(p));
},
isSaving() {
return this.props.user && this.props.user.status === "saving";
},
isSaved() {
return this.props.user && (this.props.user.status === "saved" || this.props.user.status === "created");
},
isValid() {
let valid = true;
let user = this.props.user;
if (!user) return false;
valid = valid && user.name && user.status === "modified" && this.isValidPassword();
return valid;
return user && user.name && user.status === "modified" && this.isValidPassword();
},
isValidPassword() {
let valid = true;
let user = this.props.user;
if (user && user.id) {
if (user.newPassword) {
valid = valid && (user.confirmPassword === user.newPassword);
}
} else {
valid = valid && user && user.newPassword && (user.confirmPassword === user.newPassword);
}
return valid;
return user && this.isMainPasswordValid(user.newPassword) && (user.confirmPassword === user.newPassword);
},
handleChange(event) {
this.props.onChange(event.target.name, event.target.value);
Expand Down
14 changes: 11 additions & 3 deletions web/client/components/manager/users/__tests__/UserDialog-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,24 @@ describe("Test UserDialog Component", () => {
<UserDialog user={{...enabledUser, lastError: {statusText: "ERROR"}}}/>, document.getElementById("container"));
expect(comp).toExist();
});
it('Test isValidPAssword', () => {
it('Test isValidPassword', () => {
let comp = ReactDOM.render(
<UserDialog user={{...enabledUser, newPassword: {statusText: "ERROR"}}}/>, document.getElementById("container"));
expect(comp).toExist();
// valid password, wrong confirm
comp = ReactDOM.render(
<UserDialog user={{...enabledUser, newPassword: "aaa", confirmPassword: "bbb"}}/>, document.getElementById("container"));
<UserDialog user={{...enabledUser, newPassword: "aaabbb", confirmPassword: "bbbccc"}}/>, document.getElementById("container"));
expect(comp).toExist();
expect(comp.isValidPassword()).toBe(false);
// Valid password
comp = ReactDOM.render(
<UserDialog user={{name: "user", newPassword: "aaa", confirmPassword: "aaa"}}/>, document.getElementById("container"));
<UserDialog user={{name: "user", newPassword: "aA1!@#$%&*", confirmPassword: "aA1!@#$%&*"}}/>, document.getElementById("container"));
expect(comp.isValidPassword()).toBe(true);
// Invalid password, correct confirm
comp = ReactDOM.render(
<UserDialog user={{...enabledUser, newPassword: "aaabbbà", confirmPassword: "aaabbbà"}}/>, document.getElementById("container"));
expect(comp).toExist();
expect(comp.isValidPassword()).toBe(false);

});
});
9 changes: 0 additions & 9 deletions web/client/components/maps/forms/Metadata.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const React = require('react');
const {FormControl, FormGroup, ControlLabel} = require('react-bootstrap');
Expand Down Expand Up @@ -49,7 +42,6 @@ const Metadata = React.createClass({
<ControlLabel>{this.props.nameFieldText}</ControlLabel>
<FormControl ref="mapName"
key="mapName"
hasFeedback
type="text"
onChange={this.changeName}
placeholder={this.props.namePlaceholderText}
Expand All @@ -59,7 +51,6 @@ const Metadata = React.createClass({
<ControlLabel>{this.props.descriptionFieldText}</ControlLabel>
<FormControl ref="mapDescription"
key="mapDescription"
hasFeedback
type="text"
onChange={this.changeDescription}
placeholder={this.props.descriptionPlaceholderText}
Expand Down
7 changes: 0 additions & 7 deletions web/client/components/misc/ConfirmDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const React = require('react');

Expand Down
16 changes: 9 additions & 7 deletions web/client/components/security/forms/PasswordReset.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,10 @@ const PasswordReset = React.createClass({
if (!this.state.password) {
return null;
}
let pw = this.state.password;
if (pw.length === 0) {
if (this.state.password.length === 0) {
return null;
}
return pw.length >= this.props.minPasswordSize ? "success" : "error";

return this.isMainPasswordValid() ? "success" : "error";
},
renderWarning() {
if (!this.state.password) {
Expand All @@ -66,6 +64,8 @@ const PasswordReset = React.createClass({
let pw = this.state.password;
if (pw !== null && pw.length < this.props.minPasswordSize && pw.length > 0) {
return <Alert bsStyle="danger"><Message msgId="user.passwordMinlenght" msgParams={{minSize: this.props.minPasswordSize}}/></Alert>;
} else if (!this.isMainPasswordValid()) {
return <Alert bsStyle="danger"><Message msgId="user.passwordInvalid" /></Alert>;
} else if (pw !== null && pw !== this.state.passwordcheck ) {
return <Alert bsStyle="danger"><Message msgId="user.passwordCheckFail" /></Alert>;
}
Expand All @@ -87,15 +87,13 @@ const PasswordReset = React.createClass({
<FormControl ref="password"
key="password"
type="password"
hasFeedback
onChange={this.changePassword}
placeholder={LocaleUtils.getMessageById(this.context.messages, "user.newPwd")} />
</FormGroup>
<FormGroup validationState={this.isValid(this.state.password, this.state.passwordcheck) && this.getPwStyle() ? "success" : "error"}>
<ControlLabel>{this.props.passwordCheckText}</ControlLabel>
<FormControl ref="passwordcheck"
key="passwordcheck"
hasFeedback
type="password"
label={this.props.passwordCheckText}
onChange={this.changePasswordCheck}
Expand All @@ -105,13 +103,17 @@ const PasswordReset = React.createClass({
{this.renderStatus()}
</form>);
},
isMainPasswordValid(password) {
let p = password || this.state.password;
return (p.length >= this.props.minPasswordSize) && !(/[^a-zA-Z0-9\!\@\#\$\%\&\*]/.test(p));
},
isValid(password, passwordcheck) {
let p = password || this.state.password;
let p2 = passwordcheck || this.state.passwordcheck;
if (!p) {
return false;
}
return p !== null && p.length >= this.props.minPasswordSize && p === p2;
return p !== null && this.isMainPasswordValid(p) && p === p2;
},
changePassword(e) {
this.setState({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,19 @@ describe("Test the password reset form component", () => {
password2.value = "test2";
ReactTestUtils.Simulate.change(password2);
expect(cmp.isValid()).toEqual(false);
// size is < then 6
password2.value = "test";
ReactTestUtils.Simulate.change(password2);
// size is < then 6
expect(cmp.isValid()).toEqual(false);
// invalid characters
password.value = "testòàè+";
password2.value = "testòàè+";
ReactTestUtils.Simulate.change(password2);
expect(cmp.isValid()).toEqual(false);

// test valid
password.value = "password";
password2.value = "password";
password.value = "password123!$&#";
password2.value = "password123!$&#";
ReactTestUtils.Simulate.change(password);
ReactTestUtils.Simulate.change(password2);
expect(cmp.isValid()).toEqual(true);
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.de-DE
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@
"retypePwd": "Passwort wiederholen",
"passwordMinlenght": "Dein Passwort muss aus mindestens {minSize} Zeichen bestehen",
"passwordCheckFail": "Passwörter sind nicht identisch!",
"passwordInvalid": "Ungültiges Passwort",
"username": "Benutzername",
"password": "Passwort",
"passwordChanged": "Passwort geändert",
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.en-US
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@
"retypePwd": "Retype Password",
"passwordMinlenght": "Your password must be at least {minSize} character",
"passwordCheckFail": "Passwords do not match!",
"passwordInvalid": "Invalid password",
"username": "Username",
"password": "Password",
"passwordChanged": "Password changed",
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.fr-FR
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@
"retypePwd": "Confirmer le mot de passe",
"passwordMinlenght": "Votre mot de passe doit être d'au moins {minSize} caractères",
"passwordCheckFail": "Les deux mots de passe ne correspondent pas!",
"passwordInvalid": "Mot de passe incorrect",
"username": "Nom d'utilisateur",
"password": "Mot de passe",
"passwordChanged": "Mot de passe changé",
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.it-IT
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@
"retypePwd": "Conferma Password",
"passwordMinlenght": "La password deve essere lunga almeno {minSize} caratteri",
"passwordCheckFail": "Le due password non corrispondono",
"passwordInvalid": "Password non valida",
"username": "Username",
"password": "Password",
"passwordChanged": "La password è stata cambiata",
Expand Down

0 comments on commit 553146b

Please sign in to comment.