11const path = require ( 'path' ) ;
2- const Any = require ( path . join ( path . dirname ( require . resolve ( 'joi' ) ) , 'types/any/index.js' ) ) ;
3- const Language = require ( path . join ( path . dirname ( require . resolve ( 'joi' ) ) , 'language.js' ) ) ;
2+
3+ /* eslint-disable import/no-dynamic-require */
4+ const Any = require ( path . join ( path . dirname ( require . resolve ( '@hapi/joi' ) ) , 'types/any/index.js' ) ) ;
5+ const Language = require ( path . join ( path . dirname ( require . resolve ( '@hapi/joi' ) ) , 'language.js' ) ) ;
6+ /* eslint-enable */
47
58const defaultOptions = {
69 min : 8 ,
@@ -9,7 +12,7 @@ const defaultOptions = {
912 upperCase : 1 ,
1013 numeric : 1 ,
1114 symbol : 1 ,
12- requirementCount : 3 ,
15+ requirementCount : 4 ,
1316} ;
1417
1518const PasswordComplexity = class extends Any {
@@ -20,8 +23,8 @@ const PasswordComplexity = class extends Any {
2023
2124 this . _options = options || defaultOptions ;
2225 if ( options && ! options . requirementCount ) {
23- this . _options . requirementCount = ( options . lowerCase > 0 )
24- + ( options . upperCase > 0 ) + ( options . numeric > 0 ) + ( options . symbol > 0 ) ;
26+ this . _options . requirementCount = ( options . lowerCase > 0 ) +
27+ ( options . upperCase > 0 ) + ( options . numeric > 0 ) + ( options . symbol > 0 ) ;
2528 }
2629 }
2730
@@ -42,30 +45,50 @@ const PasswordComplexity = class extends Any {
4245 }
4346
4447 _base ( value , state , options ) {
45- let validated = 0 ;
46- let matchMin = false ;
47- let matchMax = false ;
48+ const errors = [ ] ;
4849
4950 if ( typeof value === 'string' ) {
50- matchMin = this . _options . min && value . length >= this . _options . min ;
51- matchMax = this . _options . max && value . length <= this . _options . max ;
51+ const lowercaseCount = ( value . match ( / [ a - z ] / g) || [ ] ) . length ;
52+ const upperCaseCount = ( value . match ( / [ A - Z ] / g) || [ ] ) . length ;
53+ const numericCount = ( value . match ( / [ 0 - 9 ] / g) || [ ] ) . length ;
54+ const symbolCount = ( value . match ( / [ ^ a - z A - Z 0 - 9 ] / g) || [ ] ) . length ;
55+
56+ const meetsMin = this . _options . min && value . length >= this . _options . min ;
57+ const meetsMax = this . _options . max && value . length <= this . _options . max ;
58+ const meetsLowercase = lowercaseCount >= this . _options . lowerCase ;
59+ const meetsUppercase = upperCaseCount >= this . _options . upperCase ;
60+ const meetsNumeric = numericCount >= this . _options . numeric ;
61+ const meetsSymbol = symbolCount >= this . _options . symbol ;
62+ const meetsRequirementCount = ! this . _options . requirementCount ||
63+ meetsLowercase + meetsUppercase + meetsNumeric + meetsSymbol >= this . _options . requirementCount ;
5264
53- validated += ( value . match ( / [ a - z ] / g) || [ ] ) . length >= this . _options . lowerCase ;
54- validated += ( value . match ( / [ A - Z ] / g) || [ ] ) . length >= this . _options . upperCase ;
55- validated += ( value . match ( / [ 0 - 9 ] / g) || [ ] ) . length >= this . _options . numeric ;
56- validated += ( value . match ( / [ ^ a - z A - Z 0 - 9 ] / g) || [ ] ) . length >= this . _options . symbol ;
65+ if ( ! meetsMin ) errors . push ( this . createError ( 'passwordComplexity.tooShort' , { value } , state , options ) ) ;
66+ if ( ! meetsMax ) errors . push ( this . createError ( 'passwordComplexity.tooLong' , { value } , state , options ) ) ;
67+ if ( ! meetsLowercase ) errors . push ( this . createError ( 'passwordComplexity.lowercase' , { value } , state , options ) ) ;
68+ if ( ! meetsUppercase ) errors . push ( this . createError ( 'passwordComplexity.uppercase' , { value } , state , options ) ) ;
69+ if ( ! meetsNumeric ) errors . push ( this . createError ( 'passwordComplexity.numeric' , { value } , state , options ) ) ;
70+ if ( ! meetsSymbol ) errors . push ( this . createError ( 'passwordComplexity.symbol' , { value } , state , options ) ) ;
71+ if ( ! meetsRequirementCount ) {
72+ errors . push ( this . createError ( 'passwordComplexity.requirementCount' , { value } , state , options ) ) ;
73+ }
5774 }
5875
5976 return {
6077 value,
61- errors : ( matchMin && matchMax && validated >= this . _options . requirementCount ) ? null
62- : this . createError ( 'passwordComplexity.base' , { value } , state , options ) ,
78+ errors : errors . length ? errors : null ,
6379 } ;
6480 }
6581} ;
6682
6783Language . errors . passwordComplexity = {
6884 base : 'must meet password complexity requirements' ,
85+ tooShort : 'is too short' ,
86+ tooLong : 'is too long' ,
87+ lowercase : 'doesn\'t contain the required lowercase characters' ,
88+ uppercase : 'doesn\'t contain the required uppercase characters' ,
89+ numeric : 'doesn\'t contain the required numeric characters' ,
90+ symbol : 'doesn\'t contain the required symbols' ,
91+ requirementCount : 'must meet enough of the complexity requirements' ,
6992} ;
7093
7194module . exports = PasswordComplexity ;
0 commit comments