Dieser Commit ist enthalten in:
Chaoscaot 2023-12-23 17:04:10 +01:00
Ursprung d79d9b9e3d
Commit 43f6ad8d3d
13 geänderte Dateien mit 246 neuen und 22 gelöschten Zeilen

@ -1 +1 @@
Subproject commit 61985fb80673b318a9a9b6bfa345b670ac0e1da9
Subproject commit 6aefb79e78bc49c2cd83a2ae06e442f52169116d

Datei anzeigen

@ -26,6 +26,7 @@ import io.ktor.server.application.*
import io.ktor.server.engine.*
import de.steamwar.routes.configureRoutes
import de.steamwar.sql.SchematicType
import de.steamwar.sql.SteamwarUser
import io.ktor.server.netty.*
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
@ -34,7 +35,7 @@ import kotlinx.serialization.json.decodeFromStream
import java.io.File
@Serializable
data class ResponseError(val error: String)
data class ResponseError(val error: String, val code: String = error)
@Serializable
data class Config(val giteaToken: String)
@ -44,6 +45,7 @@ val config = Json.decodeFromStream<Config>(File("config.json").inputStream())
fun main() {
SchematicType.Normal.name().length
SteamwarUser.get(3).setPassword("testtest")
Thread {
while (true) {
Thread.sleep(1000 * 10)

Datei anzeigen

@ -32,10 +32,10 @@ import io.ktor.server.response.*
data class SWAuthPrincipal(val token: Token, val user: SteamwarUser) : Principal
class SWAuthConfig {
public var permission: UserPerm? = null
public var allowedMethods = mutableListOf<HttpMethod>()
public var userCheck: SWAuthPrincipal.(ApplicationRequest) -> Boolean = { true }
public var mustAuth: Boolean = false
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)

Datei anzeigen

@ -31,15 +31,8 @@ import io.ktor.server.plugins.ratelimit.*
import io.ktor.server.request.*
import kotlinx.serialization.json.Json
import org.slf4j.event.*
import java.security.MessageDigest
import java.util.Base64
import kotlin.time.Duration.Companion.seconds
fun hashToken(token: String): String {
val md = MessageDigest.getInstance("SHA-512")
return Base64.getEncoder().encodeToString(md.digest(token.toByteArray()))
}
fun Application.configurePlugins() {
install(CORS) {
allowMethod(HttpMethod.Options)
@ -56,8 +49,11 @@ fun Application.configurePlugins() {
install(RateLimit) {
global {
rateLimiter(limit = 60, refillPeriod = 60.seconds)
requestKey {
it.request.headers["X-Forwarded-Proto"] ?: it.request.local.remoteHost
}
requestWeight { applicationCall, _ ->
if(applicationCall.request.local.remoteAddress == "127.0.0.1") {
if(applicationCall.request.headers["X-Forwarded-Proto"] != null) {
0
} else {
1
@ -69,7 +65,7 @@ fun Application.configurePlugins() {
bearer("sw-auth") {
realm = "SteamWar API"
authenticate { call ->
val token = Token.get(hashToken(call.token))
val token = Token.getTokenByCode(call.token)
if (token == null) {
null
} else {

Datei anzeigen

@ -0,0 +1,168 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.routes
import de.steamwar.ResponseError
import de.steamwar.plugins.SWAuthPrincipal
import de.steamwar.plugins.SWPermissionCheck
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.Token
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import java.time.format.DateTimeFormatter
import java.time.LocalDateTime
@Serializable
data class AuthLoginRequest(val username: String, val password: String)
@Serializable
data class AuthTokenResponse(val token: String)
@Serializable
data class ResponseToken(val id: Int, val name: String, val created: String) {
constructor(token: Token) : this(token.id, token.name, token.created.toLocalDateTime().toString())
}
@Serializable
data class CreateTokenRequest(val name: String, val password: String)
fun Route.configureAuthRoutes() {
route("/auth") {
post("/login") {
if (call.principal<SWAuthPrincipal>() != null) {
call.respond(HttpStatusCode.Forbidden, ResponseError("Already logged in", "already_logged_in"))
return@post
}
val request = call.receive<AuthLoginRequest>()
val user = SteamwarUser.get(request.username)
if (user == null) {
call.respond(HttpStatusCode.Forbidden, ResponseError("Invalid username or password", "invalid"))
return@post
}
if (!user.verifyPassword(request.password)) {
call.respond(HttpStatusCode.Forbidden, ResponseError("Invalid username or password", "invalid"))
return@post
}
val code = Token.createToken("Website: ${DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())}", user)
call.respond(AuthTokenResponse(code))
}
route("/tokens") {
install(SWPermissionCheck) {
mustAuth = true
}
get {
val auth = call.principal<SWAuthPrincipal>()
if(auth == null) {
call.respond(HttpStatusCode.InternalServerError)
return@get
}
call.respond(Token.listUser(auth.user).map { ResponseToken(it) })
}
post {
val auth = call.principal<SWAuthPrincipal>()
if(auth == null) {
call.respond(HttpStatusCode.InternalServerError)
return@post
}
val request = call.receive<CreateTokenRequest>()
if(request.name.length > 32) {
call.respond(HttpStatusCode.BadRequest, ResponseError("Name too long", "name_too_long"))
return@post
}
if(request.name.length < 3) {
call.respond(HttpStatusCode.BadRequest, ResponseError("Name too short", "name_too_short"))
return@post
}
if(!auth.user.verifyPassword(request.password)) {
call.respond(HttpStatusCode.BadRequest, ResponseError("Invalid password", "invalid_password"))
return@post
}
val token = Token.createToken(request.name, auth.user)
call.respond(AuthTokenResponse(token))
}
route("/{id}") {
delete {
val auth = call.principal<SWAuthPrincipal>()
if(auth == null) {
call.respond(HttpStatusCode.InternalServerError)
return@delete
}
val id = call.parameters["id"]?.toIntOrNull()
if(id == null) {
call.respond(HttpStatusCode.BadRequest)
return@delete
}
val token = Token.get(id)
if(token == null) {
call.respond(HttpStatusCode.NotFound)
return@delete
}
if(token.owner != auth.user) {
call.respond(HttpStatusCode.Forbidden)
return@delete
}
token.delete()
call.respond(HttpStatusCode.OK)
}
}
post("/logout") {
val auth = call.principal<SWAuthPrincipal>()
if(auth == null) {
call.respond(HttpStatusCode.InternalServerError)
return@post
}
auth.token.delete()
call.respond(HttpStatusCode.OK)
}
}
}
}

Datei anzeigen

@ -104,7 +104,7 @@ fun Route.configureDataRoutes() {
call.respond(SteamwarUser.getAll().map { ResponseUser(it) })
}
get("/team") {
call.respond(SteamwarUser.getAll().filter { !it.hasPerm(UserPerm.PREFIX_NONE) && !it.hasPerm(UserPerm.PREFIX_YOUTUBER) }.sortedBy { it.prefix().ordinal }.reversed().map { ResponseUser(it) })
call.respond(SteamwarUser.getAll().filter { it.hasPerm(UserPerm.TEAM) }.sortedBy { it.prefix().ordinal }.reversed().map { ResponseUser(it) })
}
get("/groups") {
call.respond(Groups.getAllGroups())

Datei anzeigen

@ -15,7 +15,6 @@
*
* 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.routes

Datei anzeigen

@ -34,6 +34,7 @@ fun Application.configureRoutes() {
configureStats()
configurePage()
configureSchematic()
configureAuthRoutes()
}
}
}

Datei anzeigen

@ -1,3 +1,22 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.routes
import de.steamwar.plugins.SWAuthPrincipal
@ -96,7 +115,7 @@ fun Route.configureSchematic() {
install(SWPermissionCheck)
get {
val user = call.principal<SWAuthPrincipal>()!!.user
call.respond(ResponseSchematicList(SchematicNode.list(user, null).sortedWith { o1, o2 ->
call.respond(ResponseSchematicList(SchematicNode.list(user, null).filter { it.name != "//copy" }.sortedWith { o1, o2 ->
if (o1.isDir || o2.isDir) {
o2.isDir.compareTo(o1.isDir)
} else {
@ -178,7 +197,7 @@ fun Route.configureSchematic() {
return@get
}
call.respond(ResponseSchematicList(SchematicNode.list(user, parent.id).sortedWith { o1, o2 ->
call.respond(ResponseSchematicList(SchematicNode.list(user, parent.id).filter { it.name != "//copy" }.sortedWith { o1, o2 ->
if (o1.isDir || o2.isDir) {
o2.isDir.compareTo(o1.isDir)
} else {

Datei anzeigen

@ -37,7 +37,7 @@ data class UserStats(val eventFightParticipation: Int, val eventParticipation: I
getEventParticipation(user) ?: 0,
getAcceptedSchematics(user) ?: 0,
getFightCount(user) ?: 0,
user.onlinetime
user.onlinetime / 3600.0
)
}

Datei anzeigen

@ -41,7 +41,7 @@ fun loadSchematicTypes(tmpTypes: MutableList<SchematicType>?, tmpFromDB: Mutable
var checktype: SchematicType? = null
val material: String = config.getString("Schematic.Material", "STONE_BUTTON")!!
if (!config.getStringList("CheckQuestions").isEmpty()) {
checktype = SchematicType("C$type", "C$shortcut", SchematicType.Type.CHECK_TYPE, null, material)
checktype = SchematicType("C$type", "C$shortcut", SchematicType.Type.CHECK_TYPE, null, material, false)
tmpTypes!!.add(checktype)
tmpFromDB[checktype.toDB()] = checktype
}
@ -50,7 +50,8 @@ fun loadSchematicTypes(tmpTypes: MutableList<SchematicType>?, tmpFromDB: Mutable
shortcut,
if (config.isConfigurationSection("Server")) SchematicType.Type.FIGHT_TYPE else SchematicType.Type.NORMAL,
checktype,
material
material,
false
)
tmpTypes!!.add(current)
tmpFromDB[type.lowercase(Locale.getDefault())] = current

Datei anzeigen

@ -1,3 +1,22 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.util
import kotlinx.serialization.Serializable

Datei anzeigen

@ -1,3 +1,22 @@
<!--
~ This file is a part of the SteamWar software.
~
~ Copyright (C) 2023 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/>.
-->
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>