/* * 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 . */ 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() var userCheck: SWAuthPrincipal.(ApplicationRequest) -> Boolean = { true } var mustAuth: Boolean = false fun allowMethod(method: HttpMethod) { allowedMethods.add(method) } fun allowMethods(methods: List) { 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() if (token == null) { call.respond(HttpStatusCode.Unauthorized) } } return@on } val token = call.principal() 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) }*/ }