mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-01-14 15:25:00 -05:00
refactor, separate code into files
This commit is contained in:
parent
e45302df8c
commit
03548d17ae
23
src/main/scala/lc/DB.scala
Normal file
23
src/main/scala/lc/DB.scala
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package lc
|
||||||
|
|
||||||
|
import java.sql._
|
||||||
|
|
||||||
|
class DBConn(){
|
||||||
|
val con: Connection = DriverManager.getConnection("jdbc:h2:./captcha", "sa", "")
|
||||||
|
|
||||||
|
lazy val insertPstmt: PreparedStatement = con.prepareStatement("INSERT INTO challenge(token, id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?, ?)")
|
||||||
|
lazy val mapPstmt: PreparedStatement = con.prepareStatement("INSERT INTO mapId(uuid, token) VALUES (?, ?)")
|
||||||
|
lazy val selectPstmt: PreparedStatement = con.prepareStatement("SELECT secret, provider FROM challenge WHERE token = ?")
|
||||||
|
lazy val imagePstmt: PreparedStatement = con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
||||||
|
lazy val updatePstmt: PreparedStatement = con.prepareStatement("UPDATE challenge SET solved = True WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
|
||||||
|
lazy val userPstmt: PreparedStatement = con.prepareStatement("INSERT INTO users(email, hash) VALUES (?,?)")
|
||||||
|
lazy val validatePstmt: PreparedStatement = con.prepareStatement("SELECT hash FROM users WHERE hash = ? LIMIT 1")
|
||||||
|
|
||||||
|
def getConn(): Statement = {
|
||||||
|
con.createStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
def closeConnection(): Unit = {
|
||||||
|
con.close()
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +1,15 @@
|
|||||||
package lc
|
package lc
|
||||||
|
|
||||||
import com.sksamuel.scrimage._
|
import com.sksamuel.scrimage._
|
||||||
import java.sql._
|
import java.io.ByteArrayInputStream
|
||||||
import java.io._
|
|
||||||
import lc.HTTPServer._
|
|
||||||
import lc.FontFunCaptcha._
|
|
||||||
import lc.GifCaptcha._
|
|
||||||
import lc.ShadowTextCaptcha._
|
|
||||||
import javax.imageio._
|
|
||||||
import java.awt.image._
|
|
||||||
import org.json4s._
|
|
||||||
import org.json4s.jackson.JsonMethods._
|
|
||||||
import org.json4s.JsonDSL._
|
|
||||||
import java.util.Base64
|
|
||||||
import org.json4s.jackson.Serialization
|
|
||||||
import org.json4s.jackson.Serialization.{read, write}
|
|
||||||
import java.util.concurrent._
|
import java.util.concurrent._
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import scala.Array
|
import java.sql.{Blob, ResultSet}
|
||||||
|
|
||||||
class DBConn(){
|
case class Size(height: Int, width: Int)
|
||||||
val con: Connection = DriverManager.getConnection("jdbc:h2:./captcha", "sa", "")
|
case class Parameters(level: String, media: String, input_type: String, size: Option[Size])
|
||||||
|
case class Id(id: String)
|
||||||
lazy val insertPstmt: PreparedStatement = con.prepareStatement("INSERT INTO challenge(token, id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?, ?)")
|
case class Answer(answer: String, id: String)
|
||||||
lazy val mapPstmt: PreparedStatement = con.prepareStatement("INSERT INTO mapId(uuid, token) VALUES (?, ?)")
|
|
||||||
lazy val selectPstmt: PreparedStatement = con.prepareStatement("SELECT secret, provider FROM challenge WHERE token = ?")
|
|
||||||
lazy val imagePstmt: PreparedStatement = con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
|
||||||
lazy val updatePstmt: PreparedStatement = con.prepareStatement("UPDATE challenge SET solved = True WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
|
|
||||||
lazy val userPstmt: PreparedStatement = con.prepareStatement("INSERT INTO users(email, hash) VALUES (?,?)")
|
|
||||||
lazy val validatePstmt: PreparedStatement = con.prepareStatement("SELECT hash FROM users WHERE hash = ? LIMIT 1")
|
|
||||||
|
|
||||||
def getConn(): Statement = {
|
|
||||||
con.createStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
def closeConnection(): Unit = {
|
|
||||||
con.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Captcha(throttle: Int) extends DBConn {
|
class Captcha(throttle: Int) extends DBConn {
|
||||||
|
|
||||||
@ -167,128 +139,6 @@ class Captcha(throttle: Int) extends DBConn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 Secret(token: Int)
|
|
||||||
|
|
||||||
class RateLimiter extends DBConn {
|
|
||||||
val stmt = getConn()
|
|
||||||
val userLastActive = collection.mutable.Map[Int, Long]()
|
|
||||||
val userAllowance = collection.mutable.Map[Int, Double]()
|
|
||||||
val rate = 2.0
|
|
||||||
val per = 45.0
|
|
||||||
val allowance = rate
|
|
||||||
|
|
||||||
def validateUser(user: Int) : Boolean = {
|
|
||||||
synchronized {
|
|
||||||
val allow = if(userLastActive.contains(user)){
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
validatePstmt.setInt(1, user)
|
|
||||||
val rs = validatePstmt.executeQuery()
|
|
||||||
val validated = if(rs.next()){
|
|
||||||
val hash = rs.getInt("hash")
|
|
||||||
userLastActive(hash) = System.currentTimeMillis()
|
|
||||||
userAllowance(hash) = allowance
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
validated
|
|
||||||
}
|
|
||||||
allow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def checkLimit(user: Int): Boolean = {
|
|
||||||
synchronized {
|
|
||||||
val current = System.currentTimeMillis()
|
|
||||||
val time_passed = (current - userLastActive(user)) / 1000000000
|
|
||||||
userLastActive(user) = current
|
|
||||||
userAllowance(user) += time_passed * (rate/per)
|
|
||||||
if(userAllowance(user) > rate){ userAllowance(user) = rate }
|
|
||||||
val allow = if(userAllowance(user) < 1.0){
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
userAllowance(user) -= 1.0
|
|
||||||
true
|
|
||||||
}
|
|
||||||
allow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Server(port: Int){
|
|
||||||
val captcha = new Captcha(0)
|
|
||||||
val rateLimiter = new RateLimiter()
|
|
||||||
val server = new HTTPServer(port)
|
|
||||||
val host = server.getVirtualHost(null)
|
|
||||||
|
|
||||||
implicit val formats = DefaultFormats
|
|
||||||
|
|
||||||
host.addContext("/v1/captcha",(req, resp) => {
|
|
||||||
val accessToken = Option(req.getHeaders().get("access-token")).map(_.toInt)
|
|
||||||
val access = accessToken.map(t => rateLimiter.validateUser(t) && rateLimiter.checkLimit(t)).getOrElse(false)
|
|
||||||
val id = if(access){
|
|
||||||
val body = req.getJson()
|
|
||||||
val json = parse(body)
|
|
||||||
val param = json.extract[Parameters]
|
|
||||||
captcha.getChallenge(param)
|
|
||||||
} else {
|
|
||||||
"Not a valid user or rate limit reached!"
|
|
||||||
}
|
|
||||||
resp.getHeaders().add("Content-Type","application/json")
|
|
||||||
resp.send(200, write(id))
|
|
||||||
0
|
|
||||||
},"POST")
|
|
||||||
|
|
||||||
host.addContext("/v1/media",(req, resp) => {
|
|
||||||
var id = Id(null)
|
|
||||||
if ("GET" == req.getMethod()){
|
|
||||||
val params = req.getParams()
|
|
||||||
id = Id(params.get("id"))
|
|
||||||
} else {
|
|
||||||
val body = req.getJson()
|
|
||||||
val json = parse(body)
|
|
||||||
id = json.extract[Id]
|
|
||||||
}
|
|
||||||
val image = captcha.getCaptcha(id)
|
|
||||||
resp.getHeaders().add("Content-Type","image/png")
|
|
||||||
resp.send(200, image)
|
|
||||||
0
|
|
||||||
},"POST", "GET")
|
|
||||||
|
|
||||||
host.addContext("/v1/answer",(req, resp) => {
|
|
||||||
val body = req.getJson()
|
|
||||||
val json = parse(body)
|
|
||||||
val answer = json.extract[Answer]
|
|
||||||
val result = captcha.checkAnswer(answer)
|
|
||||||
resp.getHeaders().add("Content-Type","application/json")
|
|
||||||
val responseContent = if(result) """{"result":"True"}""" else """{"result":"False"}"""
|
|
||||||
resp.send(200,responseContent)
|
|
||||||
0
|
|
||||||
},"POST")
|
|
||||||
|
|
||||||
host.addContext("/v1/register", new FileContextHandler(new File("client/")))
|
|
||||||
|
|
||||||
host.addContext("/v1/token", (req,resp) => {
|
|
||||||
val params = req.getParams()
|
|
||||||
val hash = captcha.getHash(params.get("email"))
|
|
||||||
val token = Secret(hash)
|
|
||||||
resp.getHeaders().add("Content-Type", "application/json")
|
|
||||||
resp.send(200, write(token))
|
|
||||||
0
|
|
||||||
})
|
|
||||||
|
|
||||||
def start(): Unit = {
|
|
||||||
server.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object LCFramework{
|
object LCFramework{
|
||||||
def main(args: scala.Array[String]) {
|
def main(args: scala.Array[String]) {
|
||||||
val captcha = new Captcha(2)
|
val captcha = new Captcha(2)
|
||||||
|
126
src/main/scala/lc/Server.scala
Normal file
126
src/main/scala/lc/Server.scala
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package lc
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import org.json4s._
|
||||||
|
import org.json4s.jackson.JsonMethods._
|
||||||
|
import org.json4s.jackson.Serialization.{read, write}
|
||||||
|
|
||||||
|
import lc.HTTPServer._
|
||||||
|
|
||||||
|
case class Secret(token: Int)
|
||||||
|
|
||||||
|
class RateLimiter extends DBConn {
|
||||||
|
val stmt = getConn()
|
||||||
|
val userLastActive = collection.mutable.Map[Int, Long]()
|
||||||
|
val userAllowance = collection.mutable.Map[Int, Double]()
|
||||||
|
val rate = 2.0
|
||||||
|
val per = 45.0
|
||||||
|
val allowance = rate
|
||||||
|
|
||||||
|
def validateUser(user: Int) : Boolean = {
|
||||||
|
synchronized {
|
||||||
|
val allow = if(userLastActive.contains(user)){
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
validatePstmt.setInt(1, user)
|
||||||
|
val rs = validatePstmt.executeQuery()
|
||||||
|
val validated = if(rs.next()){
|
||||||
|
val hash = rs.getInt("hash")
|
||||||
|
userLastActive(hash) = System.currentTimeMillis()
|
||||||
|
userAllowance(hash) = allowance
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
validated
|
||||||
|
}
|
||||||
|
allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkLimit(user: Int): Boolean = {
|
||||||
|
synchronized {
|
||||||
|
val current = System.currentTimeMillis()
|
||||||
|
val time_passed = (current - userLastActive(user)) / 1000000000
|
||||||
|
userLastActive(user) = current
|
||||||
|
userAllowance(user) += time_passed * (rate/per)
|
||||||
|
if(userAllowance(user) > rate){ userAllowance(user) = rate }
|
||||||
|
val allow = if(userAllowance(user) < 1.0){
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
userAllowance(user) -= 1.0
|
||||||
|
true
|
||||||
|
}
|
||||||
|
allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Server(port: Int){
|
||||||
|
val captcha = new Captcha(0)
|
||||||
|
val rateLimiter = new RateLimiter()
|
||||||
|
val server = new HTTPServer(port)
|
||||||
|
val host = server.getVirtualHost(null)
|
||||||
|
|
||||||
|
implicit val formats = DefaultFormats
|
||||||
|
|
||||||
|
host.addContext("/v1/captcha",(req, resp) => {
|
||||||
|
val accessToken = Option(req.getHeaders().get("access-token")).map(_.toInt)
|
||||||
|
val access = accessToken.map(t => rateLimiter.validateUser(t) && rateLimiter.checkLimit(t)).getOrElse(false)
|
||||||
|
val id = if(access){
|
||||||
|
val body = req.getJson()
|
||||||
|
val json = parse(body)
|
||||||
|
val param = json.extract[Parameters]
|
||||||
|
captcha.getChallenge(param)
|
||||||
|
} else {
|
||||||
|
"Not a valid user or rate limit reached!"
|
||||||
|
}
|
||||||
|
resp.getHeaders().add("Content-Type","application/json")
|
||||||
|
resp.send(200, write(id))
|
||||||
|
0
|
||||||
|
},"POST")
|
||||||
|
|
||||||
|
host.addContext("/v1/media",(req, resp) => {
|
||||||
|
var id = Id(null)
|
||||||
|
if ("GET" == req.getMethod()){
|
||||||
|
val params = req.getParams()
|
||||||
|
id = Id(params.get("id"))
|
||||||
|
} else {
|
||||||
|
val body = req.getJson()
|
||||||
|
val json = parse(body)
|
||||||
|
id = json.extract[Id]
|
||||||
|
}
|
||||||
|
val image = captcha.getCaptcha(id)
|
||||||
|
resp.getHeaders().add("Content-Type","image/png")
|
||||||
|
resp.send(200, image)
|
||||||
|
0
|
||||||
|
},"POST", "GET")
|
||||||
|
|
||||||
|
host.addContext("/v1/answer",(req, resp) => {
|
||||||
|
val body = req.getJson()
|
||||||
|
val json = parse(body)
|
||||||
|
val answer = json.extract[Answer]
|
||||||
|
val result = captcha.checkAnswer(answer)
|
||||||
|
resp.getHeaders().add("Content-Type","application/json")
|
||||||
|
val responseContent = if(result) """{"result":"True"}""" else """{"result":"False"}"""
|
||||||
|
resp.send(200,responseContent)
|
||||||
|
0
|
||||||
|
},"POST")
|
||||||
|
|
||||||
|
host.addContext("/v1/register", new FileContextHandler(new File("client/")))
|
||||||
|
|
||||||
|
host.addContext("/v1/token", (req,resp) => {
|
||||||
|
val params = req.getParams()
|
||||||
|
val hash = captcha.getHash(params.get("email"))
|
||||||
|
val token = Secret(hash)
|
||||||
|
resp.getHeaders().add("Content-Type", "application/json")
|
||||||
|
resp.send(200, write(token))
|
||||||
|
0
|
||||||
|
})
|
||||||
|
|
||||||
|
def start(): Unit = {
|
||||||
|
server.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user