Refactor modules (#56)
* Refactor:Modules Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Add config file Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Restore sample images Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>
This commit is contained in:
parent
b66f777828
commit
6d04cdc3b4
|
@ -5,6 +5,9 @@
|
|||
/project/**
|
||||
/target/
|
||||
**__pycache__
|
||||
.bloop
|
||||
.metals
|
||||
.vscode
|
||||
|
||||
# for various captcha
|
||||
/known/
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"randomSeed": 20,
|
||||
"captchaExpiryTimeLimit": 5,
|
||||
"captchas":{
|
||||
"FilterChallenge":{
|
||||
"name": "FilterChallenge",
|
||||
"supportedLevels":["medium", "hard"],
|
||||
"supportedMedia":["image"],
|
||||
"supportedinputType":["text"],
|
||||
"config":{}
|
||||
},
|
||||
"GifCaptcha":{
|
||||
"name": "GifCaptcha",
|
||||
"supportedLevels":["hard"],
|
||||
"supportedMedia":["gif"],
|
||||
"supportedinputType":["text"],
|
||||
"config":{}
|
||||
},
|
||||
"ShadowTextCaptcha":{
|
||||
"name": "ShadowTextCaptcha",
|
||||
"supportedLevels":["easy"],
|
||||
"supportedMedia":["image"],
|
||||
"supportedinputType":["text"],
|
||||
"config": {}
|
||||
},
|
||||
"RainDropsCaptcha":{
|
||||
"name": "RainDropsCaptcha",
|
||||
"supportedLevels":["easy","medium"],
|
||||
"supportedMedia":["image"],
|
||||
"supportedinputType":["text"],
|
||||
"config":{}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package lc;
|
||||
package lc.captchas;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
|
@ -6,6 +6,9 @@ import java.awt.image.BufferedImage;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import lc.captchas.interfaces.Challenge;
|
||||
import lc.captchas.interfaces.ChallengeProvider;
|
||||
import lc.misc.HelperFunctions;
|
||||
|
||||
public class FontFunCaptcha implements ChallengeProvider{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lc;
|
||||
package lc.captchas;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
|
@ -9,6 +9,10 @@ import java.io.IOException;
|
|||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import lc.captchas.interfaces.Challenge;
|
||||
import lc.captchas.interfaces.ChallengeProvider;
|
||||
import lc.misc.HelperFunctions;
|
||||
import lc.misc.GifSequenceWriter;
|
||||
|
||||
public class GifCaptcha implements ChallengeProvider{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lc;
|
||||
package lc.captchas;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.Graphics2D;
|
||||
|
@ -10,11 +10,14 @@ import java.awt.image.BufferedImage;
|
|||
import java.awt.image.ConvolveOp;
|
||||
import java.awt.image.Kernel;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import lc.misc.HelperFunctions;
|
||||
import lc.captchas.interfaces.Challenge;
|
||||
import lc.captchas.interfaces.ChallengeProvider;
|
||||
|
||||
public class ShadowTextCaptcha implements ChallengeProvider{
|
||||
|
||||
public String getId() {
|
||||
return "ShadowText";
|
||||
return "ShadowTextCaptcha";
|
||||
}
|
||||
|
||||
public boolean checkAnswer(String secret, String answer) {
|
|
@ -1,4 +1,4 @@
|
|||
package lc;
|
||||
package lc.captchas.interfaces;
|
||||
|
||||
public class Challenge {
|
||||
public final byte[] content;
|
|
@ -1,6 +1,6 @@
|
|||
package lc;
|
||||
package lc.captchas.interfaces;
|
||||
|
||||
interface ChallengeProvider {
|
||||
public interface ChallengeProvider {
|
||||
public String getId();
|
||||
public Challenge returnChallenge();
|
||||
public boolean checkAnswer(String secret, String answer);
|
|
@ -1,7 +1,7 @@
|
|||
// This code was adapted from http://elliot.kroo.net/software/java/GifSequenceWriter/
|
||||
// It was available under CC By 3.0
|
||||
|
||||
package lc;
|
||||
package lc.misc;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.*;
|
||||
import javax.imageio.stream.*;
|
|
@ -1,4 +1,4 @@
|
|||
package lc;
|
||||
package lc.misc;
|
||||
|
||||
import java.awt.*;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lc;/*
|
||||
package lc.server;/*
|
||||
* Copyright © 2005-2018 Amichai Rothman
|
||||
*
|
||||
* This file is part of JLHTTP - the Java Lightweight HTTP Server.
|
|
@ -1,222 +1,20 @@
|
|||
package lc
|
||||
|
||||
import com.sksamuel.scrimage._
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.util.concurrent._
|
||||
import java.util.UUID
|
||||
import java.sql.{Blob, ResultSet}
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.io._
|
||||
import java.sql.Statement
|
||||
import org.json4s.DefaultFormats
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import scala.io.Source.fromFile
|
||||
import lc.database.Statements
|
||||
import lc.core.{Captcha, CaptchaProviders}
|
||||
import lc.server.Server
|
||||
import lc.background.BackgroundTask
|
||||
|
||||
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 Answer(answer: String, id: String)
|
||||
|
||||
case class ProviderSecret(provider: String, secret: String)
|
||||
|
||||
object CaptchaProviders {
|
||||
val providers = Map(
|
||||
"FilterChallenge" -> new FilterChallenge,
|
||||
// "FontFunCaptcha" -> new FontFunCaptcha,
|
||||
"GifCaptcha" -> new GifCaptcha,
|
||||
"ShadowTextCaptcha" -> new ShadowTextCaptcha,
|
||||
"RainDropsCaptcha" -> new RainDropsCP,
|
||||
// "LabelCaptcha" -> new LabelCaptcha
|
||||
)
|
||||
|
||||
def generateChallengeSamples() = {
|
||||
providers.map {case (key, provider) =>
|
||||
(key, provider.returnChallenge())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Statements(dbConn: DBConn) {
|
||||
val insertPstmt = dbConn.con.prepareStatement("INSERT INTO challenge(id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS )
|
||||
val mapPstmt = dbConn.con.prepareStatement("INSERT INTO mapId(uuid, token, lastServed) VALUES (?, ?, CURRENT_TIMESTAMP)")
|
||||
val selectPstmt = dbConn.con.prepareStatement("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 m.uuid = ?")
|
||||
val imagePstmt = dbConn.con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
||||
val updateAttemptedPstmt = dbConn.con.prepareStatement("UPDATE challenge SET attempted = attempted+1 WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
|
||||
val tokenPstmt = dbConn.con.prepareStatement("SELECT token FROM challenge WHERE attempted < 10 ORDER BY RAND() LIMIT 1")
|
||||
val deleteAnswerPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE uuid = ?")
|
||||
val challengeGCPstmt = dbConn.con.prepareStatement("DELETE FROM challenge WHERE attempted >= 10 AND token NOT IN (SELECT token FROM mapId)")
|
||||
val mapIdGCPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, lastServed)) < 0")
|
||||
}
|
||||
|
||||
object Statements {
|
||||
var dbConn: DBConn = _
|
||||
val tlStmts = ThreadLocal.withInitial(() => new Statements(dbConn))
|
||||
}
|
||||
|
||||
class Captcha(throttle: Int, dbConn: DBConn) {
|
||||
import CaptchaProviders._
|
||||
|
||||
private val stmt = dbConn.getStatement()
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token int auto_increment, id varchar, secret varchar, provider varchar, contentType varchar, image blob, attempted int default 0, PRIMARY KEY(token))")
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS mapId(uuid varchar, token int, lastServed timestamp, PRIMARY KEY(uuid), FOREIGN KEY(token) REFERENCES challenge(token) ON DELETE CASCADE)")
|
||||
|
||||
private val seed = System.currentTimeMillis.toString.substring(2,6).toInt
|
||||
private val random = new scala.util.Random(seed)
|
||||
|
||||
def getNextRandomInt(max: Int) = random.synchronized {
|
||||
random.nextInt(max)
|
||||
}
|
||||
|
||||
def getProvider(): String = {
|
||||
val keys = providers.keys
|
||||
val providerIndex = keys.toVector(getNextRandomInt(keys.size))
|
||||
providerIndex
|
||||
}
|
||||
|
||||
def getCaptcha(id: Id): Array[Byte] = {
|
||||
var image :Array[Byte] = null
|
||||
var blob: Blob = null
|
||||
try {
|
||||
val imagePstmt = Statements.tlStmts.get.imagePstmt
|
||||
imagePstmt.setString(1, id.id)
|
||||
val rs: ResultSet = imagePstmt.executeQuery()
|
||||
if(rs.next()){
|
||||
blob = rs.getBlob("image")
|
||||
if(blob != null){
|
||||
image = blob.getBytes(1, blob.length().toInt)
|
||||
}
|
||||
}
|
||||
image
|
||||
} catch { case e: Exception =>
|
||||
println(e)
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
private val uniqueIntCount = new AtomicInteger()
|
||||
|
||||
def generateChallenge(param: Parameters): Int = {
|
||||
//TODO: eval params to choose a provider
|
||||
val providerMap = getProvider()
|
||||
val provider = providers(providerMap)
|
||||
val challenge = provider.returnChallenge()
|
||||
val blob = new ByteArrayInputStream(challenge.content)
|
||||
val insertPstmt = Statements.tlStmts.get.insertPstmt
|
||||
insertPstmt.setString(1, provider.getId)
|
||||
insertPstmt.setString(2, challenge.secret)
|
||||
insertPstmt.setString(3, providerMap)
|
||||
insertPstmt.setString(4, challenge.contentType)
|
||||
insertPstmt.setBlob(5, blob)
|
||||
insertPstmt.executeUpdate()
|
||||
val rs: ResultSet = insertPstmt.getGeneratedKeys()
|
||||
val token = if(rs.next()){
|
||||
rs.getInt("token")
|
||||
}
|
||||
println("Added new challenge: "+ token.toString)
|
||||
token.asInstanceOf[Int]
|
||||
}
|
||||
|
||||
val task = new Runnable {
|
||||
def run(): Unit = {
|
||||
try {
|
||||
|
||||
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
|
||||
mapIdGCPstmt.executeUpdate()
|
||||
|
||||
val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt
|
||||
challengeGCPstmt.executeUpdate()
|
||||
|
||||
val imageNum = stmt.executeQuery("SELECT COUNT(*) AS total FROM challenge")
|
||||
var throttleIn = (throttle*1.1).toInt
|
||||
if(imageNum.next())
|
||||
throttleIn = (throttleIn-imageNum.getInt("total"))
|
||||
while(0 < throttleIn){
|
||||
generateChallenge(Parameters("","","",Option(Size(0,0))))
|
||||
throttleIn -= 1
|
||||
}
|
||||
} catch { case e: Exception => println(e) }
|
||||
}
|
||||
}
|
||||
|
||||
def beginThread(delay: Int) : Unit = {
|
||||
val ex = new ScheduledThreadPoolExecutor(1)
|
||||
val thread = ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
def getChallenge(param: Parameters): Id = {
|
||||
try {
|
||||
val tokenPstmt = Statements.tlStmts.get.tokenPstmt
|
||||
val rs = tokenPstmt.executeQuery()
|
||||
val tokenOpt = if(rs.next()) {
|
||||
Some(rs.getInt("token"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
def getUUID(id: Int): String = {
|
||||
val uuid = UUID.randomUUID().toString
|
||||
val mapPstmt = Statements.tlStmts.get.mapPstmt
|
||||
mapPstmt.setString(1,uuid)
|
||||
mapPstmt.setInt(2,id)
|
||||
mapPstmt.executeUpdate()
|
||||
uuid
|
||||
}
|
||||
|
||||
def checkAnswer(answer: Answer): String = {
|
||||
val selectPstmt = Statements.tlStmts.get.selectPstmt
|
||||
selectPstmt.setString(1, answer.id)
|
||||
val rs: ResultSet = selectPstmt.executeQuery()
|
||||
val psOpt = if (rs.first()) {
|
||||
val secret = rs.getString("secret")
|
||||
val provider = rs.getString("provider")
|
||||
val check = providers(provider).checkAnswer(secret, answer.answer)
|
||||
val result = if(check) "TRUE" else "FALSE"
|
||||
result
|
||||
} else {
|
||||
"EXPIRED"
|
||||
}
|
||||
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
||||
deleteAnswerPstmt.setString(1, answer.id)
|
||||
deleteAnswerPstmt.executeUpdate()
|
||||
psOpt
|
||||
}
|
||||
|
||||
def display(): Unit = {
|
||||
val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge")
|
||||
println("token\t\tid\t\tsecret\t\tattempted")
|
||||
while(rs.next()) {
|
||||
val token = rs.getInt("token")
|
||||
val id = rs.getString("id")
|
||||
val secret = rs.getString("secret")
|
||||
val attempted = rs.getString("attempted")
|
||||
println(s"${token}\t\t${id}\t\t${secret}\t\t${attempted}\n\n")
|
||||
}
|
||||
|
||||
val rss: ResultSet = stmt.executeQuery("SELECT * FROM mapId")
|
||||
println("uuid\t\ttoken\t\tlastServed")
|
||||
while(rss.next()){
|
||||
val uuid = rss.getString("uuid")
|
||||
val token = rss.getInt("token")
|
||||
val lastServed = rss.getTimestamp("lastServed")
|
||||
println(s"${uuid}\t\t${token}\t\t${lastServed}\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object LCFramework{
|
||||
def main(args: scala.Array[String]) {
|
||||
val dbConn = new DBConn()
|
||||
Statements.dbConn = dbConn
|
||||
val captcha = new Captcha(2, dbConn)
|
||||
val server = new Server(8888, captcha, dbConn)
|
||||
captcha.beginThread(2)
|
||||
val captcha = new Captcha()
|
||||
val server = new Server(8888, captcha)
|
||||
val backgroudTask = new BackgroundTask(captcha, 10)
|
||||
backgroudTask.beginThread(2)
|
||||
server.start()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package lc.background
|
||||
|
||||
import lc.database.Statements
|
||||
import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
|
||||
import lc.core.Captcha
|
||||
import lc.core.{Parameters, Size}
|
||||
|
||||
|
||||
class BackgroundTask(captcha: Captcha, throttle: Int) {
|
||||
|
||||
private val task = new Runnable {
|
||||
def run(): Unit = {
|
||||
try {
|
||||
|
||||
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
|
||||
mapIdGCPstmt.executeUpdate()
|
||||
|
||||
val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt
|
||||
challengeGCPstmt.executeUpdate()
|
||||
|
||||
val imageNum = Statements.tlStmts.get.getCountChallengeTable.executeQuery()
|
||||
var throttleIn = (throttle*1.1).toInt
|
||||
if(imageNum.next())
|
||||
throttleIn = (throttleIn-imageNum.getInt("total"))
|
||||
while(0 < throttleIn){
|
||||
captcha.generateChallenge(Parameters("","","",Option(Size(0,0))))
|
||||
throttleIn -= 1
|
||||
}
|
||||
} catch { case e: Exception => println(e) }
|
||||
}
|
||||
}
|
||||
|
||||
def beginThread(delay: Int) : Unit = {
|
||||
val ex = new ScheduledThreadPoolExecutor(1)
|
||||
val thread = ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
package lc
|
||||
package lc.captchas
|
||||
|
||||
import com.sksamuel.scrimage._
|
||||
import com.sksamuel.scrimage.filter._
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.Font
|
||||
import java.awt.Color
|
||||
import lc.captchas.interfaces.ChallengeProvider
|
||||
import lc.captchas.interfaces.Challenge
|
||||
|
||||
|
||||
class FilterChallenge extends ChallengeProvider {
|
||||
def getId = "FilterChallenge"
|
|
@ -1,4 +1,4 @@
|
|||
package lc
|
||||
package lc.captchas
|
||||
|
||||
import java.io.File
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
@ -7,6 +7,8 @@ import scala.collection.mutable.Map
|
|||
import java.nio.file.{Files,Path,StandardCopyOption}
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.{Graphics2D,Color}
|
||||
import lc.captchas.interfaces.ChallengeProvider
|
||||
import lc.captchas.interfaces.Challenge
|
||||
|
||||
class LabelCaptcha extends ChallengeProvider {
|
||||
private var knownFiles = new File("known").list.toList
|
|
@ -1,4 +1,4 @@
|
|||
package lc
|
||||
package lc.captchas
|
||||
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.RenderingHints
|
||||
|
@ -9,6 +9,9 @@ import java.io.ByteArrayOutputStream
|
|||
import javax.imageio.ImageIO
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||
import lc.captchas.interfaces.ChallengeProvider
|
||||
import lc.captchas.interfaces.Challenge
|
||||
import lc.misc.GifSequenceWriter
|
||||
|
||||
class Drop {
|
||||
var x = 0
|
|
@ -0,0 +1,121 @@
|
|||
package lc.core
|
||||
|
||||
import org.json4s.JsonAST.JValue
|
||||
import java.sql.{Blob, ResultSet}
|
||||
import java.util.UUID
|
||||
import java.io.ByteArrayInputStream
|
||||
import lc.database.Statements
|
||||
import lc.core.CaptchaProviders
|
||||
|
||||
class Captcha {
|
||||
|
||||
def getCaptcha(id: Id): Array[Byte] = {
|
||||
var image :Array[Byte] = null
|
||||
var blob: Blob = null
|
||||
try {
|
||||
val imagePstmt = Statements.tlStmts.get.imagePstmt
|
||||
imagePstmt.setString(1, id.id)
|
||||
val rs: ResultSet = imagePstmt.executeQuery()
|
||||
if(rs.next()){
|
||||
blob = rs.getBlob("image")
|
||||
if(blob != null){
|
||||
image = blob.getBytes(1, blob.length().toInt)
|
||||
}
|
||||
}
|
||||
image
|
||||
} catch { case e: Exception =>
|
||||
println(e)
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
def generateChallenge(param: Parameters): Int = {
|
||||
//TODO: eval params to choose a provider
|
||||
val provider = CaptchaProviders.getProvider()
|
||||
val providerId = provider.getId()
|
||||
val challenge = provider.returnChallenge()
|
||||
val blob = new ByteArrayInputStream(challenge.content)
|
||||
val insertPstmt = Statements.tlStmts.get.insertPstmt
|
||||
insertPstmt.setString(1, provider.getId)
|
||||
insertPstmt.setString(2, challenge.secret)
|
||||
insertPstmt.setString(3, providerId)
|
||||
insertPstmt.setString(4, challenge.contentType)
|
||||
insertPstmt.setBlob(5, blob)
|
||||
insertPstmt.executeUpdate()
|
||||
val rs: ResultSet = insertPstmt.getGeneratedKeys()
|
||||
val token = if(rs.next()){
|
||||
rs.getInt("token")
|
||||
}
|
||||
println("Added new challenge: "+ token.toString)
|
||||
token.asInstanceOf[Int]
|
||||
}
|
||||
|
||||
def getChallenge(param: Parameters): Id = {
|
||||
try {
|
||||
val tokenPstmt = Statements.tlStmts.get.tokenPstmt
|
||||
val rs = tokenPstmt.executeQuery()
|
||||
val tokenOpt = if(rs.next()) {
|
||||
Some(rs.getInt("token"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
private def getUUID(id: Int): String = {
|
||||
val uuid = UUID.randomUUID().toString
|
||||
val mapPstmt = Statements.tlStmts.get.mapPstmt
|
||||
mapPstmt.setString(1,uuid)
|
||||
mapPstmt.setInt(2,id)
|
||||
mapPstmt.executeUpdate()
|
||||
uuid
|
||||
}
|
||||
|
||||
def checkAnswer(answer: Answer): Result = {
|
||||
val selectPstmt = Statements.tlStmts.get.selectPstmt
|
||||
selectPstmt.setString(1, 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"
|
||||
result
|
||||
} else {
|
||||
"EXPIRED"
|
||||
}
|
||||
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
||||
deleteAnswerPstmt.setString(1, answer.id)
|
||||
deleteAnswerPstmt.executeUpdate()
|
||||
Result(psOpt)
|
||||
}
|
||||
|
||||
def display(): Unit = {
|
||||
val rs: ResultSet = Statements.tlStmts.get.getChallengeTable.executeQuery()
|
||||
println("token\t\tid\t\tsecret\t\tattempted")
|
||||
while(rs.next()) {
|
||||
val token = rs.getInt("token")
|
||||
val id = rs.getString("id")
|
||||
val secret = rs.getString("secret")
|
||||
val attempted = rs.getString("attempted")
|
||||
println(s"${token}\t\t${id}\t\t${secret}\t\t${attempted}\n\n")
|
||||
}
|
||||
|
||||
val rss: ResultSet = Statements.tlStmts.get.getMapIdTable.executeQuery()
|
||||
println("uuid\t\ttoken\t\tlastServed")
|
||||
while(rss.next()){
|
||||
val uuid = rss.getString("uuid")
|
||||
val token = rss.getInt("token")
|
||||
val lastServed = rss.getTimestamp("lastServed")
|
||||
println(s"${uuid}\t\t${token}\t\t${lastServed}\n\n")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package lc.core
|
||||
|
||||
import lc.captchas._
|
||||
import lc.captchas.interfaces.ChallengeProvider
|
||||
|
||||
object CaptchaProviders {
|
||||
private val providers = Map(
|
||||
"FilterChallenge" -> new FilterChallenge,
|
||||
//"FontFunCaptcha" -> new FontFunCaptcha,
|
||||
"GifCaptcha" -> new GifCaptcha,
|
||||
"ShadowTextCaptcha" -> new ShadowTextCaptcha,
|
||||
"RainDropsCaptcha" -> new RainDropsCP,
|
||||
//"LabelCaptcha" -> new LabelCaptcha
|
||||
)
|
||||
|
||||
def generateChallengeSamples() = {
|
||||
providers.map {case (key, provider) =>
|
||||
(key, provider.returnChallenge())
|
||||
}
|
||||
}
|
||||
|
||||
private val seed = System.currentTimeMillis.toString.substring(2,6).toInt
|
||||
private val random = new scala.util.Random(seed)
|
||||
|
||||
private def getNextRandomInt(max: Int) = random.synchronized {
|
||||
random.nextInt(max)
|
||||
}
|
||||
|
||||
def getProviderById(id: String): ChallengeProvider = {
|
||||
return providers(id)
|
||||
}
|
||||
|
||||
def getProvider(): ChallengeProvider = {
|
||||
val keys = providers.keys
|
||||
val providerIndex = keys.toVector(getNextRandomInt(keys.size))
|
||||
providers(providerIndex)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package lc.core
|
||||
|
||||
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 Answer(answer: String, id: String)
|
||||
case class Result(result: String)
|
|
@ -1,4 +1,4 @@
|
|||
package lc
|
||||
package lc.database
|
||||
|
||||
import java.sql._
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package lc.database
|
||||
|
||||
import lc.database.DBConn
|
||||
import java.sql.Statement
|
||||
|
||||
class Statements(dbConn: DBConn) {
|
||||
|
||||
private val stmt = dbConn.getStatement()
|
||||
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token int auto_increment, id varchar, secret varchar, provider varchar, contentType varchar, image blob, attempted int default 0, PRIMARY KEY(token))")
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS mapId(uuid varchar, token int, lastServed timestamp, PRIMARY KEY(uuid), FOREIGN KEY(token) REFERENCES challenge(token) ON DELETE CASCADE)")
|
||||
|
||||
val insertPstmt = dbConn.con.prepareStatement("INSERT INTO challenge(id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS )
|
||||
val mapPstmt = dbConn.con.prepareStatement("INSERT INTO mapId(uuid, token, lastServed) VALUES (?, ?, CURRENT_TIMESTAMP)")
|
||||
val selectPstmt = dbConn.con.prepareStatement("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 m.uuid = ?")
|
||||
val imagePstmt = dbConn.con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
||||
val updateAttemptedPstmt = dbConn.con.prepareStatement("UPDATE challenge SET attempted = attempted+1 WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
|
||||
val tokenPstmt = dbConn.con.prepareStatement("SELECT token FROM challenge WHERE attempted < 10 ORDER BY RAND() LIMIT 1")
|
||||
val deleteAnswerPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE uuid = ?")
|
||||
val challengeGCPstmt = dbConn.con.prepareStatement("DELETE FROM challenge WHERE attempted >= 10 AND token NOT IN (SELECT token FROM mapId)")
|
||||
val mapIdGCPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, lastServed)) < 0")
|
||||
|
||||
val getCountChallengeTable = dbConn.con.prepareStatement("SELECT COUNT(*) AS total FROM challenge")
|
||||
val getChallengeTable = dbConn.con.prepareStatement("SELECT * FROM challenge")
|
||||
val getMapIdTable = dbConn.con.prepareStatement("SELECT * FROM mapId")
|
||||
}
|
||||
|
||||
object Statements {
|
||||
private val dbConn: DBConn = new DBConn()
|
||||
val tlStmts = ThreadLocal.withInitial(() => new Statements(dbConn))
|
||||
}
|
|
@ -1,27 +1,26 @@
|
|||
package lc
|
||||
package lc.server
|
||||
|
||||
import java.io.File
|
||||
import org.json4s._
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import org.json4s.jackson.Serialization.{read, write}
|
||||
import org.json4s.DefaultFormats
|
||||
import org.json4s.jackson.JsonMethods.parse
|
||||
import org.json4s.jackson.Serialization.write
|
||||
import lc.core.Captcha
|
||||
import lc.core.{Parameters, Id, Answer}
|
||||
import lc.server.HTTPServer
|
||||
|
||||
import lc.HTTPServer._
|
||||
|
||||
case class Secret(token: Int)
|
||||
|
||||
class Server(port: Int, captcha: Captcha, dbConn: DBConn){
|
||||
class Server(port: Int, captcha: Captcha){
|
||||
val server = new HTTPServer(port)
|
||||
val host = server.getVirtualHost(null)
|
||||
|
||||
implicit val formats = DefaultFormats
|
||||
|
||||
host.addContext("/v1/captcha",(req, resp) => {
|
||||
val body = req.getJson()
|
||||
val body = req.getJson()
|
||||
val json = parse(body)
|
||||
val param = json.extract[Parameters]
|
||||
val id = captcha.getChallenge(param)
|
||||
resp.getHeaders().add("Content-Type","application/json")
|
||||
resp.send(200, write(id))
|
||||
val id = captcha.getChallenge(param)
|
||||
resp.getHeaders().add("Content-Type","application/json")
|
||||
resp.send(200, write(id))
|
||||
0
|
||||
},"POST")
|
||||
|
||||
|
@ -40,8 +39,7 @@ class Server(port: Int, captcha: Captcha, dbConn: DBConn){
|
|||
val answer = json.extract[Answer]
|
||||
val result = captcha.checkAnswer(answer)
|
||||
resp.getHeaders().add("Content-Type","application/json")
|
||||
val responseContent = s"""{"result":"$result"}"""
|
||||
resp.send(200,responseContent)
|
||||
resp.send(200, write(result))
|
||||
0
|
||||
},"POST")
|
||||
|
Loading…
Reference in New Issue