forked from SteamWar/SteamWar
130 lines
3.9 KiB
Kotlin
130 lines
3.9 KiB
Kotlin
/*
|
|
* This file is a part of the SteamWar software.
|
|
*
|
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package de.steamwar.plugins
|
|
|
|
import de.steamwar.sql.SWException
|
|
import de.steamwar.sql.SteamwarUser
|
|
import de.steamwar.sql.Token
|
|
import de.steamwar.sql.UserPerm
|
|
import io.ktor.http.*
|
|
import io.ktor.server.application.*
|
|
import io.ktor.server.application.hooks.*
|
|
import io.ktor.server.auth.*
|
|
import io.ktor.server.request.*
|
|
import io.ktor.server.response.*
|
|
import io.ktor.util.*
|
|
|
|
|
|
data class SWAuthPrincipal(val token: Token, val user: SteamwarUser) : Principal
|
|
|
|
class SWAuthConfig {
|
|
var permission: UserPerm? = null
|
|
var allowedMethods = mutableListOf<HttpMethod>()
|
|
var userCheck: SWAuthPrincipal.(ApplicationRequest) -> Boolean = { true }
|
|
var mustAuth: Boolean = false
|
|
|
|
fun allowMethod(method: HttpMethod) {
|
|
allowedMethods.add(method)
|
|
}
|
|
|
|
fun allowMethods(methods: List<HttpMethod>) {
|
|
allowedMethods.addAll(methods)
|
|
}
|
|
|
|
fun userCheck(check: SWAuthPrincipal.(ApplicationRequest) -> Boolean) {
|
|
userCheck = check
|
|
}
|
|
}
|
|
|
|
val SWPermissionCheck = createRouteScopedPlugin("SWAuth", ::SWAuthConfig) {
|
|
pluginConfig.apply {
|
|
on(AuthenticationChecked) { call ->
|
|
if (call.request.httpMethod in allowedMethods) {
|
|
if(mustAuth) {
|
|
val token = call.principal<SWAuthPrincipal>()
|
|
|
|
if (token == null) {
|
|
call.respond(HttpStatusCode.Unauthorized)
|
|
}
|
|
}
|
|
|
|
return@on
|
|
}
|
|
|
|
val token = call.principal<SWAuthPrincipal>()
|
|
|
|
if (token == null) {
|
|
call.respond(HttpStatusCode.Unauthorized)
|
|
return@on
|
|
}
|
|
|
|
if (permission != null && !token.user.hasPerm(permission!!)) {
|
|
call.respond(HttpStatusCode.Forbidden)
|
|
return@on
|
|
}
|
|
|
|
if (!token.userCheck(call.request)) {
|
|
call.respond(HttpStatusCode.Forbidden)
|
|
return@on
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val ErrorLogger = createApplicationPlugin("SWLogger") {
|
|
on(CallFailed) { call, cause ->
|
|
val msg = """
|
|
{
|
|
URI: ${call.request.uri}
|
|
Method: ${call.request.httpMethod.value}
|
|
Headers: ${call.request.headers.entries().joinToString("\n ") { "${it.key}: ${it.value}" }}
|
|
|
|
Message: ${cause.message}
|
|
}
|
|
"""
|
|
|
|
SWException.log(msg, cause.stackTraceToString())
|
|
call.response.headers.append("X-Caught", "1")
|
|
}
|
|
|
|
/*
|
|
onCallRespond { call ->
|
|
if ((call.response.status() ?: HttpStatusCode.OK).isSuccess()) {
|
|
return@onCallRespond
|
|
}
|
|
|
|
val msg = """
|
|
URI: ${call.request.uri}
|
|
Method: ${call.request.httpMethod.value}
|
|
Response: ${call.response.status()?.value}
|
|
IP: ${call.request.headers["X-Forwarded-For"] ?: call.request.local.remoteHost}
|
|
UserAgent: ${call.request.headers["User-Agent"]}
|
|
""".trimIndent()
|
|
|
|
val stack = """
|
|
Headers:
|
|
${call.request.headers.entries().joinToString("\n ") { "${it.key}: ${it.value}" }}
|
|
Body:
|
|
${call.request.receiveChannel().toByteArray().decodeToString()}
|
|
""".trimIndent()
|
|
|
|
SWException.log(msg, stack)
|
|
}*/
|
|
} |