Skip to content

Commit

Permalink
Merge branch 'develop' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
SMILEY4 committed Jun 17, 2023
2 parents 150b9f8 + 0c65cb2 commit dbafdb7
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 61 deletions.
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "io.github.smiley4"
version = "2.0.0"
version = "2.1.0"

repositories {
mavenCentral()
Expand All @@ -17,7 +17,7 @@ repositories {

dependencies {

val ktorVersion = "2.3.0"
val ktorVersion = "2.3.1"
implementation("io.ktor:ktor-server-core-jvm:$ktorVersion")
implementation("io.ktor:ktor-server-webjars:$ktorVersion")
implementation("io.ktor:ktor-server-auth:$ktorVersion")
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,25 @@ class OpenApiRoute {

/**
* A declaration of which security mechanism can be used for this operation.
* If not specified (and none specified with [securitySchemeNames]), defaultSecuritySchemeName (global plugin config) will be used
* If not specified (and none specified with [securitySchemeNames]), defaultSecuritySchemeName (global plugin config) will be used.
* Only applied to [protected] operations.
*/
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.
* Only applied to [protected] operations.
*/
var securitySchemeNames: Collection<String>? = null

/**
* Specifies whether this operation is protected.
* 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).
*/
var protected: Boolean? = null

private val request = OpenApiRequest()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ class RouteCollector(
return allRoutes(routeProvider())
.asSequence()
.map { route ->
val documentation = getDocumentation(route, OpenApiRoute())
RouteMeta(
method = getMethod(route),
path = getPath(route, config),
documentation = getDocumentation(route, OpenApiRoute()),
protected = isProtected(route)
documentation = documentation,
protected = documentation.protected ?: isProtected(route)
)
}
.filter { removeLeadingSlash(it.path) != removeLeadingSlash(config.getSwaggerUI().swaggerUrl) }
Expand All @@ -52,7 +53,8 @@ class RouteCollector(
private fun getDocumentation(route: Route, base: OpenApiRoute): OpenApiRoute {
var documentation = base
if (route.selector is DocumentedRouteSelector) {
documentation = routeDocumentationMerger.merge(documentation, (route.selector as DocumentedRouteSelector).documentation)
documentation =
routeDocumentationMerger.merge(documentation, (route.selector as DocumentedRouteSelector).documentation)
}
return if (route.parent != null) {
getDocumentation(route.parent!!, documentation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class RouteDocumentationMerger {
}
deprecated = a.deprecated || b.deprecated
hidden = a.hidden || b.hidden
protected = a.protected ?: b.protected
request {
(getParameters() as MutableList).also {
it.addAll(a.getRequest().getParameters())
Expand All @@ -34,4 +35,4 @@ class RouteDocumentationMerger {
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import io.ktor.server.response.respondText
import io.ktor.server.routing.routing

fun main() {
embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true)
embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true)
}

/**
Expand All @@ -28,58 +28,90 @@ fun main() {
*/
private fun Application.myModule() {

// Install "Authentication"-Plugin and setup Basic-Auth
install(Authentication) {
basic {
realm = "Access to the API"
validate { credentials ->
if (credentials.name == "user" && credentials.password == "pass") {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
// Install "Authentication"-Plugin and setup Basic-Auth
install(Authentication) {
basic {
realm = "Access to the API"
validate { credentials ->
if (credentials.name == "user" && credentials.password == "pass") {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}

// Install "Swagger-UI"-Plugin
install(SwaggerUI) {
// default value for "401 Unauthorized"-responses.
// the name of the security scheme (see below) to use for each route when nothing else is specified
defaultSecuritySchemeName = "MySecurityScheme"
defaultUnauthorizedResponse {
description = "Username or password is invalid."
}
// specify a security scheme
securityScheme("MySecurityScheme") {
type = AuthType.HTTP
scheme = AuthScheme.BASIC
}
// specify another security scheme
securityScheme("MyOtherSecurityScheme") {
type = AuthType.HTTP
scheme = AuthScheme.BASIC
}
}
// Install "Swagger-UI"-Plugin
install(SwaggerUI) {
// default value for "401 Unauthorized"-responses.
// the name of the security scheme (see below) to use for each route when nothing else is specified
defaultSecuritySchemeName = "MySecurityScheme"
defaultUnauthorizedResponse {
description = "Username or password is invalid."
}
// specify a security scheme
securityScheme("MySecurityScheme") {
type = AuthType.HTTP
scheme = AuthScheme.BASIC
}
// specify another security scheme
securityScheme("MyOtherSecurityScheme") {
type = AuthType.HTTP
scheme = AuthScheme.BASIC
}
}

// configure routes
routing {
authenticate {
// route is in an "authenticate"-block -> default security scheme will be used (see plugin-config "defaultSecuritySchemeName")
get("hello", {
// Set the security schemes to be used by this route
securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme")
description = "Protected 'Hello World'-Route"
response {
HttpStatusCode.OK to {
description = "Successful Request"
body<String> { description = "the response" }
}
// response for "401 Unauthorized" is automatically added (see plugin-config "defaultUnauthorizedResponse").
}
}) {
call.respondText("Hello World!")
}
}
}
// configure routes
routing {
authenticate {
// route is in an "authenticate"-block -> default security scheme will be used (see plugin-config "defaultSecuritySchemeName")
get("hello", {
// Set the security schemes to be used by this route
securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme")
description = "Protected 'Hello World'-Route"
response {
HttpStatusCode.OK to {
description = "Successful Request"
body<String> { description = "the response" }
}
// response for "401 Unauthorized" is automatically added (see plugin-config "defaultUnauthorizedResponse").
}
}) {
call.respondText("Hello World!")
}
}
// route is not in an "authenticate"-block and does not set the `protected` property -> security schemes will be ignored
get("hello-unprotected", {
// Security scheme will be ignored since the operation is not protected
securitySchemeNames = setOf("MyOtherSecurityScheme", "MySecurityScheme")
description = "Unprotected 'Hello World'-Route"
response {
HttpStatusCode.OK to {
description = "Successful Request"
body<String> { description = "the response" }
}
// no response for "401 Unauthorized" is added
}
}) {
call.respondText("Hello World!")
}
// route is not in an "authenticate"-block but sets the `protected` property -> security scheme (or default security scheme) will be used
get("hello-externally-protected", {
// mark the route as protected even though there is no "authenticate"-block (e.g. because the route is protected by an external proxy)
protected = true
// Set the security scheme to be used by this route
securitySchemeName = "MyOtherSecurityScheme"
description = "Externally protected 'Hello World'-Route"
response {
HttpStatusCode.OK to {
description = "Successful Request"
body<String> { description = "the response" }
}
// response for "401 Unauthorized" is automatically added (see plugin-config "defaultUnauthorizedResponse").
}
}) {
call.respondText("Hello World!")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class RouteDocumentationMergerTest : StringSpec({
route.hidden shouldBe false
route.securitySchemeName shouldBe null
route.securitySchemeNames.shouldBeEmpty()
route.protected shouldBe null
route.getRequest().also { requests ->
requests.getParameters().shouldBeEmpty()
requests.getBody() shouldBe null
Expand All @@ -44,6 +45,7 @@ class RouteDocumentationMergerTest : StringSpec({
securitySchemeNames = listOf("securitySchemeNameA1", "securitySchemeNameA2")
deprecated = true
hidden = false
protected = true
request {
queryParameter<String>("query")
pathParameter<String>("pathA1")
Expand All @@ -66,6 +68,7 @@ class RouteDocumentationMergerTest : StringSpec({
securitySchemeNames = listOf("securitySchemeNameB1", "securitySchemeNameB2")
deprecated = false
hidden = true
protected = false
request {
queryParameter<String>("query")
pathParameter<String>("pathB1")
Expand Down Expand Up @@ -93,6 +96,7 @@ class RouteDocumentationMergerTest : StringSpec({
"securitySchemeNameB1",
"securitySchemeNameB2"
)
route.protected shouldBe true
route.getRequest().also { requests ->
requests.getParameters().map { it.name } shouldContainExactlyInAnyOrder listOf(
"query",
Expand Down Expand Up @@ -128,4 +132,4 @@ class RouteDocumentationMergerTest : StringSpec({

}

}
}

0 comments on commit dbafdb7

Please sign in to comment.