email/pass login with sqlite db
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ target
|
||||
/tournamentfiles
|
||||
*.iml
|
||||
*~
|
||||
pairgoth.db
|
||||
|
9
create-user.sh
Executable file
9
create-user.sh
Executable file
@@ -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')"
|
@@ -286,6 +286,12 @@
|
||||
<artifactId>lucene-queryparser</artifactId>
|
||||
<version>${lucene.version}</version>
|
||||
</dependency>
|
||||
<!-- sqlite -->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.45.1.0</version>
|
||||
</dependency>
|
||||
<!-- tests -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
@@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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\"}"
|
||||
}
|
||||
}
|
||||
|
@@ -215,6 +215,10 @@
|
||||
width: initial;
|
||||
}
|
||||
|
||||
.ui.form .centered.inline.fields {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ui.accordion .content {
|
||||
display: block;
|
||||
max-height: 0;
|
||||
|
@@ -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
|
||||
|
@@ -8,6 +8,7 @@
|
||||
<ol>
|
||||
<li>
|
||||
<p><b>Stay in the browser</b>: If you prefer convenience, you can simply use the <span class="logo">pairgoth</span> instance graciously hosted by the French Go Federation.</p>
|
||||
<p>Note that login to this instance is reserved to French federation actors plus several external people at our discretion. Send us <a href="mailto:pairgothjeudego.org">an email</a> to request an access.</p>
|
||||
<blockquote>
|
||||
<a class="nobreak" href="/login">Launch <span class="logo">pairgoth</span></a>
|
||||
</blockquote>
|
||||
|
@@ -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) {
|
||||
|
@@ -30,30 +30,71 @@
|
||||
#elseif($auth == 'oauth')
|
||||
|
||||
<div id="login" class="section">
|
||||
<div>Log in using</div>
|
||||
<div id="oauth-buttons">
|
||||
<div id="oauth-buttons" class="roundbox">
|
||||
#foreach($provider in $oauthProviders)
|
||||
<div>
|
||||
<button id="login-$provider" class="ui floating basic button">$provider</button>
|
||||
</div>
|
||||
<form>
|
||||
<label>Log in using</label>
|
||||
<button id="login-$provider" type="button" class="ui green floating button">$provider</button>
|
||||
#if($provider == 'ffg')
|
||||
(reserved to FFG actors)
|
||||
#end
|
||||
</form>
|
||||
#end
|
||||
</div>
|
||||
<div class="roundbox">
|
||||
Log in using an email
|
||||
<form id="login-form" class="ui form">
|
||||
<div class="centered inline fields">
|
||||
<div class="field">
|
||||
<input name="email" type="text" placeholder="email"/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<input name="password" type="password" placeholder="password"/>
|
||||
</div>
|
||||
<button id="login-email" type="button" class="ui green floating button">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
async function digestMessage(message) {
|
||||
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
|
||||
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
|
||||
return hashHex;
|
||||
}
|
||||
|
||||
onLoad(()=> {
|
||||
#foreach($provider in $oauthProviders)
|
||||
let buttonId = '#login-$provider';
|
||||
let loginURL= '$application.getAttribute("${provider}Provider").getLoginURL($session.id)';
|
||||
// #[[
|
||||
console.log(`buttonId = ${buttonId}`);
|
||||
console.log(`loginURL = ${loginURL}`);
|
||||
$(buttonId).on('click', e => {
|
||||
document.location.href = loginURL;
|
||||
});
|
||||
// ]]#
|
||||
#end
|
||||
// #[[
|
||||
$('#login-email').on('click', e => {
|
||||
let form = $('#login-form')[0]
|
||||
let password = form.val('password');
|
||||
digestMessage(password).then(enc => {
|
||||
let payload = {
|
||||
'email': form.val('email'),
|
||||
'password': enc
|
||||
}
|
||||
api.postJson('login', payload)
|
||||
.then(resp => {
|
||||
if (resp !== 'error' && resp.status === 'ok') {
|
||||
document.location.href = '/index'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
// ]]#
|
||||
});
|
||||
</script>
|
||||
|
||||
|
Reference in New Issue
Block a user