mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-04-01 01:33:38 -04:00
Fix issue in GC (#54)
* Update sql to map uuid to token Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Fix millis to secs conversion Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Add synchronisation to media enpoint DB access Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Change error code for rate limiter Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * move prepared statements to Thread Local Storage * Change test end points * init GC * Add GC Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Change status return Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Auto generate token in db Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Remove user management and rate limiting Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Add seed for random number generator Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Store random instance as class member Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Update locustfile Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Add API documentation Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Move updateTimeStamp to getChallenge methdod Remove user tables for the DB Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Update Timestamp when creating mapId entry Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Add request method type Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Minor fixes Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Fix issue in GC Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Change db directory Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Update locust test Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com> * Update .gitignore
This commit is contained in:
parent
5c3bdfeb83
commit
b16c2698d2
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,10 +1,10 @@
|
|||||||
/*.log
|
/*.log
|
||||||
/*.png
|
/*.png
|
||||||
/*.db
|
**/*.db
|
||||||
/bin/
|
/bin/
|
||||||
/project/target/
|
/project/**
|
||||||
/project/project/
|
|
||||||
/target/
|
/target/
|
||||||
|
**__pycache__
|
||||||
|
|
||||||
# for various captcha
|
# for various captcha
|
||||||
/known/
|
/known/
|
||||||
|
@ -3,7 +3,7 @@ package lc
|
|||||||
import java.sql._
|
import java.sql._
|
||||||
|
|
||||||
class DBConn(){
|
class DBConn(){
|
||||||
val con: Connection = DriverManager.getConnection("jdbc:h2:./captcha", "sa", "")
|
val con: Connection = DriverManager.getConnection("jdbc:h2:./data/H2/captcha", "sa", "")
|
||||||
|
|
||||||
def getStatement(): Statement = {
|
def getStatement(): Statement = {
|
||||||
con.createStatement()
|
con.createStatement()
|
||||||
|
@ -36,10 +36,13 @@ object CaptchaProviders {
|
|||||||
class Statements(dbConn: DBConn) {
|
class Statements(dbConn: DBConn) {
|
||||||
val insertPstmt = dbConn.con.prepareStatement("INSERT INTO challenge(id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS )
|
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 mapPstmt = dbConn.con.prepareStatement("INSERT INTO mapId(uuid, token, lastServed) VALUES (?, ?, CURRENT_TIMESTAMP)")
|
||||||
val selectPstmt = dbConn.con.prepareStatement("SELECT secret, provider FROM challenge WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ? AND DATEDIFF(MINUTE, DATEADD(MINUTE,2,m.lastServed), CURRENT_TIMESTAMP) <= 0)")
|
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 imagePstmt = dbConn.con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
||||||
val updateSolvedPstmt = dbConn.con.prepareStatement("UPDATE challenge SET solved = solved+1 WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.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 solved < 10 ORDER BY RAND() LIMIT 1")
|
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 {
|
object Statements {
|
||||||
@ -51,7 +54,7 @@ class Captcha(throttle: Int, dbConn: DBConn) {
|
|||||||
import CaptchaProviders._
|
import CaptchaProviders._
|
||||||
|
|
||||||
private val stmt = dbConn.getStatement()
|
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, solved int default 0, PRIMARY KEY(token))")
|
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)")
|
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 seed = System.currentTimeMillis.toString.substring(2,6).toInt
|
||||||
@ -75,16 +78,16 @@ class Captcha(throttle: Int, dbConn: DBConn) {
|
|||||||
imagePstmt.setString(1, id.id)
|
imagePstmt.setString(1, id.id)
|
||||||
val rs: ResultSet = imagePstmt.executeQuery()
|
val rs: ResultSet = imagePstmt.executeQuery()
|
||||||
if(rs.next()){
|
if(rs.next()){
|
||||||
blob = rs.getBlob("image")
|
blob = rs.getBlob("image")
|
||||||
if(blob != null)
|
if(blob != null){
|
||||||
image = blob.getBytes(1, blob.length().toInt)
|
image = blob.getBytes(1, blob.length().toInt)
|
||||||
image
|
}
|
||||||
|
}
|
||||||
|
image
|
||||||
|
} catch { case e: Exception =>
|
||||||
|
println(e)
|
||||||
|
image
|
||||||
}
|
}
|
||||||
image
|
|
||||||
} catch{ case e: Exception =>
|
|
||||||
println(e)
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val uniqueIntCount = new AtomicInteger()
|
private val uniqueIntCount = new AtomicInteger()
|
||||||
@ -112,19 +115,23 @@ class Captcha(throttle: Int, dbConn: DBConn) {
|
|||||||
|
|
||||||
val task = new Runnable {
|
val task = new Runnable {
|
||||||
def run(): Unit = {
|
def run(): Unit = {
|
||||||
try {
|
try {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
val gcStmt = stmt.executeUpdate("DELETE FROM challenge WHERE solved > 10 AND token = (SELECT m.token FROM mapId m, challenge c WHERE c.token = m.token AND m.lastServed = (SELECT MAX(m.lastServed) FROM mapId m, challenge c WHERE c.token=m.token AND DATEDIFF(MINUTE, DATEADD(MINUTE,5,m.lastServed), CURRENT_TIMESTAMP) <= 0))")
|
|
||||||
|
|
||||||
} catch { case e: Exception => println(e) }
|
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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +149,11 @@ class Captcha(throttle: Int, dbConn: DBConn) {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Id(getUUID(tokenOpt.getOrElse(generateChallenge(param))))
|
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 =>
|
} catch {case e: Exception =>
|
||||||
println(e)
|
println(e)
|
||||||
Id(getUUID(-1))
|
Id(getUUID(-1))
|
||||||
@ -166,30 +177,26 @@ class Captcha(throttle: Int, dbConn: DBConn) {
|
|||||||
val secret = rs.getString("secret")
|
val secret = rs.getString("secret")
|
||||||
val provider = rs.getString("provider")
|
val provider = rs.getString("provider")
|
||||||
val check = providers(provider).checkAnswer(secret, answer.answer)
|
val check = providers(provider).checkAnswer(secret, answer.answer)
|
||||||
val result = if(check) {
|
val result = if(check) "TRUE" else "FALSE"
|
||||||
val updateSolvedPstmt = Statements.tlStmts.get.updateSolvedPstmt
|
|
||||||
updateSolvedPstmt.setString(1,answer.id)
|
|
||||||
updateSolvedPstmt.executeUpdate()
|
|
||||||
"TRUE"
|
|
||||||
} else {
|
|
||||||
"FALSE"
|
|
||||||
}
|
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
"EXPIRED"
|
"EXPIRED"
|
||||||
}
|
}
|
||||||
|
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
||||||
|
deleteAnswerPstmt.setString(1, answer.id)
|
||||||
|
deleteAnswerPstmt.executeUpdate()
|
||||||
psOpt
|
psOpt
|
||||||
}
|
}
|
||||||
|
|
||||||
def display(): Unit = {
|
def display(): Unit = {
|
||||||
val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge")
|
val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge")
|
||||||
println("token\t\tid\t\tsecret\t\tsolved")
|
println("token\t\tid\t\tsecret\t\tattempted")
|
||||||
while(rs.next()) {
|
while(rs.next()) {
|
||||||
val token = rs.getInt("token")
|
val token = rs.getInt("token")
|
||||||
val id = rs.getString("id")
|
val id = rs.getString("id")
|
||||||
val secret = rs.getString("secret")
|
val secret = rs.getString("secret")
|
||||||
val solved = rs.getString("solved")
|
val attempted = rs.getString("attempted")
|
||||||
println(s"${token}\t\t${id}\t\t${secret}\t\t${solved}\n\n")
|
println(s"${token}\t\t${id}\t\t${secret}\t\t${attempted}\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
val rss: ResultSet = stmt.executeQuery("SELECT * FROM mapId")
|
val rss: ResultSet = stmt.executeQuery("SELECT * FROM mapId")
|
||||||
|
@ -26,14 +26,15 @@ class Server(port: Int, captcha: Captcha, dbConn: DBConn){
|
|||||||
},"POST")
|
},"POST")
|
||||||
|
|
||||||
host.addContext("/v1/media",(req, resp) => {
|
host.addContext("/v1/media",(req, resp) => {
|
||||||
var id = Id(null)
|
val id = if ("GET" == req.getMethod()){
|
||||||
if ("GET" == req.getMethod()){
|
|
||||||
val params = req.getParams()
|
val params = req.getParams()
|
||||||
id = Id(params.get("id"))
|
val gid = Id(params.get("id"))
|
||||||
|
gid
|
||||||
} else {
|
} else {
|
||||||
val body = req.getJson()
|
val body = req.getJson()
|
||||||
val json = parse(body)
|
val json = parse(body)
|
||||||
id = json.extract[Id]
|
val gid = json.extract[Id]
|
||||||
|
gid
|
||||||
}
|
}
|
||||||
val image = captcha.getCaptcha(id)
|
val image = captcha.getCaptcha(id)
|
||||||
resp.getHeaders().add("Content-Type","image/png")
|
resp.getHeaders().add("Content-Type","image/png")
|
||||||
|
@ -1,41 +1,37 @@
|
|||||||
from locust import task, between, SequentialTaskSet
|
from locust import task, between, SequentialTaskSet
|
||||||
from locust.contrib.fasthttp import FastHttpUser
|
from locust.contrib.fasthttp import FastHttpUser
|
||||||
import json
|
import json
|
||||||
import uuid
|
|
||||||
|
|
||||||
class QuickStartUser(SequentialTaskSet):
|
class QuickStartUser(SequentialTaskSet):
|
||||||
wait_time = between(0.1,1)
|
wait_time = between(0.1,1)
|
||||||
|
|
||||||
captcha_params = {"level":"some","media":"some","input_type":"some"}
|
|
||||||
answerBody = {"answer": "qwer123"}
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def captcha(self):
|
def captcha(self):
|
||||||
resp = self.client.post(path="/v1/captcha", json=self.captcha_params, name="/captcha")
|
captcha_params = {"level":"some","media":"some","input_type":"some"}
|
||||||
|
|
||||||
|
resp = self.client.post(path="/v1/captcha", json=captcha_params, name="/captcha")
|
||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
print("\nError on /captcha endpoint: ")
|
print("\nError on /captcha endpoint: ")
|
||||||
print(resp)
|
print(resp)
|
||||||
print(resp.text)
|
print(resp.text)
|
||||||
print("----------------END.C-------------------\n\n")
|
print("----------------END.C-------------------\n\n")
|
||||||
self.answerBody["id"] = json.loads(resp.text).get("id")
|
|
||||||
|
uuid = json.loads(resp.text).get("id")
|
||||||
|
answerBody = {"answer": "qwer123","id": uuid}
|
||||||
|
|
||||||
@task
|
resp = self.client.get(path="/v1/media?id=%s" % uuid, name="/media")
|
||||||
def media(self):
|
|
||||||
resp = self.client.get(path="/v1/media?id=%s" % self.answerBody.get("id"), name="/media")
|
|
||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
print("\nError on /media endpoint: ")
|
print("\nError on /captcha endpoint: ")
|
||||||
print(resp)
|
print(resp)
|
||||||
print(resp.text)
|
print(resp.text)
|
||||||
print("-----------------END.M-------------------\n\n")
|
print("----------------END.C-------------------\n\n")
|
||||||
|
|
||||||
@task
|
resp = self.client.post(path='/v1/answer', json=answerBody, name="/answer")
|
||||||
def answer(self):
|
|
||||||
resp = self.client.post(path='/v1/answer', json=self.answerBody, name="/answer")
|
|
||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
print("\nError on /answer endpoint: ")
|
print("\nError on /captcha endpoint: ")
|
||||||
print(resp)
|
print(resp)
|
||||||
print(resp.text)
|
print(resp.text)
|
||||||
print("-------------------END.A---------------\n\n")
|
print("----------------END.C-------------------\n\n")
|
||||||
|
|
||||||
|
|
||||||
class User(FastHttpUser):
|
class User(FastHttpUser):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user