implement 'sesame' auth
This commit is contained in:
@@ -92,6 +92,7 @@
|
|||||||
<pairgoth.logger.format>${pairgoth.logger.format}</pairgoth.logger.format>
|
<pairgoth.logger.format>${pairgoth.logger.format}</pairgoth.logger.format>
|
||||||
<pairgoth.logger.level.org.jeudego.pairgoth.web.ApiServlet.api>debug</pairgoth.logger.level.org.jeudego.pairgoth.web.ApiServlet.api>
|
<pairgoth.logger.level.org.jeudego.pairgoth.web.ApiServlet.api>debug</pairgoth.logger.level.org.jeudego.pairgoth.web.ApiServlet.api>
|
||||||
<pairgoth.auth>${pairgoth.auth}</pairgoth.auth>
|
<pairgoth.auth>${pairgoth.auth}</pairgoth.auth>
|
||||||
|
<pairgoth.auth.sesame>${pairgoth.auth.sesame}</pairgoth.auth.sesame>
|
||||||
<org.slf4j.simpleLogger.defaultLogLevel>debug</org.slf4j.simpleLogger.defaultLogLevel>
|
<org.slf4j.simpleLogger.defaultLogLevel>debug</org.slf4j.simpleLogger.defaultLogLevel>
|
||||||
</systemProperties>
|
</systemProperties>
|
||||||
<webApp>
|
<webApp>
|
||||||
|
@@ -29,22 +29,31 @@ class AuthFilter: Filter {
|
|||||||
val session: HttpSession? = request.getSession(false)
|
val session: HttpSession? = request.getSession(false)
|
||||||
val auth = WebappManager.getProperty("auth") ?: throw Error("authentication not configured")
|
val auth = WebappManager.getProperty("auth") ?: throw Error("authentication not configured")
|
||||||
|
|
||||||
if (auth == "none" || whitelist.contains(uri) || uri.contains(Regex("\\.(?!html)")) || session?.getAttribute("logged") != null) {
|
if (auth == "none" || whitelisted(uri) || session?.getAttribute("logged") != null) {
|
||||||
chain.doFilter(req, resp)
|
chain.doFilter(req, resp)
|
||||||
} else {
|
} else {
|
||||||
// TODO - configure if unauth requests are redirected and/or forwarded
|
// TODO - configure if unauth requests are redirected and/or forwarded
|
||||||
// TODO - protection against brute force attacks
|
// TODO - protection against brute force attacks
|
||||||
if (uri == "/index") {
|
if (uri.endsWith("/index")) {
|
||||||
request.getRequestDispatcher("/index-ffg").forward(req, resp)
|
request.getRequestDispatcher("/index-ffg").forward(req, resp)
|
||||||
} else {
|
} else {
|
||||||
response.sendRedirect("/login")
|
response.sendRedirect("/login")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val whitelist = setOf(
|
private val whitelist = setOf(
|
||||||
"/index-ffg",
|
"/index-ffg",
|
||||||
"/login"
|
"/login",
|
||||||
|
"/api/login"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun whitelisted(uri: String): Boolean {
|
||||||
|
if (uri.contains(Regex("\\.(?!html)"))) return true
|
||||||
|
val nolangUri = uri.replace(Regex("^/../"), "/")
|
||||||
|
return whitelist.contains(nolangUri)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
package org.jeudego.pairgoth.web
|
||||||
|
|
||||||
|
import com.republicate.kson.Json
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import javax.servlet.http.HttpServlet
|
||||||
|
import javax.servlet.http.HttpServletRequest
|
||||||
|
import javax.servlet.http.HttpServletResponse
|
||||||
|
|
||||||
|
class LoginServlet: HttpServlet() {
|
||||||
|
|
||||||
|
override fun doPost(req: HttpServletRequest, resp: HttpServletResponse) {
|
||||||
|
try {
|
||||||
|
val contentType = req.contentType
|
||||||
|
val sep = contentType.indexOf(';')
|
||||||
|
val mimeType = if (sep == -1) contentType else contentType.substring(0, sep).trim { it <= ' ' }
|
||||||
|
if (!isJson(mimeType)) throw Error("expecting json")
|
||||||
|
val payload = Json.Companion.parse(req.reader.readText())?.asObject() ?: throw Error("null json")
|
||||||
|
val user = when (WebappManager.getProperty("auth")) {
|
||||||
|
"sesame" -> checkSesame(payload)
|
||||||
|
else -> null
|
||||||
|
} ?: throw Error("authentication failed")
|
||||||
|
req.session.setAttribute("logged", user)
|
||||||
|
val ret = Json.Object("status" to "ok")
|
||||||
|
resp.contentType = "application/json"
|
||||||
|
resp.writer.println(ret.toString())
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logger.error("exception while logging in", t)
|
||||||
|
resp.sendError(HttpServletResponse.SC_BAD_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkSesame(payload: Json.Object): Boolean? {
|
||||||
|
val expected = WebappManager.getProperty("auth.sesame") ?: throw Error("sesame wrongly configured")
|
||||||
|
return if (payload.getString("sesame")?.equals(expected) == true) true else null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun isJson(mimeType: String) = "text/json" == mimeType || "application/json" == mimeType || mimeType.endsWith("+json")
|
||||||
|
val logger = LoggerFactory.getLogger("login")
|
||||||
|
}
|
||||||
|
}
|
@@ -88,6 +88,12 @@
|
|||||||
<load-on-startup>1</load-on-startup>
|
<load-on-startup>1</load-on-startup>
|
||||||
<async-supported>true</async-supported>
|
<async-supported>true</async-supported>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>login</servlet-name>
|
||||||
|
<servlet-class>org.jeudego.pairgoth.web.LoginServlet</servlet-class>
|
||||||
|
<load-on-startup>1</load-on-startup>
|
||||||
|
<async-supported>true</async-supported>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
<!-- servlet mappings -->
|
<!-- servlet mappings -->
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
@@ -110,6 +116,10 @@
|
|||||||
<servlet-name>import</servlet-name>
|
<servlet-name>import</servlet-name>
|
||||||
<url-pattern>/api/import/*</url-pattern>
|
<url-pattern>/api/import/*</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>login</servlet-name>
|
||||||
|
<url-pattern>/api/login</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
<!-- context params -->
|
<!-- context params -->
|
||||||
<context-param>
|
<context-param>
|
||||||
|
@@ -219,7 +219,7 @@ onLoad(() => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'ArrowDown': {
|
case 'ArrowDown': {
|
||||||
if (searchResultShown()) {
|
if (typeof(searchResultShown) === 'function' && searchResultShown()) {
|
||||||
let lines = $('.result-line');
|
let lines = $('.result-line');
|
||||||
if (typeof (searchHighlight) === 'undefined') searchHighlight = 0;
|
if (typeof (searchHighlight) === 'undefined') searchHighlight = 0;
|
||||||
else ++searchHighlight;
|
else ++searchHighlight;
|
||||||
@@ -230,7 +230,7 @@ onLoad(() => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'ArrowUp': {
|
case 'ArrowUp': {
|
||||||
if (searchResultShown()) {
|
if (typeof(searchResultShown) === 'function' && searchResultShown()) {
|
||||||
let lines = $('.result-line');
|
let lines = $('.result-line');
|
||||||
if (typeof (searchHighlight) === 'undefined') searchHighlight = 0;
|
if (typeof (searchHighlight) === 'undefined') searchHighlight = 0;
|
||||||
else --searchHighlight;
|
else --searchHighlight;
|
||||||
@@ -241,10 +241,12 @@ onLoad(() => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'Enter': {
|
case 'Enter': {
|
||||||
if (searchResultShown()) {
|
if (typeof(searchResultShown) === 'function') {
|
||||||
fillPlayer(searchResult[searchHighlight]);
|
if (searchResultShown()) {
|
||||||
} else {
|
fillPlayer(searchResult[searchHighlight]);
|
||||||
$('#register')[0].click();
|
} else {
|
||||||
|
$('#register')[0].click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<div id="login" class="section">
|
<div id="login" class="section">
|
||||||
<form id="login-form" class="ui form">
|
<form id="login-form" class="ui form" autocomplete="off">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Enter the magic word</label>
|
<label>Enter the magic word</label>
|
||||||
<input type="text" name="sesame"/>
|
<input type="text" name="sesame" autocomplete="false"/>
|
||||||
<button type="submit" class="ui green floating button">Log in</button>
|
<button type="submit" class="ui green floating button">Log in</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
onLoad(()=>{
|
onLoad(()=>{
|
||||||
$('#login-form').on('submit', e => {
|
$('#login-form').on('submit', e => {
|
||||||
api.postJson('login', { sesame: $('input[name="sesame"]').val() })
|
api.postJson('login', { sesame: $('input[name="sesame"]')[0].value })
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (resp !== 'error') {
|
if (resp !== 'error' && resp.status === 'ok') {
|
||||||
document.location.href = '/index'
|
document.location.href = '/index'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user