Merge pull request #2494 from hongwei1/develop

bugfix/tweaked the password regex
This commit is contained in:
Simon Redfern 2025-02-21 07:40:02 +01:00 committed by GitHub
commit db86568d61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 23 deletions

View File

@ -519,7 +519,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case Some(STATIC) => {
val cacheValueFromRedis = Caching.getStaticResourceDocCache(cacheKey)
val dynamicDocs: Box[JValue] =
val staticDocs: Box[JValue] =
if (cacheValueFromRedis.isDefined) {
Full(json.parse(cacheValueFromRedis.get))
} else {
@ -530,12 +530,12 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
Full(resourceDocJsonJValue)
}
Future(dynamicDocs.map(successJsonResponse(_)))
Future(staticDocs.map(successJsonResponse(_)))
}
case _ => {
val cacheValueFromRedis = Caching.getAllResourceDocCache(cacheKey)
val dynamicDocs: Box[JValue] =
val bothStaticAndDyamicDocs: Box[JValue] =
if (cacheValueFromRedis.isDefined) {
Full(json.parse(cacheValueFromRedis.get))
} else {
@ -546,7 +546,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
Full(resourceDocJsonJValue)
}
Future(dynamicDocs.map(successJsonResponse(_)))
Future(bothStaticAndDyamicDocs.map(successJsonResponse(_)))
}
}
}
@ -715,13 +715,11 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
swaggerJValue <- NewStyle.function.tryons(s"$UnknownError Can not convert internal swagger file.", 400, cc.callContext) {
val cacheValueFromRedis = Caching.getStaticSwaggerDocCache(cacheKey)
val dynamicDocs: JValue =
if (cacheValueFromRedis.isDefined) {
json.parse(cacheValueFromRedis.get)
} else {
convertResourceDocsToSwaggerJvalueAndSetCache(cacheKey, requestedApiVersionString, resourceDocsJsonFiltered)
}
dynamicDocs
if (cacheValueFromRedis.isDefined) {
json.parse(cacheValueFromRedis.get)
} else {
convertResourceDocsToSwaggerJvalueAndSetCache(cacheKey, requestedApiVersionString, resourceDocsJsonFiltered)
}
}
} yield {
(swaggerJValue, HttpCode.`200`(cc.callContext))

View File

@ -784,16 +784,24 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* (?=.*\d) //should contain at least one digit
* (?=.*[a-z]) //should contain at least one lower case
* (?=.*[A-Z]) //should contain at least one upper case
* (?=.*[!"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~]) //should contain at least one special character
* ([A-Za-z0-9!"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~]{10,16}) //should contain 10 to 16 valid characters
* (?=.*[!\"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~\[\]]) //should contain at least one special character
* ([A-Za-z0-9!\"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~\[\]]{10,16}) //should contain 10 to 16 valid characters
**/
val regex =
"""^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~])([A-Za-z0-9!"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~]{10,16})$""".r
password match {
case password if(password.length > 16 && password.length <= 512 && basicPasswordValidation(password) ==SILENCE_IS_GOLDEN) => true
case regex(password) if(basicPasswordValidation(password) ==SILENCE_IS_GOLDEN) => true
case _ => false
"""^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!\"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~\[\]])([A-Za-z0-9!\"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~\[\]]{10,16})$""".r
// first check `basicPasswordValidation`
if (basicPasswordValidation(password) != SILENCE_IS_GOLDEN) {
return false
}
// 2nd: check the password length between 10 and 512
if (password.length > 16 && password.length <= 512) {
return true
}
// 3rd: check the regular expression
regex.pattern.matcher(password).matches()
}
/** only A-Z, a-z, 0-9,-,_,. =, & and max length <= 2048 */
@ -847,12 +855,17 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
/** also support space now */
def basicPasswordValidation(value:String): String ={
val valueLength = value.length
val regex = """^([A-Za-z0-9!"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~ ]+)$""".r
value match {
case regex(e) if(valueLength <= 512) => SILENCE_IS_GOLDEN
case regex(e) if(valueLength > 512) => ErrorMessages.InvalidValueLength
case _ => ErrorMessages.InvalidValueCharacters
val regex = """^([A-Za-z0-9!\"#$%&'\(\)*+,-./:;<=>?@\\[\\\\]^_\\`{|}~ \[\]]+)$""".r
if (!regex.pattern.matcher(value).matches()) {
return ErrorMessages.InvalidValueCharacters
}
if (valueLength > 512) {
return ErrorMessages.InvalidValueLength
}
SILENCE_IS_GOLDEN
}
/** only A-Z, a-z, 0-9, -, _, ., @, and max length <= 512 */

View File

@ -37,6 +37,7 @@ import code.api.util._
import code.setup.PropsReset
import code.util.Helper.SILENCE_IS_GOLDEN
import com.openbankproject.commons.model.UserAuthContextCommons
import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.http.provider.HTTPParam
import net.liftweb.json.{JValue, parse}
@ -838,6 +839,65 @@ class APIUtilTest extends FeatureSpec with Matchers with GivenWhenThen with Prop
actualValue6 contains (InvalidValueLength) shouldBe (true)
}
scenario(s"Test the ${nameOf(APIUtil.basicPasswordValidation _)} method") {
val firefoxStrongPasswordProposal = "9YF]gZnXzAENM+]"
basicPasswordValidation(firefoxStrongPasswordProposal) shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation("Abc!123 xyz") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation("SuperStrong#123") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation("Hello World!") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation(" ") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN allow space so far
basicPasswordValidation("short💥") shouldBe (InvalidValueCharacters) // ErrorMessages.InvalidValueCharacters
basicPasswordValidation("a" * 513) shouldBe (InvalidValueLength) // ErrorMessages.InvalidValueLength
}
scenario(s"Test the ${nameOf(APIUtil.fullPasswordValidation _)} method") {
val firefoxStrongPasswordProposal = "9YF]gZnXzAENM+]"
fullPasswordValidation(firefoxStrongPasswordProposal) // true
fullPasswordValidation("Abc!123xyz") // true
fullPasswordValidation("SuperStrong#123") // true
fullPasswordValidation("Abcdefg!1") // true
fullPasswordValidation("short1!") // falsetoo short
fullPasswordValidation("alllowercase123!") // falseno capital letter
fullPasswordValidation("ALLUPPERCASE123!") // falseno smaller case letter
fullPasswordValidation("NoSpecialChar123") // falsenot special character
}
}
feature(s"test ${nameOf(APIUtil.basicPasswordValidation _)} and ${nameOf(APIUtil.fullPasswordValidation _)}") {
scenario(s"Test the ${nameOf(APIUtil.basicPasswordValidation _)} method") {
val firefoxStrongPasswordProposal = "9YF]gZnXzAENM+]"
basicPasswordValidation(firefoxStrongPasswordProposal) shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation("Abc!123 xyz") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation("SuperStrong#123") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation("Hello World!") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN
basicPasswordValidation(" ") shouldBe (SILENCE_IS_GOLDEN) // SILENCE_IS_GOLDEN allow space so far
basicPasswordValidation("short💥") shouldBe (InvalidValueCharacters) // ErrorMessages.InvalidValueCharacters
basicPasswordValidation("a" * 513) shouldBe (InvalidValueLength) // ErrorMessages.InvalidValueLength
}
scenario(s"Test the ${nameOf(APIUtil.fullPasswordValidation _)} method") {
val firefoxStrongPasswordProposal = "9YF]gZnXzAENM+]"
fullPasswordValidation(firefoxStrongPasswordProposal) // true
fullPasswordValidation("Abc!123xyz") // true
fullPasswordValidation("SuperStrong#123") // true
fullPasswordValidation("Abcdefg!1") // true
fullPasswordValidation("short1!") // falsetoo short
fullPasswordValidation("alllowercase123!") // falseno capital letter
fullPasswordValidation("ALLUPPERCASE123!") // falseno smaller case letter
fullPasswordValidation("NoSpecialChar123") // falsenot special character
}
}