init security scheme (#29)

This commit is contained in:
dpnolte
2021-04-21 19:51:42 +02:00
committed by GitHub
parent 8a64925c9d
commit d0767aa74e
21 changed files with 835 additions and 9 deletions

View File

@ -1,5 +1,11 @@
# Changelog # Changelog
## [0.6.0] - April 21st, 2021
### Added
- Added basic and jwt security scheme support with the new module kompendium-auth
## [0.5.2] - April 19th, 2021 ## [0.5.2] - April 19th, 2021
### Removed ### Removed

View File

@ -41,7 +41,6 @@ dependencies {
Kompendium is still under active development ⚠️ There are a number of yet-to-be-implemented features, including Kompendium is still under active development ⚠️ There are a number of yet-to-be-implemented features, including
- Multiple Responses 📜 - Multiple Responses 📜
- Security Schemas 🔏
- Sealed Class / Polymorphic Support 😬 - Sealed Class / Polymorphic Support 😬
- Validation / Enforcement (❓👀❓) - Validation / Enforcement (❓👀❓)
@ -135,6 +134,55 @@ When run in the playground, this would output the following at `/openapi.json`
https://gist.github.com/rgbrizzlehizzle/b9544922f2e99a2815177f8bdbf80668 https://gist.github.com/rgbrizzlehizzle/b9544922f2e99a2815177f8bdbf80668
### Kompendium Auth and security schemes
There is a seperate library to handle security schemes: `kompendium-auth`.
This needs to be added to your project as dependency.
At the moment, the basic and jwt authentication is only supported.
A minimal example would be:
```kotlin
install(Authentication) {
notarizedBasic("basic") {
realm = "Ktor realm 1"
// ...
}
notarizedJwt("jwt") {
realm = "Ktor realm 2"
// ...
}
}
routing {
authenticate("basic") {
route("/basic_auth") {
notarizedGet<TestParams, TestResponse>(
MethodInfo(
// securitySchemes needs to be set
"Another get test", "testing more", testGetResponse, securitySchemes = setOf("basic")
)
) {
call.respondText { "basic auth" }
}
}
}
authenticate("jwt") {
route("/jwt") {
notarizedGet<TestParams, TestResponse>(
MethodInfo(
// securitySchemes needs to be set
"Another get test", "testing more", testGetResponse, securitySchemes = setOf("jwt")
)
) {
call.respondText { "jwt" }
}
}
}
}
```
## Limitations ## Limitations
### Kompendium as a singleton ### Kompendium as a singleton

View File

@ -1,5 +1,5 @@
# Kompendium # Kompendium
project.version=0.5.2 project.version=0.6.0
# Kotlin # Kotlin
kotlin.code.style=official kotlin.code.style=official
# Gradle # Gradle

View File

@ -10,6 +10,9 @@ ktor-server-core = { group = "io.ktor", name = "ktor-server-core", version.ref =
ktor-server-netty = { group = "io.ktor", name = "ktor-server-netty", version.ref = "ktor" } ktor-server-netty = { group = "io.ktor", name = "ktor-server-netty", version.ref = "ktor" }
ktor-jackson = { group = "io.ktor", name = "ktor-jackson", version.ref = "ktor" } ktor-jackson = { group = "io.ktor", name = "ktor-jackson", version.ref = "ktor" }
ktor-html-builder = { group = "io.ktor", name = "ktor-html-builder", version.ref = "ktor" } ktor-html-builder = { group = "io.ktor", name = "ktor-html-builder", version.ref = "ktor" }
ktor-auth-lib = { group = "io.ktor", name = "ktor-auth", version.ref = "ktor" }
ktor-auth-jwt = { group = "io.ktor", name = "ktor-auth-jwt", version.ref = "ktor" }
# Logging # Logging
slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" } logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
@ -17,4 +20,5 @@ logback-core = { group = "ch.qos.logback", name = "logback-core", version.ref =
[bundles] [bundles]
ktor = [ "ktor-server-core", "ktor-server-netty", "ktor-jackson", "ktor-html-builder" ] ktor = [ "ktor-server-core", "ktor-server-netty", "ktor-jackson", "ktor-html-builder" ]
ktorAuth = [ "ktor-auth-lib", "ktor-auth-jwt" ]
logging = [ "slf4j", "logback-classic", "logback-core" ] logging = [ "slf4j", "logback-classic", "logback-core" ]

View File

@ -0,0 +1,40 @@
plugins {
`java-library`
`maven-publish`
}
dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation(libs.bundles.ktor)
implementation(libs.bundles.ktorAuth)
implementation(projects.kompendiumCore)
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.0")
testImplementation("io.ktor:ktor-server-test-host:1.5.3")
}
java {
withSourcesJar()
}
publishing {
repositories {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/lg-backbone/kompendium")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
publications {
create<MavenPublication>("kompendium") {
from(components["kotlin"])
artifact(tasks.sourcesJar)
}
}
}

View File

@ -0,0 +1,19 @@
package org.leafygreens.kompendium.auth
import io.ktor.auth.AuthenticationRouteSelector
import io.ktor.routing.Route
import org.leafygreens.kompendium.path.CorePathCalculator
import org.slf4j.LoggerFactory
class AuthPathCalculator : CorePathCalculator() {
private val logger = LoggerFactory.getLogger(javaClass)
override fun handleCustomSelectors(route: Route, tail: String): String = when (route.selector) {
is AuthenticationRouteSelector -> {
logger.debug("Found authentication route selector ${route.selector}")
super.calculate(route.parent, tail)
}
else -> super.handleCustomSelectors(route, tail)
}
}

View File

@ -0,0 +1,50 @@
package org.leafygreens.kompendium.auth
import io.ktor.auth.Authentication
import io.ktor.auth.basic
import io.ktor.auth.BasicAuthenticationProvider
import io.ktor.auth.jwt.jwt
import io.ktor.auth.jwt.JWTAuthenticationProvider
import org.leafygreens.kompendium.Kompendium
import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaSecurity
object KompendiumAuth {
init {
Kompendium.pathCalculator = AuthPathCalculator()
}
fun Authentication.Configuration.notarizedBasic(
name: String? = null,
configure: BasicAuthenticationProvider.Configuration.() -> Unit
) {
Kompendium.openApiSpec.components.securitySchemes[name ?: "default"] = OpenApiSpecSchemaSecurity(
type = "http",
scheme = "basic"
)
basic(name, configure)
}
fun Authentication.Configuration.notarizedJwt(
name: String? = null,
header: String? = null,
scheme: String? = null,
configure: JWTAuthenticationProvider.Configuration.() -> Unit
) {
if (header == null || header == "Authorization") {
Kompendium.openApiSpec.components.securitySchemes[name ?: "default"] = OpenApiSpecSchemaSecurity(
type = "http",
scheme = scheme ?: "bearer"
)
} else {
Kompendium.openApiSpec.components.securitySchemes[name ?: "default"] = OpenApiSpecSchemaSecurity(
type = "apiKey",
name = header,
`in` = "header"
)
}
jwt(name, configure)
}
// TODO support other authentication providers (e.g., oAuth)?
}

View File

@ -0,0 +1,197 @@
package org.leafygreens.kompendium.auth
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.jackson.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.testing.*
import org.junit.Test
import org.leafygreens.kompendium.Kompendium
import org.leafygreens.kompendium.Kompendium.notarizedGet
import org.leafygreens.kompendium.auth.KompendiumAuth.notarizedBasic
import org.leafygreens.kompendium.auth.KompendiumAuth.notarizedJwt
import org.leafygreens.kompendium.auth.util.TestData
import org.leafygreens.kompendium.auth.util.TestParams
import org.leafygreens.kompendium.auth.util.TestResponse
import org.leafygreens.kompendium.models.meta.MethodInfo
import org.leafygreens.kompendium.models.meta.ResponseInfo
import org.leafygreens.kompendium.models.oas.OpenApiSpec
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo
import org.leafygreens.kompendium.routes.openApi
import org.leafygreens.kompendium.routes.redoc
import org.leafygreens.kompendium.util.KompendiumHttpCodes
import kotlin.test.AfterTest
import kotlin.test.assertEquals
internal class KompendiumAuthTest {
@AfterTest
fun `reset kompendium`() {
Kompendium.openApiSpec = OpenApiSpec(
info = OpenApiSpecInfo(),
servers = mutableListOf(),
paths = mutableMapOf()
)
Kompendium.cache = emptyMap()
}
@Test
fun `Notarized Get with basic authentication records all expected information`() {
withTestApplication({
configModule()
configBasicAuth()
docs()
notarizedAuthenticatedGetModule(TestData.AuthConfigName.Basic)
}) {
// do
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
// expect
val expected = TestData.getFileSnapshot("notarized_basic_authenticated_get.json").trim()
assertEquals(expected, json, "The received json spec should match the expected content")
}
}
@Test
fun `Notarized Get with jwt authentication records all expected information`() {
withTestApplication({
configModule()
configJwtAuth()
docs()
notarizedAuthenticatedGetModule(TestData.AuthConfigName.JWT)
}) {
// do
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
// expect
val expected = TestData.getFileSnapshot("notarized_jwt_authenticated_get.json").trim()
assertEquals(expected, json, "The received json spec should match the expected content")
}
}
@Test
fun `Notarized Get with jwt authentication and custom scheme records all expected information`() {
withTestApplication({
configModule()
configJwtAuth(scheme = "oauth")
docs()
notarizedAuthenticatedGetModule(TestData.AuthConfigName.JWT)
}) {
// do
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
// expect
val expected = TestData.getFileSnapshot("notarized_jwt_custom_scheme_authenticated_get.json").trim()
assertEquals(expected, json, "The received json spec should match the expected content")
}
}
@Test
fun `Notarized Get with jwt authentication and custom header records all expected information`() {
withTestApplication({
configModule()
configJwtAuth(header = "x-api-key")
docs()
notarizedAuthenticatedGetModule(TestData.AuthConfigName.JWT)
}) {
// do
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
// expect
val expected = TestData.getFileSnapshot("notarized_jwt_custom_header_authenticated_get.json").trim()
assertEquals(expected, json, "The received json spec should match the expected content")
}
}
@Test
fun `Notarized Get with multiple jwt schemes records all expected information`() {
withTestApplication({
configModule()
install(Authentication) {
notarizedJwt("jwt1", header = "x-api-key-1") {
realm = "Ktor server"
}
notarizedJwt("jwt2", header = "x-api-key-2") {
realm = "Ktor server"
}
}
docs()
notarizedAuthenticatedGetModule("jwt1", "jwt2")
}) {
// do
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
// expect
val expected = TestData.getFileSnapshot("notarized_multiple_jwt_authenticated_get.json").trim()
assertEquals(expected, json, "The received json spec should match the expected content")
}
}
private fun Application.configModule() {
install(ContentNegotiation) {
jackson(ContentType.Application.Json) {
enable(SerializationFeature.INDENT_OUTPUT)
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
}
}
private fun Application.configBasicAuth() {
install(Authentication) {
notarizedBasic(TestData.AuthConfigName.Basic) {
realm = "Ktor Server"
validate { credentials ->
if (credentials.name == credentials.password) {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
}
private fun Application.configJwtAuth(
header: String? = null,
scheme: String? = null
) {
install(Authentication) {
notarizedJwt(TestData.AuthConfigName.JWT, header, scheme) {
realm = "Ktor server"
}
}
}
private fun Application.notarizedAuthenticatedGetModule(vararg authenticationConfigName: String) {
routing {
authenticate(*authenticationConfigName) {
route(TestData.getRoutePath) {
notarizedGet<TestParams, TestResponse>(testGetInfo(*authenticationConfigName)) {
call.respondText { "hey dude ‼️ congratz on the get request" }
}
}
}
}
}
private val oas = Kompendium.openApiSpec.copy()
private fun Application.docs() {
routing {
openApi(oas)
redoc(oas)
}
}
private companion object {
val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor")
fun testGetInfo(vararg security: String) =
MethodInfo("Another get test", "testing more", testGetResponse, securitySchemes = security.toSet())
}
}

View File

@ -0,0 +1,18 @@
package org.leafygreens.kompendium.auth.util
import java.io.File
object TestData {
object AuthConfigName {
val Basic = "basic"
val JWT = "jwt"
}
val getRoutePath = "/test"
fun getFileSnapshot(fileName: String): String {
val snapshotPath = "src/test/resources"
val file = File("$snapshotPath/$fileName")
return file.readText()
}
}

View File

@ -0,0 +1,19 @@
package org.leafygreens.kompendium.auth.util
import org.leafygreens.kompendium.annotations.KompendiumField
import org.leafygreens.kompendium.annotations.PathParam
import org.leafygreens.kompendium.annotations.QueryParam
data class TestParams(
@PathParam val a: String,
@QueryParam val aa: Int
)
data class TestRequest(
@KompendiumField(name = "field_name")
val b: Double,
val aaa: List<Long>
)
data class TestResponse(val c: String)

View File

@ -0,0 +1,74 @@
{
"openapi" : "3.0.3",
"info" : { },
"servers" : [ ],
"paths" : {
"/test" : {
"get" : {
"tags" : [ ],
"summary" : "Another get test",
"description" : "testing more",
"parameters" : [ {
"name" : "a",
"in" : "path",
"schema" : {
"$ref" : "#/components/schemas/String"
},
"required" : true,
"deprecated" : false
}, {
"name" : "aa",
"in" : "query",
"schema" : {
"$ref" : "#/components/schemas/Int"
},
"required" : true,
"deprecated" : false
} ],
"responses" : {
"200" : {
"description" : "A Successful Endeavor",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TestResponse"
}
}
}
}
},
"deprecated" : false,
"security" : [ {
"basic" : [ ]
} ]
}
}
},
"components" : {
"schemas" : {
"String" : {
"type" : "string"
},
"TestResponse" : {
"properties" : {
"c" : {
"$ref" : "#/components/schemas/String"
}
},
"type" : "object"
},
"Int" : {
"format" : "int32",
"type" : "integer"
}
},
"securitySchemes" : {
"basic" : {
"type" : "http",
"scheme" : "basic"
}
}
},
"security" : [ ],
"tags" : [ ]
}

View File

@ -0,0 +1,74 @@
{
"openapi" : "3.0.3",
"info" : { },
"servers" : [ ],
"paths" : {
"/test" : {
"get" : {
"tags" : [ ],
"summary" : "Another get test",
"description" : "testing more",
"parameters" : [ {
"name" : "a",
"in" : "path",
"schema" : {
"$ref" : "#/components/schemas/String"
},
"required" : true,
"deprecated" : false
}, {
"name" : "aa",
"in" : "query",
"schema" : {
"$ref" : "#/components/schemas/Int"
},
"required" : true,
"deprecated" : false
} ],
"responses" : {
"200" : {
"description" : "A Successful Endeavor",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TestResponse"
}
}
}
}
},
"deprecated" : false,
"security" : [ {
"jwt" : [ ]
} ]
}
}
},
"components" : {
"schemas" : {
"String" : {
"type" : "string"
},
"TestResponse" : {
"properties" : {
"c" : {
"$ref" : "#/components/schemas/String"
}
},
"type" : "object"
},
"Int" : {
"format" : "int32",
"type" : "integer"
}
},
"securitySchemes" : {
"jwt" : {
"type" : "http",
"scheme" : "bearer"
}
}
},
"security" : [ ],
"tags" : [ ]
}

View File

@ -0,0 +1,75 @@
{
"openapi" : "3.0.3",
"info" : { },
"servers" : [ ],
"paths" : {
"/test" : {
"get" : {
"tags" : [ ],
"summary" : "Another get test",
"description" : "testing more",
"parameters" : [ {
"name" : "a",
"in" : "path",
"schema" : {
"$ref" : "#/components/schemas/String"
},
"required" : true,
"deprecated" : false
}, {
"name" : "aa",
"in" : "query",
"schema" : {
"$ref" : "#/components/schemas/Int"
},
"required" : true,
"deprecated" : false
} ],
"responses" : {
"200" : {
"description" : "A Successful Endeavor",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TestResponse"
}
}
}
}
},
"deprecated" : false,
"security" : [ {
"jwt" : [ ]
} ]
}
}
},
"components" : {
"schemas" : {
"String" : {
"type" : "string"
},
"TestResponse" : {
"properties" : {
"c" : {
"$ref" : "#/components/schemas/String"
}
},
"type" : "object"
},
"Int" : {
"format" : "int32",
"type" : "integer"
}
},
"securitySchemes" : {
"jwt" : {
"type" : "apiKey",
"name" : "x-api-key",
"in" : "header"
}
}
},
"security" : [ ],
"tags" : [ ]
}

View File

@ -0,0 +1,74 @@
{
"openapi" : "3.0.3",
"info" : { },
"servers" : [ ],
"paths" : {
"/test" : {
"get" : {
"tags" : [ ],
"summary" : "Another get test",
"description" : "testing more",
"parameters" : [ {
"name" : "a",
"in" : "path",
"schema" : {
"$ref" : "#/components/schemas/String"
},
"required" : true,
"deprecated" : false
}, {
"name" : "aa",
"in" : "query",
"schema" : {
"$ref" : "#/components/schemas/Int"
},
"required" : true,
"deprecated" : false
} ],
"responses" : {
"200" : {
"description" : "A Successful Endeavor",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TestResponse"
}
}
}
}
},
"deprecated" : false,
"security" : [ {
"jwt" : [ ]
} ]
}
}
},
"components" : {
"schemas" : {
"String" : {
"type" : "string"
},
"TestResponse" : {
"properties" : {
"c" : {
"$ref" : "#/components/schemas/String"
}
},
"type" : "object"
},
"Int" : {
"format" : "int32",
"type" : "integer"
}
},
"securitySchemes" : {
"jwt" : {
"type" : "http",
"scheme" : "oauth"
}
}
},
"security" : [ ],
"tags" : [ ]
}

View File

@ -0,0 +1,81 @@
{
"openapi" : "3.0.3",
"info" : { },
"servers" : [ ],
"paths" : {
"/test" : {
"get" : {
"tags" : [ ],
"summary" : "Another get test",
"description" : "testing more",
"parameters" : [ {
"name" : "a",
"in" : "path",
"schema" : {
"$ref" : "#/components/schemas/String"
},
"required" : true,
"deprecated" : false
}, {
"name" : "aa",
"in" : "query",
"schema" : {
"$ref" : "#/components/schemas/Int"
},
"required" : true,
"deprecated" : false
} ],
"responses" : {
"200" : {
"description" : "A Successful Endeavor",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TestResponse"
}
}
}
}
},
"deprecated" : false,
"security" : [ {
"jwt1" : [ ],
"jwt2" : [ ]
} ]
}
}
},
"components" : {
"schemas" : {
"String" : {
"type" : "string"
},
"TestResponse" : {
"properties" : {
"c" : {
"$ref" : "#/components/schemas/String"
}
},
"type" : "object"
},
"Int" : {
"format" : "int32",
"type" : "integer"
}
},
"securitySchemes" : {
"jwt1" : {
"type" : "apiKey",
"name" : "x-api-key-1",
"in" : "header"
},
"jwt2" : {
"type" : "apiKey",
"name" : "x-api-key-2",
"in" : "header"
}
}
},
"security" : [ ],
"tags" : [ ]
}

View File

@ -101,7 +101,11 @@ object Kompendium {
deprecated = this.deprecated, deprecated = this.deprecated,
parameters = paramType.toParameterSpec(), parameters = paramType.toParameterSpec(),
responses = responseType.toResponseSpec(responseInfo)?.let { mapOf(it) }, responses = responseType.toResponseSpec(responseInfo)?.let { mapOf(it) },
requestBody = if (method != HttpMethod.Get) requestType.toRequestSpec(requestInfo) else null requestBody = if (method != HttpMethod.Get) requestType.toRequestSpec(requestInfo) else null,
security = if (this.securitySchemes.isNotEmpty()) listOf(
// TODO support scopes
this.securitySchemes.associateWith { listOf() }
) else null
) )
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)

View File

@ -7,5 +7,6 @@ data class MethodInfo(
val responseInfo: ResponseInfo? = null, val responseInfo: ResponseInfo? = null,
val requestInfo: RequestInfo? = null, val requestInfo: RequestInfo? = null,
val tags: Set<String> = emptySet(), val tags: Set<String> = emptySet(),
val deprecated: Boolean = false val deprecated: Boolean = false,
val securitySchemes: Set<String> = emptySet()
) )

View File

@ -3,5 +3,5 @@ package org.leafygreens.kompendium.models.oas
// TODO I *think* the only thing I need here is the security https://swagger.io/specification/#components-object // TODO I *think* the only thing I need here is the security https://swagger.io/specification/#components-object
data class OpenApiSpecComponents( data class OpenApiSpecComponents(
val schemas: MutableMap<String, OpenApiSpecComponentSchema> = mutableMapOf(), val schemas: MutableMap<String, OpenApiSpecComponentSchema> = mutableMapOf(),
val securitySchemes: MutableMap<String, OpenApiSpecSchema> = mutableMapOf() val securitySchemes: MutableMap<String, OpenApiSpecSchemaSecurity> = mutableMapOf()
) )

View File

@ -7,8 +7,10 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation(projects.kompendiumCore) implementation(projects.kompendiumCore)
implementation(projects.kompendiumAuth)
implementation(libs.bundles.ktor) implementation(libs.bundles.ktor)
implementation(libs.bundles.ktorAuth)
implementation(libs.bundles.logging) implementation(libs.bundles.logging)
testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test")

View File

@ -5,6 +5,9 @@ import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.application.Application import io.ktor.application.Application
import io.ktor.application.call import io.ktor.application.call
import io.ktor.application.install import io.ktor.application.install
import io.ktor.auth.Authentication
import io.ktor.auth.authenticate
import io.ktor.auth.UserIdPrincipal
import io.ktor.features.ContentNegotiation import io.ktor.features.ContentNegotiation
import io.ktor.jackson.jackson import io.ktor.jackson.jackson
import io.ktor.response.respondText import io.ktor.response.respondText
@ -18,6 +21,7 @@ import org.leafygreens.kompendium.Kompendium.notarizedDelete
import org.leafygreens.kompendium.Kompendium.notarizedGet import org.leafygreens.kompendium.Kompendium.notarizedGet
import org.leafygreens.kompendium.Kompendium.notarizedPost import org.leafygreens.kompendium.Kompendium.notarizedPost
import org.leafygreens.kompendium.Kompendium.notarizedPut import org.leafygreens.kompendium.Kompendium.notarizedPut
import org.leafygreens.kompendium.auth.KompendiumAuth.notarizedBasic
import org.leafygreens.kompendium.annotations.KompendiumField import org.leafygreens.kompendium.annotations.KompendiumField
import org.leafygreens.kompendium.annotations.PathParam import org.leafygreens.kompendium.annotations.PathParam
import org.leafygreens.kompendium.annotations.QueryParam import org.leafygreens.kompendium.annotations.QueryParam
@ -28,6 +32,7 @@ import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoContact import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoContact
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoLicense import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoLicense
import org.leafygreens.kompendium.models.oas.OpenApiSpecServer import org.leafygreens.kompendium.models.oas.OpenApiSpecServer
import org.leafygreens.kompendium.playground.KompendiumTOC.testAuthenticatedSingleGetInfo
import org.leafygreens.kompendium.playground.KompendiumTOC.testIdGetInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testIdGetInfo
import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleDeleteInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleDeleteInfo
import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleGetInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleGetInfo
@ -73,12 +78,29 @@ fun main() {
).start(wait = true) ).start(wait = true)
} }
var featuresInstalled = false
fun Application.mainModule() { fun Application.mainModule() {
install(ContentNegotiation) { // only install once in case of auto reload
jackson { if (!featuresInstalled) {
enable(SerializationFeature.INDENT_OUTPUT) install(ContentNegotiation) {
setSerializationInclusion(JsonInclude.Include.NON_NULL) jackson {
enable(SerializationFeature.INDENT_OUTPUT)
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
} }
install(Authentication) {
notarizedBasic("basic") {
realm = "Ktor Server"
validate { credentials ->
if (credentials.name == credentials.password) {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
featuresInstalled = true
} }
routing { routing {
openApi(oas) openApi(oas)
@ -103,6 +125,13 @@ fun Application.mainModule() {
call.respondText { "heya" } call.respondText { "heya" }
} }
} }
authenticate("basic") {
route("/authenticated/single") {
notarizedGet<Unit, Unit>(testAuthenticatedSingleGetInfo) {
call.respondText("get authentiticated single")
}
}
}
} }
} }
} }
@ -180,4 +209,14 @@ object KompendiumTOC {
mediaTypes = emptyList() mediaTypes = emptyList()
) )
) )
val testAuthenticatedSingleGetInfo = MethodInfo(
summary = "Another get test",
description = "testing more",
tags = setOf("anotherTest", "sample"),
responseInfo = ResponseInfo(
status = KompendiumHttpCodes.OK,
description = "Returns a different sample"
),
securitySchemes = setOf("basic")
)
} }

View File

@ -1,5 +1,6 @@
rootProject.name = "kompendium" rootProject.name = "kompendium"
include("kompendium-core") include("kompendium-core")
include("kompendium-auth")
include("kompendium-playground") include("kompendium-playground")
// Feature Previews // Feature Previews