diff --git a/build.gradle.kts b/build.gradle.kts index f257fe6..c1ee5ef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "io.github.smiley4" -version = "1.2.0" +version = "1.3.0" repositories { mavenCentral() diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt index 9781cac..90f6f5b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt @@ -17,7 +17,6 @@ import io.ktor.http.HttpMethod import io.ktor.http.HttpStatusCode import io.ktor.server.routing.RouteSelector import kotlin.reflect.KClass -import kotlin.reflect.KType /** * Main-Configuration of the "SwaggerUI"-Plugin @@ -45,6 +44,12 @@ class SwaggerUIPluginConfig { var defaultSecuritySchemeName: String? = null + /** + * The names of the security schemes available for use for the protected paths + */ + var defaultSecuritySchemeNames: Collection? = null + + /** * function to generate a tag from the given url for a path. Result will be added to the tags defined for each path */ diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt index a0c5d3c..30f75d1 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt @@ -27,17 +27,26 @@ class OpenApiRoute { */ var operationId: String? = null + /** * Whether this route is deprecated */ var deprecated: Boolean = false + /** * A declaration of which security mechanism can be used for this operation. - * If not specified, defaultSecuritySchemeName (global plugin config) will be used + * If not specified (and none specified with [securitySchemeNames]), defaultSecuritySchemeName (global plugin config) will be used */ var securitySchemeName: String? = null + + /** + * A declaration of which security mechanisms can be used for this operation (i.e. any of the specified ones). + * If none specified (and none with [securitySchemeName]), defaultSecuritySchemeName (global plugin config) will be used. + */ + var securitySchemeNames: Collection? = null + private val request = OpenApiRequest() @@ -48,10 +57,8 @@ class OpenApiRoute { request.apply(block) } - fun getRequest() = request - private val responses = OpenApiResponses() @@ -62,7 +69,6 @@ class OpenApiRoute { responses.apply(block) } - fun getResponses() = responses } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt index 58e3a58..56a6047 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt @@ -18,7 +18,6 @@ class OApiPathBuilder { private val requestBodyBuilder = OApiRequestBodyBuilder() private val responsesBuilder = OApiResponsesBuilder() - fun build(route: RouteMeta, components: ComponentsContext, config: SwaggerUIPluginConfig): Pair { return route.path to PathItem().apply { val operation = Operation().apply { @@ -42,12 +41,20 @@ class OApiPathBuilder { } } if (route.protected) { - (route.documentation.securitySchemeName ?: config.defaultSecuritySchemeName)?.let { schemeName -> - security = mutableListOf( + val securitySchemes = mutableSetOf().also { schemes -> + route.documentation.securitySchemeName?.also { schemes.add(it) } + route.documentation.securitySchemeNames?.also { schemes.addAll(it) } + } + if (securitySchemes.isEmpty()) { + config.defaultSecuritySchemeName?.also { securitySchemes.add(it) } + config.defaultSecuritySchemeNames?.also { securitySchemes.addAll(it) } + } + if (securitySchemes.isNotEmpty()) { + security = securitySchemes.map { SecurityRequirement().apply { - addList(schemeName, emptyList()) + addList(it, emptyList()) } - ) + } } } } @@ -63,7 +70,6 @@ class OApiPathBuilder { } } - private fun buildTags(route: RouteMeta, tagGenerator: ((url: List) -> String?)?): List { val generatedTags = tagGenerator?.let { it(route.path.split("/").filter { it.isNotEmpty() }) @@ -71,7 +77,6 @@ class OApiPathBuilder { return (route.documentation.tags + generatedTags).filterNotNull() } - private fun shouldAddUnauthorized(config: RouteMeta, defaultUnauthorizedResponses: OpenApiResponse?): Boolean { val unauthorizedCode = HttpStatusCode.Unauthorized.value.toString(); return defaultUnauthorizedResponses != null diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/RouteCollector.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/RouteCollector.kt index 768bd7c..3131dbf 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/RouteCollector.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/RouteCollector.kt @@ -102,6 +102,10 @@ class RouteCollector { description = a.description ?: b.description operationId = a.operationId ?: b.operationId securitySchemeName = a.securitySchemeName ?: b.securitySchemeName + securitySchemeNames = mutableSetOf().also { merged -> + a.securitySchemeNames?.let { merged.addAll(it) } + b.securitySchemeNames?.let { merged.addAll(it) } + } deprecated = a.deprecated || b.deprecated request { (getParameters() as MutableList).also { diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt index a725b91..5ffe7bf 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt @@ -55,6 +55,11 @@ private fun Application.myModule() { type = AuthType.HTTP scheme = AuthScheme.BASIC } + // specify another security scheme + securityScheme("MyOtherSecurityScheme") { + type = AuthType.HTTP + scheme = AuthScheme.BASIC + } } // configure routes @@ -62,8 +67,8 @@ private fun Application.myModule() { authenticate { // route is in an "authenticate"-block -> default security scheme will be used (see plugin-config "defaultSecuritySchemeName") get("hello", { - // Set the security scheme to be used by this route (here redundant, since already specified by 'defaultSecuritySchemeName') - securitySchemeName = "MySecurityScheme" + // Set the security schemes to be used by this route + securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme") description = "Protected 'Hello World'-Route" response { HttpStatusCode.OK to {