implement 'sesame' auth
This commit is contained in:
@@ -92,6 +92,7 @@
|
||||
<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.auth>${pairgoth.auth}</pairgoth.auth>
|
||||
<pairgoth.auth.sesame>${pairgoth.auth.sesame}</pairgoth.auth.sesame>
|
||||
<org.slf4j.simpleLogger.defaultLogLevel>debug</org.slf4j.simpleLogger.defaultLogLevel>
|
||||
</systemProperties>
|
||||
<webApp>
|
||||
|
@@ -29,22 +29,31 @@ class AuthFilter: Filter {
|
||||
val session: HttpSession? = request.getSession(false)
|
||||
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)
|
||||
} else {
|
||||
// TODO - configure if unauth requests are redirected and/or forwarded
|
||||
// TODO - protection against brute force attacks
|
||||
if (uri == "/index") {
|
||||
if (uri.endsWith("/index")) {
|
||||
request.getRequestDispatcher("/index-ffg").forward(req, resp)
|
||||
} else {
|
||||
response.sendRedirect("/login")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val whitelist = setOf(
|
||||
"/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>
|
||||
<async-supported>true</async-supported>
|
||||
</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-mapping>
|
||||
@@ -110,6 +116,10 @@
|
||||
<servlet-name>import</servlet-name>
|
||||
<url-pattern>/api/import/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet-mapping>
|
||||
<servlet-name>login</servlet-name>
|
||||
<url-pattern>/api/login</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- context params -->
|
||||
<context-param>
|
||||
|
@@ -219,7 +219,7 @@ onLoad(() => {
|
||||
break;
|
||||
}
|
||||
case 'ArrowDown': {
|
||||
if (searchResultShown()) {
|
||||
if (typeof(searchResultShown) === 'function' && searchResultShown()) {
|
||||
let lines = $('.result-line');
|
||||
if (typeof (searchHighlight) === 'undefined') searchHighlight = 0;
|
||||
else ++searchHighlight;
|
||||
@@ -230,7 +230,7 @@ onLoad(() => {
|
||||
break;
|
||||
}
|
||||
case 'ArrowUp': {
|
||||
if (searchResultShown()) {
|
||||
if (typeof(searchResultShown) === 'function' && searchResultShown()) {
|
||||
let lines = $('.result-line');
|
||||
if (typeof (searchHighlight) === 'undefined') searchHighlight = 0;
|
||||
else --searchHighlight;
|
||||
@@ -241,11 +241,13 @@ onLoad(() => {
|
||||
break;
|
||||
}
|
||||
case 'Enter': {
|
||||
if (typeof(searchResultShown) === 'function') {
|
||||
if (searchResultShown()) {
|
||||
fillPlayer(searchResult[searchHighlight]);
|
||||
} else {
|
||||
$('#register')[0].click();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<div id="login" class="section">
|
||||
<form id="login-form" class="ui form">
|
||||
<form id="login-form" class="ui form" autocomplete="off">
|
||||
<div class="field">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
@@ -10,9 +10,9 @@
|
||||
<script type="text/javascript">
|
||||
onLoad(()=>{
|
||||
$('#login-form').on('submit', e => {
|
||||
api.postJson('login', { sesame: $('input[name="sesame"]').val() })
|
||||
api.postJson('login', { sesame: $('input[name="sesame"]')[0].value })
|
||||
.then(resp => {
|
||||
if (resp !== 'error') {
|
||||
if (resp !== 'error' && resp.status === 'ok') {
|
||||
document.location.href = '/index'
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user