diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index 7afbe37..8007c8f 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -13,10 +13,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 1.11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 1.11 - name: Run tests run: sbt test - name: Run linter diff --git a/build.sbt b/build.sbt index ba3cf96..4691280 100644 --- a/build.sbt +++ b/build.sbt @@ -21,7 +21,6 @@ scalacOptions ++= List( "-Ywarn-unused" ) javacOptions += "-g:none" -scalafmtOnCompile := true compileOrder := CompileOrder.JavaThenScala fork in run := true diff --git a/config.json b/config.json index ee7ed58..727b973 100644 --- a/config.json +++ b/config.json @@ -1,34 +1,37 @@ { "randomSeed": 20, + "port": 8888, "captchaExpiryTimeLimit": 5, - "captchas":{ - "FilterChallenge":{ + "threadDelay": 2, + "throttle": 10, + "captchas":[ + { "name": "FilterChallenge", - "supportedLevels":["medium", "hard"], - "supportedMedia":["image"], - "supportedinputType":["text"], + "allowedLevels":["medium", "hard"], + "allowedMedia":["image/png"], + "allowedInputType":["text"], "config":{} }, - "GifCaptcha":{ + { "name": "GifCaptcha", - "supportedLevels":["hard"], - "supportedMedia":["gif"], - "supportedinputType":["text"], + "allowedLevels":["hard"], + "allowedMedia":["image/gif"], + "allowedInputType":["text"], "config":{} }, - "ShadowTextCaptcha":{ + { "name": "ShadowTextCaptcha", - "supportedLevels":["easy"], - "supportedMedia":["image"], - "supportedinputType":["text"], + "allowedLevels":["easy"], + "allowedMedia":["image/png"], + "allowedInputType":["text"], "config": {} }, - "RainDropsCaptcha":{ + { "name": "RainDropsCaptcha", - "supportedLevels":["easy","medium"], - "supportedMedia":["image"], - "supportedinputType":["text"], + "allowedLevels":["easy","medium"], + "allowedMedia":["image/gif"], + "allowedInputType":["text"], "config":{} } - } + ] } \ No newline at end of file diff --git a/src/main/java/lc/captchas/FontFunCaptcha.java b/src/main/java/lc/captchas/FontFunCaptcha.java index a69998b..bb1c278 100644 --- a/src/main/java/lc/captchas/FontFunCaptcha.java +++ b/src/main/java/lc/captchas/FontFunCaptcha.java @@ -6,6 +6,8 @@ import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.util.HashMap; +import java.util.List; import lc.captchas.interfaces.Challenge; import lc.captchas.interfaces.ChallengeProvider; import lc.misc.HelperFunctions; @@ -16,6 +18,19 @@ public class FontFunCaptcha implements ChallengeProvider { return "FontFunCaptcha"; } + public HashMap> supportedParameters() { + HashMap> supportedParams = new HashMap>(); + supportedParams.put("supportedLevels", List.of("medium")); + supportedParams.put("supportedMedia", List.of("image/png")); + supportedParams.put("supportedInputType", List.of("text")); + + return supportedParams; + } + + public void configure(String config) { + // TODO: Add custom config + } + private String getFontName(String path, String level) { File file = new File(path + level + "/"); FilenameFilter txtFileFilter = diff --git a/src/main/java/lc/captchas/GifCaptcha.java b/src/main/java/lc/captchas/GifCaptcha.java index 92a87ec..13cf0fa 100644 --- a/src/main/java/lc/captchas/GifCaptcha.java +++ b/src/main/java/lc/captchas/GifCaptcha.java @@ -6,6 +6,9 @@ import java.awt.RenderingHints; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.HashMap; +import java.util.List; + import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.MemoryCacheImageOutputStream; import java.io.ByteArrayOutputStream; @@ -48,6 +51,19 @@ public class GifCaptcha implements ChallengeProvider { return null; } + public void configure(String config) { + // TODO: Add custom config + } + + public HashMap> supportedParameters() { + HashMap> supportedParams = new HashMap>(); + supportedParams.put("supportedLevels", List.of("hard")); + supportedParams.put("supportedMedia", List.of("image/gif")); + supportedParams.put("supportedInputType", List.of("text")); + + return supportedParams; + } + public Challenge returnChallenge() { String secret = HelperFunctions.randomString(6); return new Challenge(gifCaptcha(secret), "image/gif", secret.toLowerCase()); diff --git a/src/main/java/lc/captchas/ShadowTextCaptcha.java b/src/main/java/lc/captchas/ShadowTextCaptcha.java index 0128e97..5b3b873 100644 --- a/src/main/java/lc/captchas/ShadowTextCaptcha.java +++ b/src/main/java/lc/captchas/ShadowTextCaptcha.java @@ -10,6 +10,9 @@ import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.List; + import lc.misc.HelperFunctions; import lc.captchas.interfaces.Challenge; import lc.captchas.interfaces.ChallengeProvider; @@ -20,6 +23,19 @@ public class ShadowTextCaptcha implements ChallengeProvider { return "ShadowTextCaptcha"; } + public void configure(String config) { + // TODO: Add custom config + } + + public HashMap> supportedParameters() { + HashMap> supportedParams = new HashMap>(); + supportedParams.put("supportedLevels", List.of("easy")); + supportedParams.put("supportedMedia", List.of("image/png")); + supportedParams.put("supportedInputType", List.of("text")); + + return supportedParams; + } + public boolean checkAnswer(String secret, String answer) { return answer.toLowerCase().equals(secret); } diff --git a/src/main/java/lc/captchas/interfaces/ChallengeProvider.java b/src/main/java/lc/captchas/interfaces/ChallengeProvider.java index ef8e7d0..a3a70e2 100644 --- a/src/main/java/lc/captchas/interfaces/ChallengeProvider.java +++ b/src/main/java/lc/captchas/interfaces/ChallengeProvider.java @@ -1,5 +1,8 @@ package lc.captchas.interfaces; +import java.util.Map; +import java.util.List; + public interface ChallengeProvider { public String getId(); @@ -7,5 +10,7 @@ public interface ChallengeProvider { public boolean checkAnswer(String secret, String answer); - // TODO: def configure(): Unit + public void configure(String config); + + public Map> supportedParameters(); } diff --git a/src/main/scala/lc/Main.scala b/src/main/scala/lc/Main.scala index 739bc35..491ed3f 100644 --- a/src/main/scala/lc/Main.scala +++ b/src/main/scala/lc/Main.scala @@ -3,13 +3,18 @@ package lc import lc.core.{Captcha, CaptchaProviders} import lc.server.Server import lc.background.BackgroundTask +import lc.core.Config object LCFramework { def main(args: scala.Array[String]): Unit = { val captcha = new Captcha() - val server = new Server(8888, captcha) - val backgroudTask = new BackgroundTask(captcha, 10) - backgroudTask.beginThread(2) + val server = new Server(port = Config.port, captcha = captcha) + val backgroundTask = new BackgroundTask( + captcha = captcha, + throttle = Config.throttle, + timeLimit = Config.captchaExpiryTimeLimit + ) + backgroundTask.beginThread(delay = Config.threadDelay) server.start() } } diff --git a/src/main/scala/lc/background/taskThread.scala b/src/main/scala/lc/background/taskThread.scala index fe3cbbc..c6cfe7e 100644 --- a/src/main/scala/lc/background/taskThread.scala +++ b/src/main/scala/lc/background/taskThread.scala @@ -5,13 +5,14 @@ import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit} import lc.core.Captcha import lc.core.{Parameters, Size} -class BackgroundTask(captcha: Captcha, throttle: Int) { +class BackgroundTask(captcha: Captcha, throttle: Int, timeLimit: Int) { private val task = new Runnable { def run(): Unit = { try { val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt + mapIdGCPstmt.setInt(1, timeLimit) mapIdGCPstmt.executeUpdate() val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt @@ -22,7 +23,7 @@ class BackgroundTask(captcha: Captcha, throttle: Int) { if (imageNum.next()) throttleIn = (throttleIn - imageNum.getInt("total")) while (0 < throttleIn) { - captcha.generateChallenge(Parameters("", "", "", Option(Size(0, 0)))) + captcha.generateChallenge(Parameters("medium", "image/png", "text", Option(Size(0, 0)))) throttleIn -= 1 } } catch { case e: Exception => println(e) } diff --git a/src/main/scala/lc/captchas/FilterChallenge.scala b/src/main/scala/lc/captchas/FilterChallenge.scala index 0a1ef61..a4508b6 100644 --- a/src/main/scala/lc/captchas/FilterChallenge.scala +++ b/src/main/scala/lc/captchas/FilterChallenge.scala @@ -7,9 +7,26 @@ import java.awt.Font import java.awt.Color import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge +import scala.jdk.CollectionConverters.MapHasAsJava +import java.util.{List => JavaList, Map => JavaMap} class FilterChallenge extends ChallengeProvider { def getId = "FilterChallenge" + + def configure(config: String): Unit = { + // TODO: add custom config + } + + def supportedParameters(): JavaMap[String, JavaList[String]] = { + val supportedParams = Map( + "supportedLevels" -> JavaList.of("medium", "hard"), + "supportedMedia" -> JavaList.of("image/png"), + "supportedInputType" -> JavaList.of("text") + ).asJava + + supportedParams + } + def returnChallenge(): Challenge = { val filterTypes = List(new FilterType1, new FilterType2) val r = new scala.util.Random diff --git a/src/main/scala/lc/captchas/LabelCaptcha.scala b/src/main/scala/lc/captchas/LabelCaptcha.scala index afbc5e0..5233678 100644 --- a/src/main/scala/lc/captchas/LabelCaptcha.scala +++ b/src/main/scala/lc/captchas/LabelCaptcha.scala @@ -9,6 +9,8 @@ import java.awt.image.BufferedImage import java.awt.Color import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge +import scala.jdk.CollectionConverters.MapHasAsJava +import java.util.{List => JavaList, Map => JavaMap} class LabelCaptcha extends ChallengeProvider { private var knownFiles = new File("known").list.toList @@ -23,6 +25,20 @@ class LabelCaptcha extends ChallengeProvider { def getId = "LabelCaptcha" + def configure(config: String): Unit = { + // TODO: add custom config + } + + def supportedParameters(): JavaMap[String, JavaList[String]] = { + val supportedParams = Map( + "supportedLevels" -> JavaList.of("hard"), + "supportedMedia" -> JavaList.of("image/png"), + "supportedInputType" -> JavaList.of("text") + ).asJava + + supportedParams + } + def returnChallenge(): Challenge = synchronized { val r = scala.util.Random.nextInt(knownFiles.length) diff --git a/src/main/scala/lc/captchas/RainDropsCaptcha.scala b/src/main/scala/lc/captchas/RainDropsCaptcha.scala index da12829..2788096 100644 --- a/src/main/scala/lc/captchas/RainDropsCaptcha.scala +++ b/src/main/scala/lc/captchas/RainDropsCaptcha.scala @@ -10,6 +10,8 @@ import javax.imageio.stream.MemoryCacheImageOutputStream; import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge import lc.misc.GifSequenceWriter +import scala.jdk.CollectionConverters.MapHasAsJava +import java.util.{List => JavaList, Map => JavaMap} class Drop { var x = 0 @@ -31,6 +33,20 @@ class RainDropsCP extends ChallengeProvider { def getId = "FilterChallenge" + def configure(config: String): Unit = { + // TODO: add custom config + } + + def supportedParameters(): JavaMap[String, JavaList[String]] = { + val supportedParams = Map( + "supportedLevels" -> JavaList.of("medium", "easy"), + "supportedMedia" -> JavaList.of("image/gif"), + "supportedInputType" -> JavaList.of("text") + ).asJava + + supportedParams + } + private def extendDrops(drops: Array[Drop], steps: Int, xOffset: Int) = { drops.map(d => { val nd = new Drop() diff --git a/src/main/scala/lc/core/captcha.scala b/src/main/scala/lc/core/captcha.scala index dfbda96..c8c2edc 100644 --- a/src/main/scala/lc/core/captcha.scala +++ b/src/main/scala/lc/core/captcha.scala @@ -5,6 +5,7 @@ import java.util.UUID import java.io.ByteArrayInputStream import lc.database.Statements import lc.core.CaptchaProviders +import lc.captchas.interfaces.ChallengeProvider class Captcha { @@ -30,8 +31,8 @@ class Captcha { } def generateChallenge(param: Parameters): Int = { - //TODO: eval params to choose a provider - val provider = CaptchaProviders.getProvider() + val provider = CaptchaProviders.getProvider(param) + if (!provider.isInstanceOf[ChallengeProvider]) return -1 val providerId = provider.getId() val challenge = provider.returnChallenge() val blob = new ByteArrayInputStream(challenge.content) @@ -40,7 +41,9 @@ class Captcha { insertPstmt.setString(2, challenge.secret) insertPstmt.setString(3, providerId) insertPstmt.setString(4, challenge.contentType) - insertPstmt.setBlob(5, blob) + insertPstmt.setString(5, param.level) + insertPstmt.setString(6, param.input_type) + insertPstmt.setBlob(7, blob) insertPstmt.executeUpdate() val rs: ResultSet = insertPstmt.getGeneratedKeys() val token = if (rs.next()) { @@ -50,24 +53,53 @@ class Captcha { token.asInstanceOf[Int] } - def getChallenge(param: Parameters): Id = { + val allowedInputType = Config.allowedInputType + val allowedLevels = Config.allowedLevels + val allowedMedia = Config.allowedMedia + + private def validateParam(param: Parameters): Boolean = { + if ( + allowedLevels.contains(param.level) && + allowedMedia.contains(param.media) && + allowedInputType.contains(param.input_type) + ) + return true + else + return false + } + + def getChallenge(param: Parameters): ChallengeResult = { try { - val tokenPstmt = Statements.tlStmts.get.tokenPstmt - val rs = tokenPstmt.executeQuery() - val tokenOpt = if (rs.next()) { - Some(rs.getInt("token")) + val validParam = validateParam(param) + if (validParam) { + val tokenPstmt = Statements.tlStmts.get.tokenPstmt + tokenPstmt.setString(1, param.level) + tokenPstmt.setString(2, param.media) + tokenPstmt.setString(3, param.input_type) + val rs = tokenPstmt.executeQuery() + val tokenOpt = if (rs.next()) { + Some(rs.getInt("token")) + } else { + None + } + val updateAttemptedPstmt = Statements.tlStmts.get.updateAttemptedPstmt + val token = tokenOpt.getOrElse(generateChallenge(param)) + val result = if (token != -1) { + val uuid = getUUID(token) + updateAttemptedPstmt.setString(1, uuid) + updateAttemptedPstmt.executeUpdate() + Id(uuid) + } else { + Error(ErrorMessageEnum.NO_CAPTCHA.toString) + } + result } else { - None + Error(ErrorMessageEnum.INVALID_PARAM.toString) } - val updateAttemptedPstmt = Statements.tlStmts.get.updateAttemptedPstmt - val uuid = getUUID(tokenOpt.getOrElse(generateChallenge(param))) - updateAttemptedPstmt.setString(1, uuid) - updateAttemptedPstmt.executeUpdate() - Id(uuid) } catch { case e: Exception => println(e) - Id(getUUID(-1)) + Error(ErrorMessageEnum.SMW.toString) } } @@ -82,16 +114,17 @@ class Captcha { def checkAnswer(answer: Answer): Result = { val selectPstmt = Statements.tlStmts.get.selectPstmt - selectPstmt.setString(1, answer.id) + selectPstmt.setInt(1, Config.captchaExpiryTimeLimit) + selectPstmt.setString(2, answer.id) val rs: ResultSet = selectPstmt.executeQuery() val psOpt = if (rs.first()) { val secret = rs.getString("secret") val provider = rs.getString("provider") val check = CaptchaProviders.getProviderById(provider).checkAnswer(secret, answer.answer) - val result = if (check) "TRUE" else "FALSE" + val result = if (check) ResultEnum.TRUE.toString else ResultEnum.FALSE.toString result } else { - "EXPIRED" + ResultEnum.EXPIRED.toString } val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt deleteAnswerPstmt.setString(1, answer.id) diff --git a/src/main/scala/lc/core/captchaFields.scala b/src/main/scala/lc/core/captchaFields.scala new file mode 100644 index 0000000..5418ee9 --- /dev/null +++ b/src/main/scala/lc/core/captchaFields.scala @@ -0,0 +1,29 @@ +package lc.core + +object ParametersEnum extends Enumeration { + type Parameter = Value + + val SUPPORTEDLEVEL: Value = Value("supportedLevels") + val SUPPORTEDMEDIA: Value = Value("supportedMedia") + val SUPPORTEDINPUTTYPE: Value = Value("supportedInputType") + + val ALLOWEDLEVELS: Value = Value("allowedLevels") + val ALLOWEDMEDIA: Value = Value("allowedMedia") + val ALLOWEDINPUTTYPE: Value = Value("allowedInputType") +} + +object ResultEnum extends Enumeration { + type Result = Value + + val TRUE: Value = Value("True") + val FALSE: Value = Value("False") + val EXPIRED: Value = Value("Expired") +} + +object ErrorMessageEnum extends Enumeration { + type ErrorMessage = Value + + val SMW: Value = Value("Oops, something went worng!") + val INVALID_PARAM: Value = Value("Invalid Pramaters") + val NO_CAPTCHA: Value = Value("No captcha for the provided parameters") +} diff --git a/src/main/scala/lc/core/captchaProviders.scala b/src/main/scala/lc/core/captchaProviders.scala index 0277015..5af97a0 100644 --- a/src/main/scala/lc/core/captchaProviders.scala +++ b/src/main/scala/lc/core/captchaProviders.scala @@ -3,6 +3,7 @@ package lc.core import lc.captchas._ import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge +import scala.collection.mutable.Map object CaptchaProviders { private val providers = Map( @@ -21,10 +22,11 @@ object CaptchaProviders { } } - private val seed = System.currentTimeMillis.toString.substring(2, 6).toInt + private val seed = Config.seed private val random = new scala.util.Random(seed) + private val config = Config.captchaConfig - private def getNextRandomInt(max: Int) = + private def getNextRandomInt(max: Int): Int = random.synchronized { random.nextInt(max) } @@ -33,9 +35,31 @@ object CaptchaProviders { return providers(id) } - def getProvider(): ChallengeProvider = { - val keys = providers.keys - val providerIndex = keys.toVector(getNextRandomInt(keys.size)) - providers(providerIndex) + private def filterProviderByParam(param: Parameters): Iterable[(String, String)] = { + val configFilter = for { + configValue <- config + if configValue.allowedLevels.contains(param.level) + if configValue.allowedMedia.contains(param.media) + if configValue.allowedInputType.contains(param.input_type) + } yield (configValue.name, configValue.config) + + val providerFilter = for { + providerValue <- configFilter + providerConfigMap = providers(providerValue._1).supportedParameters() + if providerConfigMap.get(ParametersEnum.SUPPORTEDLEVEL.toString).contains(param.level) + if providerConfigMap.get(ParametersEnum.SUPPORTEDMEDIA.toString).contains(param.media) + if providerConfigMap.get(ParametersEnum.SUPPORTEDINPUTTYPE.toString).contains(param.input_type) + } yield (providerValue._1, providerValue._2) + + providerFilter + } + + def getProvider(param: Parameters): ChallengeProvider = { + val providerConfig = filterProviderByParam(param).toList + val randomIndex = getNextRandomInt(providerConfig.length) + val providerIndex = providerConfig(randomIndex)._1 + val selectedProvider = providers(providerIndex) + selectedProvider.configure(providerConfig(randomIndex)._2) + selectedProvider } } diff --git a/src/main/scala/lc/core/config.scala b/src/main/scala/lc/core/config.scala new file mode 100644 index 0000000..2bf02be --- /dev/null +++ b/src/main/scala/lc/core/config.scala @@ -0,0 +1,48 @@ +package lc.core + +import scala.io.Source.fromFile +import org.json4s.{DefaultFormats, JValue, JObject, JField, JString} +import org.json4s.jackson.JsonMethods.parse + +object Config { + + implicit val formats: DefaultFormats.type = DefaultFormats + + private val configFile = fromFile("config.json") + private val configString = + try configFile.mkString + finally configFile.close + private val configJson = parse(configString) + + val port: Int = (configJson \ "port").extract[Int] + val throttle: Int = (configJson \ "throttle").extract[Int] + val seed: Int = (configJson \ "randomSeed").extract[Int] + val captchaExpiryTimeLimit: Int = (configJson \ "captchaExpiryTimeLimit").extract[Int] + val threadDelay: Int = (configJson \ "threadDelay").extract[Int] + + private val captchaConfigJson = (configJson \ "captchas") + val captchaConfigTransform: JValue = captchaConfigJson transformField { + case JField("config", JObject(config)) => ("config", JString(config.toString)) + } + val captchaConfig: List[CaptchaConfig] = captchaConfigTransform.extract[List[CaptchaConfig]] + val allowedLevels: Set[String] = getAllValues(configJson, ParametersEnum.ALLOWEDLEVELS.toString) + val allowedMedia: Set[String] = getAllValues(configJson, ParametersEnum.ALLOWEDMEDIA.toString) + val allowedInputType: Set[String] = getAllValues(configJson, ParametersEnum.ALLOWEDINPUTTYPE.toString) + + private def getAllValues(config: JValue, param: String): Set[String] = { + val configValues = (config \\ param) + val result = for { + JObject(child) <- configValues + JField(param) <- child + } yield (param) + + var valueSet = Set[String]() + for (valueList <- result) { + for (value <- valueList._2.children) { + valueSet += value.values.toString + } + } + valueSet + } + +} diff --git a/src/main/scala/lc/core/models.scala b/src/main/scala/lc/core/models.scala index 2e6f48c..4c16333 100644 --- a/src/main/scala/lc/core/models.scala +++ b/src/main/scala/lc/core/models.scala @@ -1,7 +1,16 @@ package lc.core +sealed trait ChallengeResult case class Size(height: Int, width: Int) case class Parameters(level: String, media: String, input_type: String, size: Option[Size]) -case class Id(id: String) +case class Id(id: String) extends ChallengeResult +case class Error(message: String) extends ChallengeResult case class Answer(answer: String, id: String) case class Result(result: String) +case class CaptchaConfig( + name: String, + allowedLevels: List[String], + allowedMedia: List[String], + allowedInputType: List[String], + config: String +) diff --git a/src/main/scala/lc/database/statements.scala b/src/main/scala/lc/database/statements.scala index 24bfe24..1439b56 100644 --- a/src/main/scala/lc/database/statements.scala +++ b/src/main/scala/lc/database/statements.scala @@ -15,6 +15,8 @@ class Statements(dbConn: DBConn) { "secret varchar, " + "provider varchar, " + "contentType varchar, " + + "contentLevel varchar, " + + "contentInput varchar, " + "image blob, " + "attempted int default 0, " + "PRIMARY KEY(token))" @@ -32,8 +34,8 @@ class Statements(dbConn: DBConn) { val insertPstmt: PreparedStatement = dbConn.con.prepareStatement( "INSERT INTO " + - "challenge(id, secret, provider, contentType, image) " + - "VALUES (?, ?, ?, ?, ?)", + "challenge(id, secret, provider, contentType, contentLevel, contentInput, image) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS ) @@ -48,7 +50,7 @@ class Statements(dbConn: DBConn) { "SELECT c.secret, c.provider " + "FROM challenge c, mapId m " + "WHERE m.token=c.token AND " + - "DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, m.lastServed)) > 0 AND " + + "DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, ?, m.lastServed)) > 0 AND " + "m.uuid = ?" ) @@ -71,7 +73,10 @@ class Statements(dbConn: DBConn) { val tokenPstmt: PreparedStatement = dbConn.con.prepareStatement( "SELECT token " + "FROM challenge " + - "WHERE attempted < 10 " + + "WHERE attempted < 10 AND " + + "contentLevel = ? AND " + + "contentType = ? AND " + + "contentInput = ? " + "ORDER BY RAND() LIMIT 1" ) @@ -86,7 +91,7 @@ class Statements(dbConn: DBConn) { ) val mapIdGCPstmt: PreparedStatement = dbConn.con.prepareStatement( - "DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, lastServed)) < 0" + "DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, ?, lastServed)) < 0" ) val getCountChallengeTable: PreparedStatement = dbConn.con.prepareStatement( diff --git a/tests/locustfile.py b/tests/locustfile.py index d3a72b0..54774e0 100644 --- a/tests/locustfile.py +++ b/tests/locustfile.py @@ -7,31 +7,32 @@ class QuickStartUser(SequentialTaskSet): @task def captcha(self): - captcha_params = {"level":"some","media":"some","input_type":"some"} + # TODO: Iterate over parameters for a more comprehensive test + captcha_params = {"level":"easy","media":"image/png","input_type":"text"} resp = self.client.post(path="/v1/captcha", json=captcha_params, name="/captcha") if resp.status_code != 200: print("\nError on /captcha endpoint: ") print(resp) print(resp.text) - print("----------------END.C-------------------\n\n") + print("----------------END.CAPTCHA-------------------\n\n") uuid = json.loads(resp.text).get("id") answerBody = {"answer": "qwer123","id": uuid} resp = self.client.get(path="/v1/media?id=%s" % uuid, name="/media") if resp.status_code != 200: - print("\nError on /captcha endpoint: ") + print("\nError on /media endpoint: ") print(resp) print(resp.text) - print("----------------END.C-------------------\n\n") + print("----------------END.MEDIA-------------------\n\n") resp = self.client.post(path='/v1/answer', json=answerBody, name="/answer") if resp.status_code != 200: - print("\nError on /captcha endpoint: ") + print("\nError on /answer endpoint: ") print(resp) print(resp.text) - print("----------------END.C-------------------\n\n") + print("----------------END.ANSWER-------------------\n\n") class User(FastHttpUser):