From 999221de9d51329b30b13cdb6e7e7b889efbeaea Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Mon, 19 Feb 2024 23:32:55 +0100 Subject: [PATCH] email/pass login with sqlite db --- .gitignore | 1 + create-user.sh | 9 +++ view-webapp/pom.xml | 6 ++ .../pairgoth/util/CredentialsChecker.kt | 35 ++++++++++++ .../org/jeudego/pairgoth/web/LoginServlet.kt | 14 ++++- view-webapp/src/main/sass/main.scss | 4 ++ .../src/main/webapp/WEB-INF/translations/fr | 8 +++ view-webapp/src/main/webapp/index-ffg.html | 1 + view-webapp/src/main/webapp/js/main.js | 2 +- view-webapp/src/main/webapp/login.html | 55 ++++++++++++++++--- 10 files changed, 125 insertions(+), 10 deletions(-) create mode 100755 create-user.sh create mode 100644 view-webapp/src/main/kotlin/org/jeudego/pairgoth/util/CredentialsChecker.kt diff --git a/.gitignore b/.gitignore index fc95ea5..40238cc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ target /tournamentfiles *.iml *~ +pairgoth.db diff --git a/create-user.sh b/create-user.sh new file mode 100755 index 0000000..776e4e5 --- /dev/null +++ b/create-user.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +read -rp 'email: ' EMAIL +read -rp 'password: ' PASSWORD + +ENCPASS=$(echo -n $PASSWORD | sha256sum) +ENCPASS=${ENCPASS% -} + +sqlite3 pairgoth.db "INSERT INTO cred (email, password) VALUES ('$EMAIL', '$ENCPASS')" diff --git a/view-webapp/pom.xml b/view-webapp/pom.xml index 2b5967b..abc7851 100644 --- a/view-webapp/pom.xml +++ b/view-webapp/pom.xml @@ -286,6 +286,12 @@ lucene-queryparser ${lucene.version} + + + org.xerial + sqlite-jdbc + 3.45.1.0 + org.junit.jupiter diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/util/CredentialsChecker.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/util/CredentialsChecker.kt new file mode 100644 index 0000000..1a7f90d --- /dev/null +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/util/CredentialsChecker.kt @@ -0,0 +1,35 @@ +package org.jeudego.pairgoth.util + +import com.republicate.kson.Json +import java.io.File +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.sql.DriverManager + +object CredentialsChecker { + private const val CREDENTIALS_DB = "pairgoth.db" + private val hasher = MessageDigest.getInstance("SHA-256") + @OptIn(ExperimentalStdlibApi::class) + fun check(email: String, password: String): String? { + initDatabase() + val sha256 = hasher.digest(password.toByteArray(StandardCharsets.UTF_8)).toHexString() + DriverManager.getConnection("jdbc:sqlite:$CREDENTIALS_DB").use { conn -> + val rs = + conn.prepareStatement("SELECT 1 FROM cred WHERE email = ? AND password = ?").apply { + setString(1, email) + setString(2, password) + }.executeQuery() + return if (rs.next()) email else null + } + } + + @Synchronized + fun initDatabase() { + if (!File(CREDENTIALS_DB).exists()) { + DriverManager.getConnection("jdbc:sqlite:$CREDENTIALS_DB").use { conn -> + conn.createStatement().executeUpdate("CREATE TABLE cred (email VARCHAR(200) UNIQUE NOT NULL, password VARCHAR(200) NOT NULL)") + } + } + } + +} diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/LoginServlet.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/LoginServlet.kt index 3fe3022..3188363 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/LoginServlet.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/LoginServlet.kt @@ -1,6 +1,7 @@ package org.jeudego.pairgoth.web import com.republicate.kson.Json +import org.jeudego.pairgoth.util.CredentialsChecker import org.slf4j.LoggerFactory import javax.servlet.http.HttpServlet import javax.servlet.http.HttpServletRequest @@ -17,7 +18,7 @@ class LoginServlet: HttpServlet() { val payload = Json.Companion.parse(req.reader.readText())?.asObject() ?: throw Error("null json") val user = when (WebappManager.getProperty("auth")) { "sesame" -> checkSesame(payload) - else -> null + else -> checkLoginPass(payload) } ?: throw Error("authentication failed") req.session.setAttribute("logged", user) val ret = Json.Object("status" to "ok") @@ -25,7 +26,9 @@ class LoginServlet: HttpServlet() { resp.writer.println(ret.toString()) } catch (t: Throwable) { logger.error("exception while logging in", t) - resp.sendError(HttpServletResponse.SC_BAD_REQUEST) + resp.contentType = "application/json" + resp.status = HttpServletResponse.SC_BAD_REQUEST + resp.writer.println(errorJson) } } @@ -34,8 +37,15 @@ class LoginServlet: HttpServlet() { return if (payload.getString("sesame")?.equals(expected) == true) true else null } + fun checkLoginPass(payload: Json.Object): String? { + return CredentialsChecker.check( + payload.getString("email") ?: throw Error("Missing login field"), + payload.getString("password") ?: throw Error("missing password field")) + } + companion object { fun isJson(mimeType: String) = "text/json" == mimeType || "application/json" == mimeType || mimeType.endsWith("+json") val logger = LoggerFactory.getLogger("login") + private val errorJson = "{ \"status\": \"error\", \"error\": \"authentication failed\"}" } } diff --git a/view-webapp/src/main/sass/main.scss b/view-webapp/src/main/sass/main.scss index dc4c1c5..04ef24b 100644 --- a/view-webapp/src/main/sass/main.scss +++ b/view-webapp/src/main/sass/main.scss @@ -215,6 +215,10 @@ width: initial; } + .ui.form .centered.inline.fields { + justify-content: center; + } + .ui.accordion .content { display: block; max-height: 0; diff --git a/view-webapp/src/main/webapp/WEB-INF/translations/fr b/view-webapp/src/main/webapp/WEB-INF/translations/fr index 95001cd..ea2a3b8 100644 --- a/view-webapp/src/main/webapp/WEB-INF/translations/fr +++ b/view-webapp/src/main/webapp/WEB-INF/translations/fr @@ -162,3 +162,11 @@ white blanc White Blanc white vs. black blanc vs. Noir confirmed. confirmé(s). +Note that login to this instance is reserved to French federation actors plus several external people at our discretion. Send us La connexion à cette instance est réservée aux acteurs de la FFG et à quelques personnes extérieures, à notre discrétion. Envoyez-nous +an email un email +to request an access. pour demander un accès. +(not yet available) (pas encore disponible) +Log in using Se connecter avec +(reserved to FFG actors) (réservé aux acteurs FFG) +Log in using an email Se connecter avec un email +password mot de passe diff --git a/view-webapp/src/main/webapp/index-ffg.html b/view-webapp/src/main/webapp/index-ffg.html index c3ced80..020007d 100644 --- a/view-webapp/src/main/webapp/index-ffg.html +++ b/view-webapp/src/main/webapp/index-ffg.html @@ -8,6 +8,7 @@
  1. Stay in the browser: If you prefer convenience, you can simply use the instance graciously hosted by the French Go Federation.

    +

    Note that login to this instance is reserved to French federation actors plus several external people at our discretion. Send us an email to request an access.

    Launch
    diff --git a/view-webapp/src/main/webapp/js/main.js b/view-webapp/src/main/webapp/js/main.js index 16b5b51..1c3c251 100644 --- a/view-webapp/src/main/webapp/js/main.js +++ b/view-webapp/src/main/webapp/js/main.js @@ -114,7 +114,7 @@ HTMLFormElement.prototype.val = function(name, value) { let tag = ctl.tagName; let type = tag === 'INPUT' ? ctl.attr('type') : undefined; if ( - (tag === 'INPUT' && ['text', 'number', 'hidden'].includes(ctl.attr('type'))) || + (tag === 'INPUT' && ['text', 'number', 'hidden', 'password'].includes(ctl.attr('type'))) || tag === 'SELECT' ) { if (hasValue) { diff --git a/view-webapp/src/main/webapp/login.html b/view-webapp/src/main/webapp/login.html index c85cf3f..754b16a 100644 --- a/view-webapp/src/main/webapp/login.html +++ b/view-webapp/src/main/webapp/login.html @@ -30,30 +30,71 @@ #elseif($auth == 'oauth')
    -
    Log in using
    -
    +
    #foreach($provider in $oauthProviders) -
    - -
    +
    + + + #if($provider == 'ffg') + (reserved to FFG actors) + #end +
    #end
    +
    + Log in using an email +
    +
    +
    + +
    +
    + +
    + +
    +
    +