mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-01-13 06:53:19 -05:00
commit
055d999e17
4
.github/workflows/scala.yml
vendored
4
.github/workflows/scala.yml
vendored
@ -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
|
||||
|
@ -21,7 +21,6 @@ scalacOptions ++= List(
|
||||
"-Ywarn-unused"
|
||||
)
|
||||
javacOptions += "-g:none"
|
||||
scalafmtOnCompile := true
|
||||
compileOrder := CompileOrder.JavaThenScala
|
||||
|
||||
fork in run := true
|
||||
|
39
config.json
39
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":{}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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<String, List<String>> supportedParameters() {
|
||||
HashMap<String, List<String>> supportedParams = new HashMap<String, List<String>>();
|
||||
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 =
|
||||
|
@ -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<String, List<String>> supportedParameters() {
|
||||
HashMap<String, List<String>> supportedParams = new HashMap<String, List<String>>();
|
||||
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());
|
||||
|
@ -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<String, List<String>> supportedParameters() {
|
||||
HashMap<String, List<String>> supportedParams = new HashMap<String, List<String>>();
|
||||
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);
|
||||
}
|
||||
|
@ -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<String, List<String>> supportedParameters();
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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) }
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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,9 +53,29 @@ 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 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"))
|
||||
@ -60,14 +83,23 @@ class Captcha {
|
||||
None
|
||||
}
|
||||
val updateAttemptedPstmt = Statements.tlStmts.get.updateAttemptedPstmt
|
||||
val uuid = getUUID(tokenOpt.getOrElse(generateChallenge(param)))
|
||||
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 {
|
||||
Error(ErrorMessageEnum.INVALID_PARAM.toString)
|
||||
}
|
||||
} 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)
|
||||
|
29
src/main/scala/lc/core/captchaFields.scala
Normal file
29
src/main/scala/lc/core/captchaFields.scala
Normal file
@ -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")
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
48
src/main/scala/lc/core/config.scala
Normal file
48
src/main/scala/lc/core/config.scala
Normal file
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user