refactor: extract shared auth route handlers and route registration for type/scripts/meta/run
This commit is contained in:
@@ -3,11 +3,13 @@ package work.slhaf.hub
|
|||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.server.application.Application
|
import io.ktor.server.application.Application
|
||||||
|
import io.ktor.server.application.ApplicationCall
|
||||||
import io.ktor.server.application.call
|
import io.ktor.server.application.call
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import io.ktor.server.netty.Netty
|
import io.ktor.server.netty.Netty
|
||||||
import io.ktor.server.request.receiveText
|
import io.ktor.server.request.receiveText
|
||||||
import io.ktor.server.response.respondText
|
import io.ktor.server.response.respondText
|
||||||
|
import io.ktor.server.routing.Routing
|
||||||
import io.ktor.server.routing.delete
|
import io.ktor.server.routing.delete
|
||||||
import io.ktor.server.routing.get
|
import io.ktor.server.routing.get
|
||||||
import io.ktor.server.routing.post
|
import io.ktor.server.routing.post
|
||||||
@@ -90,33 +92,118 @@ private suspend fun handleSubTokenDelete(call: io.ktor.server.application.Applic
|
|||||||
call.respondText("deleted subtoken: $name")
|
call.respondText("deleted subtoken: $name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun handleTypeForAuth(call: ApplicationCall, auth: AuthContext) {
|
||||||
|
call.respondText(tokenTypeJson(auth), contentType = ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleScriptsForAuth(call: ApplicationCall, scriptsDir: File, auth: AuthContext) {
|
||||||
|
val allow = visibleScriptsFor(auth)
|
||||||
|
call.respondText(renderScriptList(scriptsDir, allow), ContentType.Text.Plain)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleMetaForAuth(call: ApplicationCall, scriptsDir: File, auth: AuthContext) {
|
||||||
|
val name = call.parameters["script"]
|
||||||
|
?: return call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
||||||
|
if (!requireScriptAccess(call, auth, name)) return
|
||||||
|
|
||||||
|
val script = resolveScriptFile(scriptsDir, name)
|
||||||
|
?: return call.respondText("invalid script name", status = HttpStatusCode.BadRequest)
|
||||||
|
if (!script.exists()) {
|
||||||
|
return call.respondText("script not found: ${script.name}", status = HttpStatusCode.NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
val (metadata, source) = loadMetadata(script)
|
||||||
|
call.respondText(
|
||||||
|
metadataJson(name, metadata, source),
|
||||||
|
contentType = ContentType.Application.Json,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleRunForAuth(
|
||||||
|
call: ApplicationCall,
|
||||||
|
scriptsDir: File,
|
||||||
|
auth: AuthContext,
|
||||||
|
runConcurrencyLimiter: Semaphore,
|
||||||
|
consumeBody: Boolean,
|
||||||
|
) {
|
||||||
|
val name = call.parameters["script"]
|
||||||
|
?: return call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
||||||
|
if (!requireScriptAccess(call, auth, name)) return
|
||||||
|
runConcurrencyLimiter.withPermit {
|
||||||
|
handleRunRequest(call, scriptsDir, consumeBody = consumeBody)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Routing.registerHeaderAuthenticatedRoutes(
|
||||||
|
scriptsDir: File,
|
||||||
|
security: HostSecurity,
|
||||||
|
runConcurrencyLimiter: Semaphore,
|
||||||
|
) {
|
||||||
|
get("/type") {
|
||||||
|
val auth = requireAuth(call, security) ?: return@get
|
||||||
|
handleTypeForAuth(call, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/scripts") {
|
||||||
|
val auth = requireAuth(call, security) ?: return@get
|
||||||
|
handleScriptsForAuth(call, scriptsDir, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/meta/{script}") {
|
||||||
|
val auth = requireAuth(call, security) ?: return@get
|
||||||
|
handleMetaForAuth(call, scriptsDir, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/run/{script}") {
|
||||||
|
val auth = requireAuth(call, security) ?: return@get
|
||||||
|
handleRunForAuth(call, scriptsDir, auth, runConcurrencyLimiter, consumeBody = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/run/{script}") {
|
||||||
|
val auth = requireAuth(call, security) ?: return@post
|
||||||
|
handleRunForAuth(call, scriptsDir, auth, runConcurrencyLimiter, consumeBody = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Routing.registerSubTokenPathRoutes(
|
||||||
|
scriptsDir: File,
|
||||||
|
security: HostSecurity,
|
||||||
|
runConcurrencyLimiter: Semaphore,
|
||||||
|
) {
|
||||||
|
get("/u/{subAuth}/type") {
|
||||||
|
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
||||||
|
handleTypeForAuth(call, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/u/{subAuth}/scripts") {
|
||||||
|
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
||||||
|
handleScriptsForAuth(call, scriptsDir, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/u/{subAuth}/meta/{script}") {
|
||||||
|
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
||||||
|
handleMetaForAuth(call, scriptsDir, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/u/{subAuth}/run/{script}") {
|
||||||
|
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
||||||
|
handleRunForAuth(call, scriptsDir, auth, runConcurrencyLimiter, consumeBody = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/u/{subAuth}/run/{script}") {
|
||||||
|
val auth = requireSubTokenPathAuth(call, security) ?: return@post
|
||||||
|
handleRunForAuth(call, scriptsDir, auth, runConcurrencyLimiter, consumeBody = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Application.webModule(scriptsDir: File, security: HostSecurity, runConcurrencyLimiter: Semaphore) {
|
fun Application.webModule(scriptsDir: File, security: HostSecurity, runConcurrencyLimiter: Semaphore) {
|
||||||
routing {
|
routing {
|
||||||
get("/health") {
|
get("/health") {
|
||||||
call.respondText("OK")
|
call.respondText("OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
get("/type") {
|
registerHeaderAuthenticatedRoutes(scriptsDir, security, runConcurrencyLimiter)
|
||||||
val auth = requireAuth(call, security) ?: return@get
|
registerSubTokenPathRoutes(scriptsDir, security, runConcurrencyLimiter)
|
||||||
call.respondText(tokenTypeJson(auth), contentType = ContentType.Application.Json)
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/u/{subAuth}/type") {
|
|
||||||
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
|
||||||
call.respondText(tokenTypeJson(auth), contentType = ContentType.Application.Json)
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/scripts") {
|
|
||||||
val auth = requireAuth(call, security) ?: return@get
|
|
||||||
val allow = visibleScriptsFor(auth)
|
|
||||||
call.respondText(renderScriptList(scriptsDir, allow), ContentType.Text.Plain)
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/u/{subAuth}/scripts") {
|
|
||||||
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
|
||||||
val allow = visibleScriptsFor(auth)
|
|
||||||
call.respondText(renderScriptList(scriptsDir, allow), ContentType.Text.Plain)
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/scripts/{script}") {
|
get("/scripts/{script}") {
|
||||||
val auth = requireAuth(call, security) ?: return@get
|
val auth = requireAuth(call, security) ?: return@get
|
||||||
@@ -142,86 +229,6 @@ fun Application.webModule(scriptsDir: File, security: HostSecurity, runConcurren
|
|||||||
handleDeleteScript(call, scriptsDir)
|
handleDeleteScript(call, scriptsDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
get("/meta/{script}") {
|
|
||||||
val auth = requireAuth(call, security) ?: return@get
|
|
||||||
val name = call.parameters["script"]
|
|
||||||
?: return@get call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
|
||||||
|
|
||||||
if (!requireScriptAccess(call, auth, name)) return@get
|
|
||||||
|
|
||||||
val script = resolveScriptFile(scriptsDir, name)
|
|
||||||
?: return@get call.respondText("invalid script name", status = HttpStatusCode.BadRequest)
|
|
||||||
if (!script.exists()) {
|
|
||||||
return@get call.respondText("script not found: ${script.name}", status = HttpStatusCode.NotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
val (metadata, source) = loadMetadata(script)
|
|
||||||
call.respondText(
|
|
||||||
metadataJson(name, metadata, source),
|
|
||||||
contentType = ContentType.Application.Json,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/u/{subAuth}/meta/{script}") {
|
|
||||||
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
|
||||||
val name = call.parameters["script"]
|
|
||||||
?: return@get call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
|
||||||
|
|
||||||
if (!requireScriptAccess(call, auth, name)) return@get
|
|
||||||
|
|
||||||
val script = resolveScriptFile(scriptsDir, name)
|
|
||||||
?: return@get call.respondText("invalid script name", status = HttpStatusCode.BadRequest)
|
|
||||||
if (!script.exists()) {
|
|
||||||
return@get call.respondText("script not found: ${script.name}", status = HttpStatusCode.NotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
val (metadata, source) = loadMetadata(script)
|
|
||||||
call.respondText(
|
|
||||||
metadataJson(name, metadata, source),
|
|
||||||
contentType = ContentType.Application.Json,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/run/{script}") {
|
|
||||||
val auth = requireAuth(call, security) ?: return@get
|
|
||||||
val name = call.parameters["script"]
|
|
||||||
?: return@get call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
|
||||||
if (!requireScriptAccess(call, auth, name)) return@get
|
|
||||||
runConcurrencyLimiter.withPermit {
|
|
||||||
handleRunRequest(call, scriptsDir, consumeBody = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/u/{subAuth}/run/{script}") {
|
|
||||||
val auth = requireSubTokenPathAuth(call, security) ?: return@get
|
|
||||||
val name = call.parameters["script"]
|
|
||||||
?: return@get call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
|
||||||
if (!requireScriptAccess(call, auth, name)) return@get
|
|
||||||
runConcurrencyLimiter.withPermit {
|
|
||||||
handleRunRequest(call, scriptsDir, consumeBody = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post("/run/{script}") {
|
|
||||||
val auth = requireAuth(call, security) ?: return@post
|
|
||||||
val name = call.parameters["script"]
|
|
||||||
?: return@post call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
|
||||||
if (!requireScriptAccess(call, auth, name)) return@post
|
|
||||||
runConcurrencyLimiter.withPermit {
|
|
||||||
handleRunRequest(call, scriptsDir, consumeBody = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post("/u/{subAuth}/run/{script}") {
|
|
||||||
val auth = requireSubTokenPathAuth(call, security) ?: return@post
|
|
||||||
val name = call.parameters["script"]
|
|
||||||
?: return@post call.respondText("missing route name", status = HttpStatusCode.BadRequest)
|
|
||||||
if (!requireScriptAccess(call, auth, name)) return@post
|
|
||||||
runConcurrencyLimiter.withPermit {
|
|
||||||
handleRunRequest(call, scriptsDir, consumeBody = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/subtokens") {
|
get("/subtokens") {
|
||||||
val auth = requireAuth(call, security) ?: return@get
|
val auth = requireAuth(call, security) ?: return@get
|
||||||
if (!requireRoot(call, auth)) return@get
|
if (!requireRoot(call, auth)) return@get
|
||||||
|
|||||||
Reference in New Issue
Block a user