mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-01-13 06:53:19 -05:00
Merge branch 'master' of github.com:librecaptcha/lc-core
This commit is contained in:
commit
19175d122f
23
client/index.html
Normal file
23
client/index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Libre Captcha</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Libre Captcha</h1><hr>
|
||||
<h2>Open Source solution to Captchas</h2>
|
||||
<h3>v0.2 (Beta)</h3>
|
||||
</div>
|
||||
<div class="form">
|
||||
<input type="text" id="email" placeholder="email">
|
||||
<input type="button" id="reg-btn" value="Register">
|
||||
<!-- <p id="token"></p> -->
|
||||
</div>
|
||||
<div class="secret">
|
||||
<h4 id="token"></h4>
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
9
client/script.js
Normal file
9
client/script.js
Normal file
@ -0,0 +1,9 @@
|
||||
document.getElementById("reg-btn").addEventListener("click", function(){
|
||||
var email = document.getElementById("email").value;
|
||||
var url = window.location.origin+"/v1/token?email="+email
|
||||
fetch(url)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
document.getElementById("token").innerHTML = "SECRET "+data.token;
|
||||
})
|
||||
})
|
24
client/style.css
Normal file
24
client/style.css
Normal file
@ -0,0 +1,24 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form input {
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#token {
|
||||
margin: 10px;
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
}
|
@ -19,16 +19,32 @@ import java.util.concurrent._
|
||||
import java.util.UUID
|
||||
import scala.Array
|
||||
|
||||
class Captcha(throttle: Int) {
|
||||
class DBConn(){
|
||||
val con: Connection = DriverManager.getConnection("jdbc:h2:./captcha", "sa", "")
|
||||
val stmt: Statement = con.createStatement()
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token varchar, id varchar, secret varchar, provider varchar, contentType varchar, image blob, solved boolean default False, PRIMARY KEY(token))")
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS mapId(uuid varchar, token varchar, PRIMARY KEY(uuid), FOREIGN KEY(token) REFERENCES challenge(token))")
|
||||
|
||||
val insertPstmt: PreparedStatement = con.prepareStatement("INSERT INTO challenge(token, id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?, ?)")
|
||||
val mapPstmt: PreparedStatement = con.prepareStatement("INSERT INTO mapId(uuid, token) VALUES (?, ?)")
|
||||
val selectPstmt: PreparedStatement = con.prepareStatement("SELECT secret, provider FROM challenge WHERE token = ?")
|
||||
val imagePstmt: PreparedStatement = con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
||||
val updatePstmt: PreparedStatement = con.prepareStatement("UPDATE challenge SET solved = True WHERE token = ?")
|
||||
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 = ?)")
|
||||
val userPstmt: PreparedStatement = con.prepareStatement("INSERT INTO users(email, hash) VALUES (?,?)")
|
||||
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 {
|
||||
|
||||
val stmt = getConn()
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token varchar, id varchar, secret varchar, provider varchar, contentType varchar, image blob, solved boolean default False, PRIMARY KEY(token))")
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS mapId(uuid varchar, token varchar, PRIMARY KEY(uuid), FOREIGN KEY(token) REFERENCES challenge(token))")
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS users(email varchar, hash int)")
|
||||
|
||||
val providers = Map("FilterChallenge" -> new FilterChallenge,
|
||||
"FontFunCaptcha" -> new FontFunCaptcha,
|
||||
@ -128,6 +144,16 @@ class Captcha(throttle: Int) {
|
||||
providers(provider).checkAnswer(secret, answer.answer)
|
||||
}
|
||||
|
||||
def getHash(email: String): Int = {
|
||||
val secret = ""
|
||||
val str = email+secret
|
||||
val hash = str.hashCode()
|
||||
userPstmt.setString(1, email)
|
||||
userPstmt.setInt(2, hash)
|
||||
userPstmt.executeUpdate()
|
||||
hash
|
||||
}
|
||||
|
||||
def display(): Unit = {
|
||||
val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge")
|
||||
println("token\t\tid\t\tsecret\t\tsolved")
|
||||
@ -139,29 +165,81 @@ class Captcha(throttle: Int) {
|
||||
println(s"${token}\t\t${id}\t\t${secret}\t\t${solved}")
|
||||
}
|
||||
}
|
||||
|
||||
def closeConnection(): Unit = {
|
||||
con.close()
|
||||
}
|
||||
}
|
||||
|
||||
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 body = req.getJson()
|
||||
val json = parse(body)
|
||||
val param = json.extract[Parameters]
|
||||
val id = captcha.getChallenge(param)
|
||||
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
|
||||
@ -194,6 +272,17 @@ class Server(port: Int){
|
||||
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