Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

Implement too long check #68

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ Using in a __Universal JS App__ (server-side rendering):

- Minimum password length acceptable for password to be considered valid

#### maxLength (Default: 1000)

- Maximum password length acceptable for password to be considered valid
- Used to prevent DDOS attacks with exceptionally long passwords meant to overload servers with work.

#### minScore (Default: 2)

- Minimum score acceptable for password to be considered valid
Expand All @@ -70,6 +75,10 @@ Using in a __Universal JS App__ (server-side rendering):

- A string to describe when password is too short (based on minLength prop).

#### tooLongWord (Default: 'too long')

- A string to describe when password is too long (based on maxLength prop).

#### changeCallback

- Callback after input has changed (and score was recomputed)
Expand Down
53 changes: 53 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!doctype html>

<html>
<head>
<title>React Password Strength Example</title>

<style media="screen">
body {
background: #e6e6e6;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

#example {
background: #fff;
margin: 50px auto 30px;
min-height: 250px;
padding: 45px 75px;
width: 500px;
}

label {
display: inline-block;
margin-bottom: 10px;
}

button {
background: #fff;
border: none;
color: #6e6e6e;
font-size: 16px;
padding: 5px 8px;
margin-top: 10px;
opacity: 1;
transition: opacity 250ms ease-in-out;
}

button:hover,
button:focus {
cursor: pointer;
outline: none;
}

button:disabled {
opacity: 0;
}
</style>
</head>

<body>
<div id="example"></div>
<script src="bundle.js"></script>
</body>
</html>
54 changes: 54 additions & 0 deletions example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactPasswordStrength from "../dist/index";

class App extends React.Component {
state = {
passLength: 0,
}

changeCallback = state =>
this.setState({ passLength: state.password.length })

clear = () => this.ReactPasswordStrength.clear()

render() {
const inputProps = {
placeholder: "Try a password...",
id: "inputPassword",
autoFocus: true,
className: 'another-input-prop-class-name',
};

return (
<div>
<h1>React Password Strength Tool</h1>
<p>Powered by <a href="https://github.com/dropbox/zxcvbn" target="_blank">zxcvbn</a></p>

<ReactPasswordStrength
ref={ref => this.ReactPasswordStrength = ref}
minLength={6}
maxLength={10}
tooLongWord="woah there"
inputProps={inputProps}
changeCallback={this.changeCallback}
/>

<button onClick={this.clear} disabled={this.state.passLength === 0}>
Clear
</button>

<h3>Password Input with Default Value for password</h3>

<ReactPasswordStrength
minLength={6}
maxLength={10}
inputProps={{ ...inputProps, id: "inputPassword2" }}
defaultValue="defaultValue"
/>
</div>
);
}
}

ReactDOM.render(<App />, document.getElementById("example"));
22 changes: 14 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import zxcvbn from 'zxcvbn';
import PropTypes from 'prop-types';

const isTooShort = (password, minLength) => password.length < minLength;
const isTooLong = (password, maxLength) => password.length > maxLength;

export default class ReactPasswordStrength extends Component {
static propTypes = {
Expand All @@ -13,6 +14,7 @@ export default class ReactPasswordStrength extends Component {
defaultValue: PropTypes.string,
inputProps: PropTypes.object,
minLength: PropTypes.number,
maxLength: PropTypes.number,
minScore: PropTypes.number,
namespaceClassName: PropTypes.string,
scoreWords: PropTypes.array,
Expand All @@ -25,10 +27,12 @@ export default class ReactPasswordStrength extends Component {
changeCallback: null,
className: '',
defaultValue: '',
maxLength: 1000,
minLength: 5,
minScore: 2,
namespaceClassName: 'ReactPasswordStrength',
scoreWords: ['weak', 'weak', 'okay', 'good', 'strong'],
tooLongWord: 'too long',
tooShortWord: 'too short',
userInputs: [],
}
Expand Down Expand Up @@ -64,21 +68,21 @@ export default class ReactPasswordStrength extends Component {
}

handleChange = () => {
const { changeCallback, minScore, userInputs, minLength } = this.props;
const { changeCallback, minScore, userInputs, minLength, maxLength } = this.props;
const password = this.reactPasswordStrengthInput.value;

let score = 0;
let result = null;

// always sets a zero score when min length requirement is not met
// avoids unnecessary zxcvbn computations (CPU intensive)
if (isTooShort(password, minLength) === false) {
if (isTooShort(password, minLength) === false && isTooLong(password, maxLength) === false) {
result = zxcvbn(password, userInputs);
score = result.score;
}

this.setState({
isValid: score >= minScore,
isValid: score >= minScore && !isTooLong(password, maxLength),
password,
score,
}, () => {
Expand All @@ -93,10 +97,12 @@ export default class ReactPasswordStrength extends Component {
const {
className,
inputProps,
maxLength,
minLength,
namespaceClassName,
scoreWords,
style,
tooLongWord,
tooShortWord,
} = this.props;

Expand All @@ -106,11 +112,11 @@ export default class ReactPasswordStrength extends Component {
className ? className : '',
password.length > 0 ? `is-strength-${score}` : '',
];
const strengthDesc = (
isTooShort(password, minLength)
? tooShortWord
: scoreWords[score]
);

let strengthDesc = scoreWords[score];

if (isTooShort(password, minLength)) strengthDesc = tooShortWord;
if (isTooLong(password, maxLength)) strengthDesc = tooLongWord;

if (isValid === true) {
inputClasses.push('is-password-valid');
Expand Down
14 changes: 14 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,20 @@ describe('ReactPasswordStrength Events', () => {
expect(result.state.isValid).toBe(false);
})

it('invalidates too long passwords', () => {
const result = renderIntoDocument(<PassStrength maxLength={9} />);
let input = findRenderedDOMComponentWithClass(result, 'ReactPasswordStrength-input');

// this normally passes but must fail because it exceeds the max length
input.value = '4mf2df32df52df3';

Simulate.change(input);

expect(result.state.password).toBe('4mf2df32df52df3');
expect(result.state.score).toBe(0);
expect(result.state.isValid).toBe(false);
})

it('adds strings in userInputs to zxcvbn dictionary', () => {
const knownKeyword = 'longwordthatiscommon';
const result = renderIntoDocument(<PassStrength minScore={2} userInputs={[knownKeyword]} />);
Expand Down