Skip to content

Commit dbafdb7

Browse files
committed
Merge branch 'develop' into release
2 parents 150b9f8 + 0c65cb2 commit dbafdb7

File tree

7 files changed

+108
-61
lines changed

7 files changed

+108
-61
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ plugins {
88
}
99

1010
group = "io.github.smiley4"
11-
version = "2.0.0"
11+
version = "2.1.0"
1212

1313
repositories {
1414
mavenCentral()
@@ -17,7 +17,7 @@ repositories {
1717

1818
dependencies {
1919

20-
val ktorVersion = "2.3.0"
20+
val ktorVersion = "2.3.1"
2121
implementation("io.ktor:ktor-server-core-jvm:$ktorVersion")
2222
implementation("io.ktor:ktor-server-webjars:$ktorVersion")
2323
implementation("io.ktor:ktor-server-auth:$ktorVersion")

gradlew

100644100755
File mode changed.

src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,25 @@ class OpenApiRoute {
4242

4343
/**
4444
* A declaration of which security mechanism can be used for this operation.
45-
* If not specified (and none specified with [securitySchemeNames]), defaultSecuritySchemeName (global plugin config) will be used
45+
* If not specified (and none specified with [securitySchemeNames]), defaultSecuritySchemeName (global plugin config) will be used.
46+
* Only applied to [protected] operations.
4647
*/
4748
var securitySchemeName: String? = null
4849

4950

5051
/**
5152
* A declaration of which security mechanisms can be used for this operation (i.e. any of the specified ones).
5253
* If none specified (and none with [securitySchemeName]), defaultSecuritySchemeName (global plugin config) will be used.
54+
* Only applied to [protected] operations.
5355
*/
5456
var securitySchemeNames: Collection<String>? = null
5557

58+
/**
59+
* Specifies whether this operation is protected.
60+
* If not specified, the authentication state of the Ktor route will be used (i.e. whether it is surrounded by an [authenticate][io.ktor.server.auth.authenticate] block or not).
61+
*/
62+
var protected: Boolean? = null
63+
5664
private val request = OpenApiRequest()
5765

5866

src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ class RouteCollector(
2323
return allRoutes(routeProvider())
2424
.asSequence()
2525
.map { route ->
26+
val documentation = getDocumentation(route, OpenApiRoute())
2627
RouteMeta(
2728
method = getMethod(route),
2829
path = getPath(route, config),
29-
documentation = getDocumentation(route, OpenApiRoute()),
30-
protected = isProtected(route)
30+
documentation = documentation,
31+
protected = documentation.protected ?: isProtected(route)
3132
)
3233
}
3334
.filter { removeLeadingSlash(it.path) != removeLeadingSlash(config.getSwaggerUI().swaggerUrl) }
@@ -52,7 +53,8 @@ class RouteCollector(
5253
private fun getDocumentation(route: Route, base: OpenApiRoute): OpenApiRoute {
5354
var documentation = base
5455
if (route.selector is DocumentedRouteSelector) {
55-
documentation = routeDocumentationMerger.merge(documentation, (route.selector as DocumentedRouteSelector).documentation)
56+
documentation =
57+
routeDocumentationMerger.merge(documentation, (route.selector as DocumentedRouteSelector).documentation)
5658
}
5759
return if (route.parent != null) {
5860
getDocumentation(route.parent!!, documentation)

src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class RouteDocumentationMerger {
2020
}
2121
deprecated = a.deprecated || b.deprecated
2222
hidden = a.hidden || b.hidden
23+
protected = a.protected ?: b.protected
2324
request {
2425
(getParameters() as MutableList).also {
2526
it.addAll(a.getRequest().getParameters())
@@ -34,4 +35,4 @@ class RouteDocumentationMerger {
3435
}
3536
}
3637

37-
}
38+
}

src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt

Lines changed: 85 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import io.ktor.server.response.respondText
1818
import io.ktor.server.routing.routing
1919

2020
fun main() {
21-
embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true)
21+
embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true)
2222
}
2323

2424
/**
@@ -28,58 +28,90 @@ fun main() {
2828
*/
2929
private fun Application.myModule() {
3030

31-
// Install "Authentication"-Plugin and setup Basic-Auth
32-
install(Authentication) {
33-
basic {
34-
realm = "Access to the API"
35-
validate { credentials ->
36-
if (credentials.name == "user" && credentials.password == "pass") {
37-
UserIdPrincipal(credentials.name)
38-
} else {
39-
null
40-
}
41-
}
42-
}
43-
}
31+
// Install "Authentication"-Plugin and setup Basic-Auth
32+
install(Authentication) {
33+
basic {
34+
realm = "Access to the API"
35+
validate { credentials ->
36+
if (credentials.name == "user" && credentials.password == "pass") {
37+
UserIdPrincipal(credentials.name)
38+
} else {
39+
null
40+
}
41+
}
42+
}
43+
}
4444

45-
// Install "Swagger-UI"-Plugin
46-
install(SwaggerUI) {
47-
// default value for "401 Unauthorized"-responses.
48-
// the name of the security scheme (see below) to use for each route when nothing else is specified
49-
defaultSecuritySchemeName = "MySecurityScheme"
50-
defaultUnauthorizedResponse {
51-
description = "Username or password is invalid."
52-
}
53-
// specify a security scheme
54-
securityScheme("MySecurityScheme") {
55-
type = AuthType.HTTP
56-
scheme = AuthScheme.BASIC
57-
}
58-
// specify another security scheme
59-
securityScheme("MyOtherSecurityScheme") {
60-
type = AuthType.HTTP
61-
scheme = AuthScheme.BASIC
62-
}
63-
}
45+
// Install "Swagger-UI"-Plugin
46+
install(SwaggerUI) {
47+
// default value for "401 Unauthorized"-responses.
48+
// the name of the security scheme (see below) to use for each route when nothing else is specified
49+
defaultSecuritySchemeName = "MySecurityScheme"
50+
defaultUnauthorizedResponse {
51+
description = "Username or password is invalid."
52+
}
53+
// specify a security scheme
54+
securityScheme("MySecurityScheme") {
55+
type = AuthType.HTTP
56+
scheme = AuthScheme.BASIC
57+
}
58+
// specify another security scheme
59+
securityScheme("MyOtherSecurityScheme") {
60+
type = AuthType.HTTP
61+
scheme = AuthScheme.BASIC
62+
}
63+
}
6464

65-
// configure routes
66-
routing {
67-
authenticate {
68-
// route is in an "authenticate"-block -> default security scheme will be used (see plugin-config "defaultSecuritySchemeName")
69-
get("hello", {
70-
// Set the security schemes to be used by this route
71-
securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme")
72-
description = "Protected 'Hello World'-Route"
73-
response {
74-
HttpStatusCode.OK to {
75-
description = "Successful Request"
76-
body<String> { description = "the response" }
77-
}
78-
// response for "401 Unauthorized" is automatically added (see plugin-config "defaultUnauthorizedResponse").
79-
}
80-
}) {
81-
call.respondText("Hello World!")
82-
}
83-
}
84-
}
65+
// configure routes
66+
routing {
67+
authenticate {
68+
// route is in an "authenticate"-block -> default security scheme will be used (see plugin-config "defaultSecuritySchemeName")
69+
get("hello", {
70+
// Set the security schemes to be used by this route
71+
securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme")
72+
description = "Protected 'Hello World'-Route"
73+
response {
74+
HttpStatusCode.OK to {
75+
description = "Successful Request"
76+
body<String> { description = "the response" }
77+
}
78+
// response for "401 Unauthorized" is automatically added (see plugin-config "defaultUnauthorizedResponse").
79+
}
80+
}) {
81+
call.respondText("Hello World!")
82+
}
83+
}
84+
// route is not in an "authenticate"-block and does not set the `protected` property -> security schemes will be ignored
85+
get("hello-unprotected", {
86+
// Security scheme will be ignored since the operation is not protected
87+
securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme")
88+
description = "Unprotected 'Hello World'-Route"
89+
response {
90+
HttpStatusCode.OK to {
91+
description = "Successful Request"
92+
body<String> { description = "the response" }
93+
}
94+
// no response for "401 Unauthorized" is added
95+
}
96+
}) {
97+
call.respondText("Hello World!")
98+
}
99+
// route is not in an "authenticate"-block but sets the `protected` property -> security scheme (or default security scheme) will be used
100+
get("hello-externally-protected", {
101+
// mark the route as protected even though there is no "authenticate"-block (e.g. because the route is protected by an external proxy)
102+
protected = true
103+
// Set the security scheme to be used by this route
104+
securitySchemeName = "MyOtherSecurityScheme"
105+
description = "Externally protected 'Hello World'-Route"
106+
response {
107+
HttpStatusCode.OK to {
108+
description = "Successful Request"
109+
body<String> { description = "the response" }
110+
}
111+
// response for "401 Unauthorized" is automatically added (see plugin-config "defaultUnauthorizedResponse").
112+
}
113+
}) {
114+
call.respondText("Hello World!")
115+
}
116+
}
85117
}

src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class RouteDocumentationMergerTest : StringSpec({
2323
route.hidden shouldBe false
2424
route.securitySchemeName shouldBe null
2525
route.securitySchemeNames.shouldBeEmpty()
26+
route.protected shouldBe null
2627
route.getRequest().also { requests ->
2728
requests.getParameters().shouldBeEmpty()
2829
requests.getBody() shouldBe null
@@ -44,6 +45,7 @@ class RouteDocumentationMergerTest : StringSpec({
4445
securitySchemeNames = listOf("securitySchemeNameA1", "securitySchemeNameA2")
4546
deprecated = true
4647
hidden = false
48+
protected = true
4749
request {
4850
queryParameter<String>("query")
4951
pathParameter<String>("pathA1")
@@ -66,6 +68,7 @@ class RouteDocumentationMergerTest : StringSpec({
6668
securitySchemeNames = listOf("securitySchemeNameB1", "securitySchemeNameB2")
6769
deprecated = false
6870
hidden = true
71+
protected = false
6972
request {
7073
queryParameter<String>("query")
7174
pathParameter<String>("pathB1")
@@ -93,6 +96,7 @@ class RouteDocumentationMergerTest : StringSpec({
9396
"securitySchemeNameB1",
9497
"securitySchemeNameB2"
9598
)
99+
route.protected shouldBe true
96100
route.getRequest().also { requests ->
97101
requests.getParameters().map { it.name } shouldContainExactlyInAnyOrder listOf(
98102
"query",
@@ -128,4 +132,4 @@ class RouteDocumentationMergerTest : StringSpec({
128132

129133
}
130134

131-
}
135+
}

0 commit comments

Comments
 (0)