initial commit - source

This commit is contained in:
Hartmut Bauer committed 2026-02-03 22:36:35 +01:00
1 parent 003b5501f0
commit e89bb29070
45 files changed
+5997

No files matched your search

@@ -0,0 +1,19 @@
package net.keinekekse.app
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import net.keinekekse.app.server.VERSION
import net.keinekekse.app.server.config.configureSecurity
fun main() {
// embeddedServer(Netty, port = SERVER_PORT, host = "0.0.0.0", module = Application::module)
println("**\n** Starting ${VERSION.displayName} - ${VERSION.description} ${VERSION.fullVersion}\n**")
embeddedServer(Netty, port = 7770, host = "0.0.0.0", module = Application::module)
.start(wait = true)
}
fun Application.module() {
configureSecurity()
}
@@ -0,0 +1,18 @@
package net.keinekekse.app.server
import net.keinekekse.version.Version
object VERSION: Version {
override val version = "0.1.0-beta.1+build.19"
override val fullVersion = "0.1.0-beta.1+build.19"
override val major = 0
override val minor = 1
override val patch = 0
override val preRelease = "beta.1"
override val buildMetadata = "build.19"
override val buildDate = "2026-02-02T23:47:38.629893Z"
override val displayName = "kAS"
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
override val name = "server"
override val description = "ktor Application-Server"
}
@@ -0,0 +1,22 @@
package net.keinekekse.app.server.config
import io.ktor.server.auth.jwt.*
import java.security.Principal
class AppPrincipal(credential: JWTCredential) : Principal {
val userId: String = credential.getClaim("preferred_username", String::class) ?: ""
val vorname: String = credential.getClaim("given_name", String::class) ?: ""
val nachname: String = credential.getClaim("family_name", String::class) ?: ""
val email: String = credential.getClaim("email", String::class) ?: ""
val roles: List<String>
init {
val realmAccess: Map<*, *>? = credential.getClaim("realm_access", Map::class)
val list = realmAccess?.get("roles")
roles = if (list is List<*>) list.map { it as String } else emptyList()
}
override fun getName(): String {
return "$userId ($vorname $nachname) <$email>"
}
}
@@ -0,0 +1,55 @@
package net.keinekekse.app.server.config
import com.auth0.jwk.UrlJwkProvider
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import net.keinekekse.app.server.VERSION
import java.net.URI
fun Application.configureSecurity() {
// TODO die Werte müssen aus der Config kommen
// Kommunikation funktioniert nur im internen Containernetzwerk,
// d.h. Container- bzw. Pod-Name + interner Port
val keycloakInternal = "http://kec:8080/realms"
val keycloak = "https://kec.keinekekse.net/realms"
val realm = "app-server-realm"
val clientId = "pkce-book-app"
val certUrl = "$keycloakInternal/$realm/protocol/openid-connect/certs"
val issuer = "$keycloak/$realm"
authentication {
jwt("auth-KC") {
verifier(
UrlJwkProvider(URI(certUrl).toURL()), issuer
) {
withAudience("account")
withClaim("azp", clientId)
}
challenge { _, _ ->
call.respond(HttpStatusCode.Unauthorized, "Token is not valid or has expired")
}
validate { credential ->
AppPrincipal(credential)
}
}
}
routing {
authenticate("auth-KC") {
get("/") {
val principal = call.principal<AppPrincipal>()
call.respondText("Ktor ${VERSION.displayName} ${VERSION.fullVersion} - principal=${principal?.name}")
}
get("/test") {
val principal = call.principal<AppPrincipal>()
call.respondText("user: ${principal?.userId}")
}
}
}
}