implement registration chess captcha

This commit is contained in:
Thibault Duplessis 2012-06-05 21:58:20 +02:00
parent d2d60f1ff7
commit ac552594a2
19 changed files with 158 additions and 79 deletions

View file

@ -12,6 +12,7 @@ import play.api.mvc.Results._
object Auth extends LilaController {
def userRepo = env.user.userRepo
def forms = env.security.forms
def login = Open { implicit ctx
Ok(html.auth.login(loginForm))
@ -32,16 +33,20 @@ object Auth extends LilaController {
}
def signup = Open { implicit ctx
Ok(html.auth.signup(signupForm))
val (form, captcha) = forms.signupWithCaptcha
Ok(html.auth.signup(form, captcha))
}
def signupPost = OpenBody { implicit ctx
implicit val req = ctx.body
signupForm.bindFromRequest.fold(
err BadRequest(html.auth.signup(err)),
userOption gotoSignupSucceeded(
userOption.err("authenticate error").username
)
forms.signup.bindFromRequest.fold(
err BadRequest(html.auth.signup(err, forms.captchaCreate)),
data {
val user = userRepo.create(data.username, data.password).unsafePerformIO
gotoSignupSucceeded(
user.err("register error").username
)
}
)
}
}

View file

@ -73,7 +73,7 @@ object User extends LilaController {
me
IORedirect {
userRepo disable me map { _
env.securityStore deleteUsername me.username
env.security.store deleteUsername me.username
routes.User show me.username
}
}

View file

@ -97,6 +97,12 @@ final class CoreEnv private (application: Application, val settings: Settings) {
settings = settings,
gameRepo = game.gameRepo)
lazy val security = new lila.security.SecurityEnv(
settings = settings,
captcha = site.captcha,
mongodb = mongodb.apply _,
userRepo = user.userRepo)
lazy val metaHub = new lila.socket.MetaHub(
List(site.hub, lobby.hub, round.hubMaster))
@ -105,13 +111,6 @@ final class CoreEnv private (application: Application, val settings: Settings) {
mongodb = mongodb.connection,
settings = settings)
lazy val securityStore = new security.Store(
collection = mongodb(MongoCollectionSecurity))
lazy val firewall = new security.Firewall(
collection = mongodb(MongoCollectionFirewall),
cacheTtl = FirewallCacheTtl)
lazy val titivate = new core.Titivate(
gameRepo = game.gameRepo,
finisher = round.finisher)

View file

@ -21,7 +21,7 @@ object Global extends GlobalSettings {
override def onRouteRequest(req: RequestHeader): Option[Handler] = {
env.monitor.rpsProvider.countRequest()
env.firewall.requestHandler(req) orElse
env.security.firewall.requestHandler(req) orElse
env.i18n.requestHandler(req) orElse
super.onRouteRequest(req)
}

View file

@ -26,19 +26,6 @@ trait AuthImpl {
.verifying("Invalid username or password", _.isDefined)
)
val signupForm = Form(mapping(
"username" -> of[String].verifying(
Constraints minLength 2,
Constraints maxLength 20,
Constraints.pattern(
regex = """^[\w-]+$""".r,
error = "Invalid username. Please use only letters, numbers and dash")
),
"password" -> text(minLength = 4)
)(signupUser)(_ None)
.verifying("This user already exists", _.isDefined)
)
def env: CoreEnv
def logoutSucceeded(req: RequestHeader): PlainResult =
@ -49,7 +36,7 @@ trait AuthImpl {
def saveAuthentication(username: String)(implicit req: RequestHeader): String =
(OrnicarRandom nextAsciiString 12) ~ { sessionId
env.securityStore.save(sessionId, username, req)
env.security.store.save(sessionId, username, req)
}
def gotoLoginSucceeded[A](username: String)(implicit req: RequestHeader) = {
@ -63,7 +50,7 @@ trait AuthImpl {
}
def gotoLogoutSucceeded(implicit req: RequestHeader) = {
req.session.get("sessionId") foreach env.securityStore.delete
req.session.get("sessionId") foreach env.security.store.delete
logoutSucceeded(req).withNewSession
}
@ -79,12 +66,9 @@ trait AuthImpl {
def authenticateUser(username: String, password: String): Option[User] =
env.user.userRepo.authenticate(username, password).unsafePerformIO
def signupUser(username: String, password: String): Option[User] =
env.user.userRepo.create(username, password).unsafePerformIO
def restoreUser(req: RequestHeader): Option[User] = for {
sessionId req.session.get("sessionId")
username env.securityStore.getUsername(sessionId)
username env.security.store.getUsername(sessionId)
user (env.user.userRepo byId username).unsafePerformIO
} yield user
}

View file

@ -0,0 +1,51 @@
package lila
package security
import user.{ User, UserRepo }
import site.Captcha
import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints
final class DataForm(
userRepo: UserRepo,
captcher: Captcha) {
import DataForm._
val signup = Form(mapping(
"username" -> nonEmptyText.verifying(
Constraints minLength 2,
Constraints maxLength 20,
Constraints.pattern(
regex = """^[\w-]+$""".r,
error = "Invalid username. Please use only letters, numbers and dash")
),
"password" -> text(minLength = 4),
"gameId" -> nonEmptyText,
"move" -> nonEmptyText
)(SignupData.apply)(_ None)
.verifying("This user already exists", d !userExists(d))
.verifying(
"Not a checkmate",
data captcher get data.gameId valid data.move.trim.toLowerCase
)
)
def signupWithCaptcha = signup -> captchaCreate
def captchaCreate: Captcha.Challenge = captcher.create
private def userExists(data: SignupData) =
userRepo.exists(data.username).unsafePerformIO
}
object DataForm {
case class SignupData(
username: String,
password: String,
gameId: String,
move: String)
}

View file

@ -0,0 +1,28 @@
package lila
package security
import user.{ User, UserRepo }
import core.Settings
import site.Captcha
import com.mongodb.casbah.MongoCollection
final class SecurityEnv(
settings: Settings,
captcha: Captcha,
mongodb: String MongoCollection,
userRepo: UserRepo) {
import settings._
lazy val store = new security.Store(
collection = mongodb(MongoCollectionSecurity))
lazy val firewall = new security.Firewall(
collection = mongodb(MongoCollectionFirewall),
cacheTtl = FirewallCacheTtl)
lazy val forms = new DataForm(
userRepo = userRepo,
captcher = captcha)
}

View file

@ -1,4 +1,4 @@
@(form: Form[_])(implicit ctx: Context)
@(form: Form[_], captcha: lila.site.Captcha.Challenge)(implicit ctx: Context)
@auth.layout(
title = trans.signUp.str()) {
@ -17,11 +17,12 @@ title = trans.signUp.str()) {
</em>
</p>
<form action="@routes.Auth.signupPost" method="POST">
@form.globalError.map { error =>
<p class="error">@error.message</p>
}
<ul>
@auth.formFields(form("username"), form("password"))
<li>
@base.captcha(form("move"), form("gameId"), captcha)
</li>
@errMsg(form)
<li>
<input type="submit" value="@trans.signUp()" class="submit">
<span class="alternative">@trans.haveAnAccount() <a href="@routes.Auth.login">@trans.signIn()</a></span>

View file

@ -9,12 +9,10 @@
</div>
<div class="checkmateSection">
<label>
<div>
<strong>@captcha.color.toString.capitalize plays, mate in one!</strong><br />
This is a chess CAPTCHA.<br />
Click on the board to make your move,<br />
and prove you are human.<br /><br />
</div>
<strong>@captcha.color.toString.capitalize plays, mate in one!</strong><br />
This is a chess CAPTCHA.<br />
Click on the board to make your move,<br />
and prove you are human.<br /><br />
<input type="text" name="@move.name" id="@move.id" />
@errMsg(move)
</label>

View file

@ -15,7 +15,7 @@ title = "New forum topic") {
@errMsg(form("name"))
</label>
@forum.post.formFields(form("post")("text"), form("post")("author"))
@forum.post.captcha(form("post")("move"), form("post")("gameId"), captcha)
@base.captcha(form("post")("move"), form("post")("gameId"), captcha)
@errMsg(form("post"))
<button class="submit button" type="submit">Create the topic</button>
<a href="@routes.ForumCateg.show(categ.slug)" style="margin-left:20px">Cancel</a>

View file

@ -36,7 +36,7 @@ title = topic.name) {
<h2 class="postNewTitle" id="reply">Reply to this topic</h2>
<form action="@routes.ForumPost.create(categ.slug, topic.slug, posts.currentPage)#reply" method="POST" novalidate>
@forum.post.formFields(form("text"), form("author"))
@forum.post.captcha(form("move"), form("gameId"), captcha)
@base.captcha(form("move"), form("gameId"), captcha)
@errMsg(form)
<input type="submit" class="submit button" value="Reply" />
<a href="@routes.ForumCateg.show(categ.slug)" style="margin-left:20px">Cancel</a>

View file

@ -16,7 +16,7 @@ object Main {
lazy val env = CoreEnv(app)
lazy val users = Users(env.user.userRepo, env.securityStore)
lazy val users = Users(env.user.userRepo, env.security.store)
lazy val games = Games(env)
lazy val titivate = env.titivate
lazy val forum = Forum(env.forum)

View file

@ -28,7 +28,7 @@ var lichess = {
onProduction: /.+\.lichess\.org/.test(document.domain),
socketUrl: document.domain + ":9000"
};
lichess.socketDefaults.options.debug = !lichess.onProduction;
//lichess.socketDefaults.options.debug = !lichess.onProduction;
$(function() {

View file

@ -1,15 +1,4 @@
$(function() {
$('.checkmateCaptcha').each(function() {
var $captcha = $(this);
var $input = $captcha.find('input');
var i1, i2;
$captcha.find('div.lmcs').click(function() {
var key = $(this).data('key');
i1 = $input.val();
i2 = i1.length > 3 ? key : i1 + " " + key;
$input.val(i2);
});
});
$("#lichess_forum a.delete").unbind("click").click(function() {
if (confirm("Delete?")) {
var $this = $(this)

View file

@ -72,6 +72,17 @@ $(function() {
});
}
parseFen();
$('body').on('lichess.content_loaded', parseFen);
$('div.checkmateCaptcha').each(function() {
var $captcha = $(this);
var $input = $captcha.find('input');
var i1, i2;
$captcha.find('div.lmcs').click(function() {
var key = $(this).data('key');
i1 = $input.val();
i2 = i1.length > 3 ? key : i1 + " " + key;
$input.val(i2);
});
});
});

View file

@ -544,3 +544,23 @@ div.game_share_top a.game_permalink {
display: block;
margin-top: 3px;
}
div.checkmateFen {
float: left;
width: 220px;
margin-left: 110px;
cursor: pointer;
}
div.checkmateFen div.lmcs:hover {
background: #ddddff;
}
div.checkmateSection {
float: left;
}
div.checkmateCaptcha input {
padding: 0.5em;
border: 1px solid #D4D4D4;
background: #dfdfff;
font-weight: bold;
width: 5em;
}

View file

@ -258,25 +258,6 @@ div.bar div.pagination {
float: right;
}
div.checkmateFen {
float: left;
width: 220px;
margin-left: 110px;
cursor: pointer;
}
div.checkmateFen div.lmcs:hover {
background: #ddddff;
}
div.checkmateSection {
float: left;
}
div.checkmateCaptcha input {
padding: 0.5em;
border: 1px solid #D4D4D4;
background: #dfdfff;
font-weight: bold;
width: 5em;
}
#lichess_forum div.checkmateCaptcha p.error {
margin-left: 0;
}

View file

@ -52,3 +52,14 @@ form .error {
margin-left: 160px;
color: red;
}
div.checkmateCaptcha {
margin-top: 2em;
}
div.checkmateCaptcha div.checkmateFen {
margin-left: 0;
}
div.checkmateCaptcha label {
width: 250px;
text-align: left;
}

1
todo
View file

@ -30,6 +30,7 @@ star people and games (and forum threads?)
autoclose top menus
strings and captchas http://fr.lichess.org/forum/off-topic-discussion/lichess-asks-lichess-answers?page=11#109
tournaments http://www.chess.com/tournaments/help.html
forum last post link broken http://fr.lichess.org/forum/off-topic-discussion/lichess-asks-lichess-answers?page=12#111
new translations:
-rematchOfferCanceled=Rematch offer canceled