diff --git a/CHANGELOG.md b/CHANGELOG.md index 064d926c3..14a6dd6d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added ### Changed +- Can now put redoc route behind authentication ### Remove diff --git a/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt b/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt index df1262c5e..b67061894 100644 --- a/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt +++ b/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt @@ -3,7 +3,7 @@ package io.bkbn.kompendium.core.routes import io.ktor.http.HttpStatusCode import io.ktor.server.application.call import io.ktor.server.html.respondHtml -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.get import io.ktor.server.routing.route import kotlinx.html.body @@ -20,7 +20,7 @@ import kotlinx.html.unsafe * @param pageTitle Webpage title you wish to be displayed on your docs * @param specUrl url to point ReDoc to the OpenAPI json document */ -fun Routing.redoc(pageTitle: String = "Docs", specUrl: String = "/openapi.json") { +fun Route.redoc(pageTitle: String = "Docs", specUrl: String = "/openapi.json") { route("/docs") { get { call.respondHtml(HttpStatusCode.OK) { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt index e6c63c753..c2f6fbcea 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt @@ -30,10 +30,6 @@ import io.ktor.server.routing.route import io.ktor.server.routing.routing import kotlinx.serialization.json.Json -/** - * Application entrypoint. Run this and head on over to `localhost:8081/docs` - * to see a very simple yet beautifully documented API - */ fun main() { embeddedServer( Netty, @@ -43,7 +39,6 @@ fun main() { } private fun Application.mainModule() { - // Installs Simple JSON Content Negotiation install(ContentNegotiation) { json(Json { serializersModule = KompendiumSerializersModule.module @@ -75,10 +70,6 @@ private fun Application.mainModule() { routing { redoc(pageTitle = "Simple API Docs") authenticate("basic") { - // This adds ReDoc support at the `/docs` endpoint. - // By default, it will point at the `/openapi.json` created by Kompendium - - // Route with a get handler route("/{id}") { idDocumentation() get { @@ -89,7 +80,6 @@ private fun Application.mainModule() { } } -// Documentation for our route private fun Route.idDocumentation() { install(NotarizedRoute()) { parameters = listOf( diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt new file mode 100644 index 000000000..6fce2099a --- /dev/null +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt @@ -0,0 +1,116 @@ +package io.bkbn.kompendium.playground + +import io.bkbn.kompendium.core.attribute.KompendiumAttributes +import io.bkbn.kompendium.core.metadata.GetInfo +import io.bkbn.kompendium.core.plugin.NotarizedApplication +import io.bkbn.kompendium.core.plugin.NotarizedRoute +import io.bkbn.kompendium.core.routes.redoc +import io.bkbn.kompendium.json.schema.definition.TypeDefinition +import io.bkbn.kompendium.oas.component.Components +import io.bkbn.kompendium.oas.payload.Parameter +import io.bkbn.kompendium.oas.security.BasicAuth +import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule +import io.bkbn.kompendium.playground.util.ExampleResponse +import io.bkbn.kompendium.playground.util.Util.baseSpec +import io.ktor.http.HttpStatusCode +import io.ktor.serialization.kotlinx.json.json +import io.ktor.server.application.Application +import io.ktor.server.application.call +import io.ktor.server.application.install +import io.ktor.server.auth.Authentication +import io.ktor.server.auth.UserIdPrincipal +import io.ktor.server.auth.authenticate +import io.ktor.server.auth.basic +import io.ktor.server.engine.embeddedServer +import io.ktor.server.netty.Netty +import io.ktor.server.plugins.contentnegotiation.ContentNegotiation +import io.ktor.server.response.respond +import io.ktor.server.routing.Route +import io.ktor.server.routing.application +import io.ktor.server.routing.get +import io.ktor.server.routing.route +import io.ktor.server.routing.routing +import kotlinx.serialization.json.Json + +fun main() { + embeddedServer( + Netty, + port = 8081, + module = Application::mainModule + ).start(wait = true) +} + +private fun Application.mainModule() { + install(ContentNegotiation) { + json(Json { + serializersModule = KompendiumSerializersModule.module + encodeDefaults = true + explicitNulls = false + }) + } + install(Authentication) { + basic("basic") { + realm = "Ktor Server" + validate { credentials -> + if (credentials.name == credentials.password) { + UserIdPrincipal(credentials.name) + } else { + null + } + } + } + } + install(NotarizedApplication()) { + spec = baseSpec.copy( + components = Components( + securitySchemes = mutableMapOf( + "basic" to BasicAuth() + ) + ) + ) + openApiJson = { + authenticate("basic") { + route("/openapi.json") { + get { + call.respond(HttpStatusCode.OK, this@route.application.attributes[KompendiumAttributes.openApiSpec]) + } + } + } + } + } + routing { + authenticate("basic") { + redoc(pageTitle = "Simple API Docs") + route("/{id}") { + idDocumentation() + get { + call.respond(HttpStatusCode.OK, ExampleResponse(true)) + } + } + } + } +} + +private fun Route.idDocumentation() { + install(NotarizedRoute()) { + parameters = listOf( + Parameter( + name = "id", + `in` = Parameter.Location.path, + schema = TypeDefinition.STRING + ) + ) + get = GetInfo.builder { + summary("Get user by id") + description("A very neat endpoint!") + security = mapOf( + "basic" to emptyList() + ) + response { + responseCode(HttpStatusCode.OK) + responseType() + description("Will return whether or not the user is real 😱") + } + } + } +}