ldx-widgets
Version:
widgets
208 lines (169 loc) • 5.49 kB
text/coffeescript
React = require 'react'
_ = require 'lodash'
owasp = require 'owasp-password-strength-test'
TextInput = React.createFactory(require './text_input')
CheckboxInput = React.createFactory(require './checkbox_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: ->
inputColumnClass: 'col-3-12'
requirementsColumnClass: 'col-3-12'
confirmColumnClass: 'col-3-12'
className: 'grid grid-pad'
compact: no
focusOnMount: no
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, inputColumnClass, requirementsColumnClass, confirmColumnClass, className, compact} = @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 = yes
confirmField = TextInput {
key: 'inputConfirm'
ref: 'inputConfirm'
type: 'password'
autoComplete: @props.autoComplete
tabIndex: @props.tabIndex
placeholder: t 'Confirm Password'
onChange: @testConfirmPassword
wrapperClass: if compact then 'password-confirm' else null
value: confirmPassword
onKeyUp: @handleKeyUp
disabled: not passingTest
}
requirementsEls = passwordRequirements.concat([
li {
key: 'passMatch'
className: 'req-item'
}, [
span {
key: 'match-icon'
className: "icon-status #{if passwordsMatch then 'success' else 'error'}"
}
span {
key: 'match-text'
className: 'req-text'
}, t('Passwords match')
] if passingTest
])
div {
className: className
}, [
div {
key: 'inputs'
className: inputColumnClass
}, [
TextInput {
placeholder: t 'Password'
ref: 'input'
key: 'input'
type: 'password'
onChange: @testCurrentPassword
tabIndex: @props.tabIndex
}
confirmField if compact
]
div {
key: 'confirm'
className: confirmColumnClass
}, confirmField if not compact
div {
key: 'requirements'
className: requirementsColumnClass
}, [
h4 {
key: 'pass-req-header'
className: 'password-reqs-header'
}, "#{t('Password requirements')}:"
ul {
key: 'pass-reqs'
className: 'password-reqs'
}, requirementsEls
]
]
componentDidMount: ->
if @props.focusOnMount then @refs.input.focus()
getValue: ->
{passwordTest, currentPassword, confirmPassword} = @state
passwordsMatch = currentPassword is confirmPassword
passwordFailures = passwordTest.failedTests
passingTest = passwordFailures?.length is 0
if not passwordsMatch or not passingTest then ''
else confirmPassword
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