ldx-widgets
Version:
widgets
181 lines (145 loc) • 4.68 kB
text/coffeescript
React = require 'react'
_ = require 'lodash'
owasp = require 'owasp-password-strength-test'
TextInput = React.createFactory(require './text_input')
{ENTER} = require '../constants/keyboard'
{div, li, h4, ul, span} = React.DOM
MIN_PASSWORD_LENGTH = 6
MAX_PASSWORD_LENGTH = 15
REMOVED_REQUIREMENTS = [2, 6]
PasswordInput = React.createClass
displayName: 'PasswordInput'
propTypes:
onChange: React.PropTypes.func
getInitialState: ->
owasp.config
minLength: MIN_PASSWORD_LENGTH
maxLength: MAX_PASSWORD_LENGTH
isPassphrase: false
test = owasp.test('')
return {
passwordTest: test
passwordRequirements: @buildTestRequirements(test)
currentPassword: null
confirmPassword: null
}
getDefaultProps: ->
onChange: ->
isValid: ->
current = @refs.input.getValue()
confirm = @refs.inputConfirm.getValue() if @refs.inputConfirm?
return confirm?.length and confirm is current and @state.passwordTest.failedTests.length is 0
render: ->
{isNewUser, resetPass, focusOnMount} = @props
{passwordTest, currentPassword, confirmPassword, passwordRequirements} = @state
passwordsMatch = currentPassword is confirmPassword
passwordFailures = passwordTest.failedTests
passwordErrors = passwordTest.errors
passingTest = passwordFailures?.length is 0
# remove certain tests for our purposes
if passwordFailures and passwordFailures.length is 0 then passingTest = true
inputProps =
placeholder: t 'Password'
ref: 'input'
key: 'input'
type: 'password'
inputProps = _.assign(inputProps, @props)
inputProps.onChange = @testCurrentPassword
div {
className: 'password-form'
}, [
TextInput(inputProps)
TextInput {
key: 'inputConfirm'
ref: 'inputConfirm'
type: 'password'
autoComplete: @props.autoComplete
tabIndex: @props.tabIndex
placeholder: t 'Confirm Password'
onChange: @testConfirmPassword
wrapperClass: 'confirm'
value: confirmPassword
onKeyUp: @handleKeyUp
} if (not isNewUser or resetPass) and passingTest
div {
key: 'requirements'
className: 'requirements'
}, [
h4 {
key: 'pass-req-header'
className: 'header'
}, "#{t('Password requirements')}:"
ul {
key: 'pass-reqs'
className: 'password-reqs'
}, passwordRequirements
div({
key: 'pass-match'
}, [
span {
key: 'match-icon'
className: "icon-status #{if passwordsMatch then 'success' else 'error'}"
}, null
span {
key: 'match-text'
className: 'req-text'
}, t('Passwords match')
]
) if passingTest
]
]
componentDidMount: ->
if @props.focusOnMount then @refs.input.focus()
getValue: ->
unless @refs.inputConfirm? then ''
else @refs.inputConfirm.getValue()
handleKeyUp: (e) ->
return unless e.keyCode is ENTER
@props.handleKeyUp?() if @isValid()
buildTestRequirements: (test) ->
failedTestErrors =
0: t("At least __minLength__ characters", { minLength: MIN_PASSWORD_LENGTH })
3: t("At least __count__ lowercase letter", { count: 1 })
4: t("At least __count__ uppercase letter", { count: 1 })
5: t("At least __count__ number", { count: 1 })
passwordRequirements = []
{failedTests, errors} = test
# Remove specified requirements
for req in REMOVED_REQUIREMENTS
index = failedTests?.indexOf(req)
if index > -1 then failedTests.splice(index, 1)
if index > -1 then errors.splice(index, 1)
# Display the remaining tests
for key, val of failedTestErrors
status = if failedTests.indexOf(+key) > -1 then 'error' else 'success'
passwordRequirements.push(
li {
key: key
className: 'req-item'
}, [
span {
key: 'status'
className: "icon-status #{status}"
}, null
span {
key: 'val'
className: 'req-text'
}, val
]
)
return passwordRequirements
testCurrentPassword: (e) ->
value = @refs.input.getValue()
test = owasp.test(value)
@setState {
passwordTest: test
currentPassword: value
passwordRequirements: @buildTestRequirements(test)
}
testConfirmPassword: (e) ->
value = @refs.inputConfirm.getValue()
@setState
confirmPassword: value
, ->
@props.onChange()
module.exports = PasswordInput