forked from loafle/openapi-generator-original
[Kotlin Server Ktor] upgrade to stable version (1.1.3) (#2333)
* [Kotlin Server Ktor] upgrade to stable version (1.1.3) * [Kotlin Server Ktor] upgrade to stable version (1.1.3) fix indentation * [Kotlin Server Ktor] upgrade to stable version (1.1.3) fix indentation * add missing kotlin ktor in Server stubs list * [Kotlin Server Ktor] upgrade to stable version (1.1.3) fix compilation warnings
This commit is contained in:
committed by
William Cheng
parent
3e085e9492
commit
f2ff473155
@@ -39,7 +39,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se
|
||||
| | Languages/Frameworks |
|
||||
|-|-|
|
||||
**API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client), **Kotlin**, **Lua**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (AngularJS, Angular (2.x - 7.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs)
|
||||
**Server stubs** | **Ada**, **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go** (net/http, Gin), **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples)), **Kotlin** (Spring Boot), **PHP** (Laravel, Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), Scalatra)
|
||||
**Server stubs** | **Ada**, **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go** (net/http, Gin), **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples)), **Kotlin** (Spring Boot, Ktor), **PHP** (Laravel, Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), Scalatra)
|
||||
**API documentation generators** | **HTML**, **Confluence Wiki**
|
||||
**Configuration files** | [**Apache2**](https://httpd.apache.org/)
|
||||
**Others** | **GraphQL**, **JMeter**, **MySQL Schema**
|
||||
|
||||
@@ -3,38 +3,48 @@ package {{packageName}}.infrastructure
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.http.auth.*
|
||||
import io.ktor.request.ApplicationRequest
|
||||
import io.ktor.response.respond
|
||||
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.pipeline.*
|
||||
import io.ktor.request.*
|
||||
import io.ktor.response.*
|
||||
import java.util.*
|
||||
|
||||
enum class ApiKeyLocation(val location: String) {
|
||||
QUERY("query"),
|
||||
HEADER("header")
|
||||
}
|
||||
data class ApiKey(val value: String): Credential
|
||||
data class ApiPrincipal(val apiKey: ApiKey?) : Principal
|
||||
fun ApplicationCall.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? = request.apiKey(key, keyLocation)
|
||||
fun ApplicationRequest.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? {
|
||||
val value: String? = when(keyLocation) {
|
||||
ApiKeyLocation.QUERY -> this.queryParameters[key]
|
||||
ApiKeyLocation.HEADER -> this.headers[key]
|
||||
}
|
||||
when (value) {
|
||||
null -> return null
|
||||
else -> return ApiKey(value)
|
||||
data class ApiKeyCredential(val value: String): Credential
|
||||
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents a Api Key authentication provider
|
||||
* @param name is the name of the provider, or `null` for a default provider
|
||||
*/
|
||||
class ApiKeyAuthenticationProvider(name: String?) : AuthenticationProvider(name) {
|
||||
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
|
||||
|
||||
var apiKeyName: String = "";
|
||||
|
||||
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
|
||||
|
||||
/**
|
||||
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
|
||||
* or null if credential does not correspond to an authenticated principal
|
||||
*/
|
||||
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
|
||||
authenticationFunction = body
|
||||
}
|
||||
}
|
||||
|
||||
fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String, validate: suspend (ApiKey) -> ApiPrincipal?) {
|
||||
intercept(AuthenticationPipeline.RequestAuthentication) { context ->
|
||||
val credentials = call.request.apiKey(apiKeyName, ApiKeyLocation.values().first { it.location == authLocation })
|
||||
val principal = credentials?.let { validate(it) }
|
||||
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
|
||||
val provider = ApiKeyAuthenticationProvider(name).apply(configure)
|
||||
val apiKeyName = provider.apiKeyName
|
||||
val apiKeyLocation = provider.apiKeyLocation
|
||||
val authenticate = provider.authenticationFunction
|
||||
|
||||
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
|
||||
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
|
||||
val principal = credentials?.let { authenticate(call, it) }
|
||||
|
||||
val cause = when {
|
||||
credentials == null -> AuthenticationFailedCause.NoCredentials
|
||||
@@ -49,9 +59,20 @@ fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String,
|
||||
it.complete()
|
||||
}
|
||||
}
|
||||
|
||||
if (principal != null) {
|
||||
context.principal(principal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
|
||||
val value: String? = when(apiKeyLocation) {
|
||||
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
|
||||
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
|
||||
}
|
||||
when (value) {
|
||||
null -> return null
|
||||
else -> return ApiKeyCredential(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ import io.ktor.locations.*
|
||||
import io.ktor.metrics.*
|
||||
import io.ktor.routing.*
|
||||
import java.util.concurrent.*
|
||||
{{#hasAuthMethods}}
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.openapitools.server.infrastructure.*
|
||||
{{/hasAuthMethods}}
|
||||
{{#generateApis}}
|
||||
import {{apiPackage}}.*
|
||||
{{/generateApis}}
|
||||
@@ -20,12 +25,15 @@ import {{apiPackage}}.*
|
||||
{{#imports}}import {{import}}
|
||||
{{/imports}}
|
||||
|
||||
@KtorExperimentalAPI
|
||||
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
|
||||
|
||||
object HTTP {
|
||||
val client = HttpClient(Apache)
|
||||
}
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Application.main() {
|
||||
install(DefaultHeaders)
|
||||
install(Metrics) {
|
||||
@@ -56,6 +64,49 @@ fun Application.main() {
|
||||
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
|
||||
{{/featureCompression}}
|
||||
install(Locations) // see http://ktor.io/features/locations.html
|
||||
{{#hasAuthMethods}}
|
||||
install(Authentication) {
|
||||
{{#authMethods}}
|
||||
{{#isBasic}}
|
||||
basic("{{{name}}}") {
|
||||
validate { credentials ->
|
||||
// TODO: "Apply your basic authentication functionality."
|
||||
// Accessible in-method via call.principal<UserIdPrincipal>()
|
||||
if (credentials.name == "Swagger" && "Codegen" == credentials.password) {
|
||||
UserIdPrincipal(credentials.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
{{/isBasic}}
|
||||
{{#isApiKey}}
|
||||
// "Implement API key auth ({{{name}}}) for parameter name '{{{keyParamName}}}'."
|
||||
apiKeyAuth("{{{name}}}") {
|
||||
validate { apikeyCredential: ApiKeyCredential ->
|
||||
when {
|
||||
apikeyCredential.value == "keyboardcat" -> ApiPrincipal(apikeyCredential)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
{{/isApiKey}}
|
||||
{{#isOAuth}}
|
||||
{{#bodyAllowed}}
|
||||
{{/bodyAllowed}}
|
||||
{{^bodyAllowed}}
|
||||
oauth("{{name}}") {
|
||||
client = HttpClient(Apache)
|
||||
providerLookup = { ApplicationAuthProviders["{{{name}}}"] }
|
||||
urlProvider = { _ ->
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
}
|
||||
}
|
||||
{{/bodyAllowed}}
|
||||
{{/isOAuth}}
|
||||
{{/authMethods}}
|
||||
}
|
||||
{{/hasAuthMethods}}
|
||||
install(Routing) {
|
||||
{{#apiInfo}}
|
||||
{{#apis}}
|
||||
@@ -65,10 +116,11 @@ fun Application.main() {
|
||||
{{/apis}}
|
||||
{{/apiInfo}}
|
||||
}
|
||||
|
||||
{{/generateApis}}
|
||||
|
||||
environment.monitor.subscribe(ApplicationStopping)
|
||||
{
|
||||
HTTP.client.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package {{packageName}}
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.features.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@@ -77,6 +78,7 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
|
||||
{{/featureCompression}}
|
||||
|
||||
// Defines authentication mechanisms used throughout the application.
|
||||
@KtorExperimentalAPI
|
||||
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
|
||||
{{#authMethods}}
|
||||
{{#isOAuth}}
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
{{>licenseInfo}}
|
||||
package {{packageName}}
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.locations.*
|
||||
import io.ktor.pipeline.PipelineContext
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.method
|
||||
import {{modelPackage}}.*
|
||||
|
||||
{{#imports}}
|
||||
import {{import}}
|
||||
{{/imports}}
|
||||
|
||||
// NOTE: ktor-location@0.9.0 is missing extension for Route.delete. This includes it.
|
||||
inline fun <reified T : Any> Route.delete(noinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit): Route {
|
||||
return location(T::class) {
|
||||
method(HttpMethod.Delete) {
|
||||
handle(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{{#apiInfo}}
|
||||
object Paths {
|
||||
{{#apis}}
|
||||
@@ -33,6 +19,7 @@ object Paths {
|
||||
* {{#unescapedNotes}}{{.}}{{/unescapedNotes}}
|
||||
{{#allParams}}* @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
|
||||
{{/allParams}}*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("{{path}}") class {{operationId}}({{#allParams}}val {{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
|
||||
|
||||
{{/bodyAllowed}}
|
||||
|
||||
@@ -8,8 +8,8 @@ Generated by OpenAPI Generator {{generatorVersion}}{{^hideGenerationTimestamp}}
|
||||
|
||||
## Requires
|
||||
|
||||
* Kotlin 1.2.10
|
||||
* Gradle 4.3
|
||||
* Kotlin 1.3.21
|
||||
* Gradle 4.9
|
||||
|
||||
## Build
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import com.google.gson.Gson
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.UserIdPrincipal
|
||||
import io.ktor.auth.authentication
|
||||
import io.ktor.auth.basicAuthentication
|
||||
import io.ktor.auth.oauth
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.auth.OAuthAccessTokenResponse
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.http.ContentType
|
||||
@@ -16,23 +15,15 @@ import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.*
|
||||
|
||||
import kotlinx.coroutines.experimental.asCoroutineDispatcher
|
||||
|
||||
import {{packageName}}.ApplicationAuthProviders
|
||||
import {{packageName}}.Paths
|
||||
import {{packageName}}.ApplicationExecutors
|
||||
import {{packageName}}.HTTP.client
|
||||
import {{packageName}}.infrastructure.ApiPrincipal
|
||||
import {{packageName}}.infrastructure.apiKeyAuth
|
||||
|
||||
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
|
||||
// see https://github.com/ktorio/ktor/issues/288
|
||||
import {{packageName}}.delete
|
||||
|
||||
{{#imports}}import {{import}}
|
||||
{{/imports}}
|
||||
|
||||
{{#operations}}
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.{{classname}}() {
|
||||
val gson = Gson()
|
||||
val empty = mutableMapOf<String, Any?>()
|
||||
@@ -40,75 +31,27 @@ fun Route.{{classname}}() {
|
||||
{{#bodyAllowed}}
|
||||
|
||||
route("{{path}}") {
|
||||
{{#hasAuthMethods}}
|
||||
{{#authMethods}}
|
||||
authenticate("{{{name}}}") {
|
||||
{{/authMethods}}
|
||||
{{/hasAuthMethods}}
|
||||
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}} {
|
||||
{{#lambda.indented_12}}{{>libraries/ktor/_api_body}}{{/lambda.indented_12}}
|
||||
}
|
||||
{{#hasAuthMethods}}
|
||||
}
|
||||
{{/hasAuthMethods}}
|
||||
}
|
||||
{{/bodyAllowed}}
|
||||
{{^bodyAllowed}}
|
||||
|
||||
{{! NOTE: Locations can be used on routes without body parameters.}}
|
||||
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> { it: Paths.{{operationId}} ->
|
||||
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> { _: Paths.{{operationId}} ->
|
||||
{{#lambda.indented_8}}{{>libraries/ktor/_api_body}}{{/lambda.indented_8}}
|
||||
}
|
||||
{{/bodyAllowed}}
|
||||
{{! THis looks a little weird, but it's completely valid Kotlin code, and simplifies templated route logic above. }}
|
||||
{{#hasAuthMethods}}.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
{{#authMethods}}
|
||||
{{#isBasic}}
|
||||
basicAuthentication("{{{name}}}") { credentials ->
|
||||
// TODO: "Apply your basic authentication functionality."
|
||||
// Accessible in-method via call.principal<UserIdPrincipal>()
|
||||
if (credentials.name == "Swagger" && "Codegen" == credentials.password) {
|
||||
UserIdPrincipal(credentials.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
{{/isBasic}}
|
||||
{{#isApiKey}}
|
||||
// "Implement API key auth ({{{name}}}) for parameter name '{{{keyParamName}}}'."
|
||||
apiKeyAuth("{{{keyParamName}}}", {{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInHeader}}"header"{{/isKeyInHeader}}) {
|
||||
// TODO: "Verify key here , accessible as it.value"
|
||||
if (it.value == "keyboardcat") {
|
||||
ApiPrincipal(it)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
{{/isApiKey}}
|
||||
{{#isOAuth}}
|
||||
{{#bodyAllowed}}
|
||||
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["{{{name}}}"] }, {
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
{{/bodyAllowed}}
|
||||
{{^bodyAllowed}}
|
||||
oauthAtLocation<Paths.{{operationId}}>(client, ApplicationExecutors.asCoroutineDispatcher(),
|
||||
providerLookup = { ApplicationAuthProviders["{{{name}}}"] },
|
||||
urlProvider = { currentLocation, provider ->
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
{{/bodyAllowed}}
|
||||
{{/isOAuth}}
|
||||
{{/authMethods}}
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '{{path}}' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
{{/hasAuthMethods}}
|
||||
{{^hasAuthMethods}}
|
||||
|
||||
{{/hasAuthMethods}}
|
||||
{{/operation}}
|
||||
}
|
||||
{{/operations}}
|
||||
{{/operations}}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
group '{{groupId}}'
|
||||
version '{{artifactVersion}}'
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.3'
|
||||
wrapper {
|
||||
gradleVersion = '4.9'
|
||||
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.10'
|
||||
ext.ktor_version = '0.9.1-alpha-9'
|
||||
ext.shadow_version = '2.0.2'
|
||||
ext.kotlin_version = '1.3.21'
|
||||
ext.ktor_version = '1.1.3'
|
||||
ext.shadow_version = '2.0.3'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -43,12 +43,6 @@ compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "enable"
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
baseName = '{{artifactId}}'
|
||||
classifier = null
|
||||
@@ -62,7 +56,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
compile "io.ktor:ktor-server-netty:$ktor_version"
|
||||
compile "io.ktor:ktor-metrics:$ktor_version"
|
||||
compile "io.ktor:ktor-locations:$ktor_version"
|
||||
|
||||
@@ -6,8 +6,8 @@ Generated by OpenAPI Generator 4.0.0-SNAPSHOT.
|
||||
|
||||
## Requires
|
||||
|
||||
* Kotlin 1.2.10
|
||||
* Gradle 4.3
|
||||
* Kotlin 1.3.21
|
||||
* Gradle 4.9
|
||||
|
||||
## Build
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
group 'org.openapitools'
|
||||
version '1.0.0'
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.3'
|
||||
wrapper {
|
||||
gradleVersion = '4.9'
|
||||
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.10'
|
||||
ext.ktor_version = '0.9.1-alpha-9'
|
||||
ext.shadow_version = '2.0.2'
|
||||
ext.kotlin_version = '1.3.21'
|
||||
ext.ktor_version = '1.1.3'
|
||||
ext.shadow_version = '2.0.3'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -43,12 +43,6 @@ compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "enable"
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
baseName = 'kotlin-server'
|
||||
classifier = null
|
||||
@@ -62,7 +56,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
compile "io.ktor:ktor-server-netty:$ktor_version"
|
||||
compile "io.ktor:ktor-metrics:$ktor_version"
|
||||
compile "io.ktor:ktor-locations:$ktor_version"
|
||||
|
||||
@@ -13,15 +13,21 @@ import io.ktor.locations.*
|
||||
import io.ktor.metrics.*
|
||||
import io.ktor.routing.*
|
||||
import java.util.concurrent.*
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.openapitools.server.infrastructure.*
|
||||
import org.openapitools.server.apis.*
|
||||
|
||||
|
||||
@KtorExperimentalAPI
|
||||
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
|
||||
|
||||
object HTTP {
|
||||
val client = HttpClient(Apache)
|
||||
}
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Application.main() {
|
||||
install(DefaultHeaders)
|
||||
install(Metrics) {
|
||||
@@ -39,14 +45,34 @@ fun Application.main() {
|
||||
install(HSTS, ApplicationHstsConfiguration()) // see http://ktor.io/features/hsts.html
|
||||
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
|
||||
install(Locations) // see http://ktor.io/features/locations.html
|
||||
install(Authentication) {
|
||||
// "Implement API key auth (api_key) for parameter name 'api_key'."
|
||||
apiKeyAuth("api_key") {
|
||||
validate { apikeyCredential: ApiKeyCredential ->
|
||||
when {
|
||||
apikeyCredential.value == "keyboardcat" -> ApiPrincipal(apikeyCredential)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
oauth("petstore_auth") {
|
||||
client = HttpClient(Apache)
|
||||
providerLookup = { ApplicationAuthProviders["petstore_auth"] }
|
||||
urlProvider = { _ ->
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
}
|
||||
}
|
||||
}
|
||||
install(Routing) {
|
||||
PetApi()
|
||||
StoreApi()
|
||||
UserApi()
|
||||
}
|
||||
|
||||
|
||||
environment.monitor.subscribe(ApplicationStopping)
|
||||
{
|
||||
HTTP.client.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package org.openapitools.server
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.features.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@@ -50,6 +51,7 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
|
||||
}
|
||||
|
||||
// Defines authentication mechanisms used throughout the application.
|
||||
@KtorExperimentalAPI
|
||||
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
|
||||
OAuthServerSettings.OAuth2ServerSettings(
|
||||
name = "petstore_auth",
|
||||
|
||||
@@ -11,24 +11,10 @@
|
||||
*/
|
||||
package org.openapitools.server
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.locations.*
|
||||
import io.ktor.pipeline.PipelineContext
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.method
|
||||
import org.openapitools.server.models.*
|
||||
|
||||
|
||||
// NOTE: ktor-location@0.9.0 is missing extension for Route.delete. This includes it.
|
||||
inline fun <reified T : Any> Route.delete(noinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit): Route {
|
||||
return location(T::class) {
|
||||
method(HttpMethod.Delete) {
|
||||
handle(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Paths {
|
||||
/**
|
||||
* Deletes a pet
|
||||
@@ -36,6 +22,7 @@ object Paths {
|
||||
* @param petId Pet id to delete
|
||||
* @param apiKey (optional, default to null)
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/pet/{petId}") class deletePet(val petId: kotlin.Long, val apiKey: kotlin.String)
|
||||
|
||||
/**
|
||||
@@ -43,6 +30,7 @@ object Paths {
|
||||
* Multiple status values can be provided with comma separated strings
|
||||
* @param status Status values that need to be considered for filter
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/pet/findByStatus") class findPetsByStatus(val status: kotlin.Array<kotlin.String>)
|
||||
|
||||
/**
|
||||
@@ -50,6 +38,7 @@ object Paths {
|
||||
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
|
||||
* @param tags Tags to filter by
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/pet/findByTags") class findPetsByTags(val tags: kotlin.Array<kotlin.String>)
|
||||
|
||||
/**
|
||||
@@ -57,6 +46,7 @@ object Paths {
|
||||
* Returns a single pet
|
||||
* @param petId ID of pet to return
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/pet/{petId}") class getPetById(val petId: kotlin.Long)
|
||||
|
||||
/**
|
||||
@@ -64,12 +54,14 @@ object Paths {
|
||||
* For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
|
||||
* @param orderId ID of the order that needs to be deleted
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/store/order/{orderId}") class deleteOrder(val orderId: kotlin.String)
|
||||
|
||||
/**
|
||||
* Returns pet inventories by status
|
||||
* Returns a map of status codes to quantities
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/store/inventory") class getInventory()
|
||||
|
||||
/**
|
||||
@@ -77,6 +69,7 @@ object Paths {
|
||||
* For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
|
||||
* @param orderId ID of pet that needs to be fetched
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/store/order/{orderId}") class getOrderById(val orderId: kotlin.Long)
|
||||
|
||||
/**
|
||||
@@ -84,6 +77,7 @@ object Paths {
|
||||
* This can only be done by the logged in user.
|
||||
* @param username The name that needs to be deleted
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/user/{username}") class deleteUser(val username: kotlin.String)
|
||||
|
||||
/**
|
||||
@@ -91,6 +85,7 @@ object Paths {
|
||||
*
|
||||
* @param username The name that needs to be fetched. Use user1 for testing.
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/user/{username}") class getUserByName(val username: kotlin.String)
|
||||
|
||||
/**
|
||||
@@ -99,12 +94,14 @@ object Paths {
|
||||
* @param username The user name for login
|
||||
* @param password The password for login in clear text
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/user/login") class loginUser(val username: kotlin.String, val password: kotlin.String)
|
||||
|
||||
/**
|
||||
* Logs out current logged in user session
|
||||
*
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/user/logout") class logoutUser()
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ import com.google.gson.Gson
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.UserIdPrincipal
|
||||
import io.ktor.auth.authentication
|
||||
import io.ktor.auth.basicAuthentication
|
||||
import io.ktor.auth.oauth
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.auth.OAuthAccessTokenResponse
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.http.ContentType
|
||||
@@ -26,27 +25,20 @@ import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.*
|
||||
|
||||
import kotlinx.coroutines.experimental.asCoroutineDispatcher
|
||||
|
||||
import org.openapitools.server.ApplicationAuthProviders
|
||||
import org.openapitools.server.Paths
|
||||
import org.openapitools.server.ApplicationExecutors
|
||||
import org.openapitools.server.HTTP.client
|
||||
import org.openapitools.server.infrastructure.ApiPrincipal
|
||||
import org.openapitools.server.infrastructure.apiKeyAuth
|
||||
|
||||
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
|
||||
// see https://github.com/ktorio/ktor/issues/288
|
||||
import org.openapitools.server.delete
|
||||
|
||||
import org.openapitools.server.models.ApiResponse
|
||||
import org.openapitools.server.models.Pet
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.PetApi() {
|
||||
val gson = Gson()
|
||||
val empty = mutableMapOf<String, Any?>()
|
||||
|
||||
route("/pet") {
|
||||
authenticate("petstore_auth") {
|
||||
post {
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
@@ -56,25 +48,11 @@ fun Route.PetApi() {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
delete<Paths.deletePet> { it: Paths.deletePet ->
|
||||
|
||||
delete<Paths.deletePet> { _: Paths.deletePet ->
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
if (principal == null) {
|
||||
@@ -83,26 +61,9 @@ fun Route.PetApi() {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauthAtLocation<Paths.deletePet>(client, ApplicationExecutors.asCoroutineDispatcher(),
|
||||
providerLookup = { ApplicationAuthProviders["petstore_auth"] },
|
||||
urlProvider = { currentLocation, provider ->
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet/{petId}' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
get<Paths.findPetsByStatus> { it: Paths.findPetsByStatus ->
|
||||
get<Paths.findPetsByStatus> { _: Paths.findPetsByStatus ->
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
if (principal == null) {
|
||||
@@ -134,26 +95,9 @@ fun Route.PetApi() {
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauthAtLocation<Paths.findPetsByStatus>(client, ApplicationExecutors.asCoroutineDispatcher(),
|
||||
providerLookup = { ApplicationAuthProviders["petstore_auth"] },
|
||||
urlProvider = { currentLocation, provider ->
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet/findByStatus' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
get<Paths.findPetsByTags> { it: Paths.findPetsByTags ->
|
||||
get<Paths.findPetsByTags> { _: Paths.findPetsByTags ->
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
if (principal == null) {
|
||||
@@ -185,26 +129,9 @@ fun Route.PetApi() {
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauthAtLocation<Paths.findPetsByTags>(client, ApplicationExecutors.asCoroutineDispatcher(),
|
||||
providerLookup = { ApplicationAuthProviders["petstore_auth"] },
|
||||
urlProvider = { currentLocation, provider ->
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet/findByTags' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
get<Paths.getPetById> { it: Paths.getPetById ->
|
||||
get<Paths.getPetById> { _: Paths.getPetById ->
|
||||
val principal = call.authentication.principal<ApiPrincipal>()
|
||||
|
||||
if (principal == null) {
|
||||
@@ -236,29 +163,10 @@ fun Route.PetApi() {
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
// "Implement API key auth (api_key) for parameter name 'api_key'."
|
||||
apiKeyAuth("api_key", "header") {
|
||||
// TODO: "Verify key here , accessible as it.value"
|
||||
if (it.value == "keyboardcat") {
|
||||
ApiPrincipal(it)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet/{petId}' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
route("/pet") {
|
||||
authenticate("petstore_auth") {
|
||||
put {
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
@@ -268,25 +176,12 @@ fun Route.PetApi() {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
route("/pet/{petId}") {
|
||||
authenticate("petstore_auth") {
|
||||
post {
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
@@ -296,25 +191,12 @@ fun Route.PetApi() {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet/{petId}' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
route("/pet/{petId}/uploadImage") {
|
||||
authenticate("petstore_auth") {
|
||||
post {
|
||||
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
|
||||
|
||||
@@ -335,21 +217,7 @@ fun Route.PetApi() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
|
||||
// TODO: define a callback url here.
|
||||
"/"
|
||||
})
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/pet/{petId}/uploadImage' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ import com.google.gson.Gson
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.UserIdPrincipal
|
||||
import io.ktor.auth.authentication
|
||||
import io.ktor.auth.basicAuthentication
|
||||
import io.ktor.auth.oauth
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.auth.OAuthAccessTokenResponse
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.http.ContentType
|
||||
@@ -26,31 +25,23 @@ import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.*
|
||||
|
||||
import kotlinx.coroutines.experimental.asCoroutineDispatcher
|
||||
|
||||
import org.openapitools.server.ApplicationAuthProviders
|
||||
import org.openapitools.server.Paths
|
||||
import org.openapitools.server.ApplicationExecutors
|
||||
import org.openapitools.server.HTTP.client
|
||||
import org.openapitools.server.infrastructure.ApiPrincipal
|
||||
import org.openapitools.server.infrastructure.apiKeyAuth
|
||||
|
||||
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
|
||||
// see https://github.com/ktorio/ktor/issues/288
|
||||
import org.openapitools.server.delete
|
||||
|
||||
import org.openapitools.server.models.Order
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.StoreApi() {
|
||||
val gson = Gson()
|
||||
val empty = mutableMapOf<String, Any?>()
|
||||
|
||||
delete<Paths.deleteOrder> { it: Paths.deleteOrder ->
|
||||
delete<Paths.deleteOrder> { _: Paths.deleteOrder ->
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
|
||||
|
||||
get<Paths.getInventory> { it: Paths.getInventory ->
|
||||
|
||||
get<Paths.getInventory> { _: Paths.getInventory ->
|
||||
val principal = call.authentication.principal<ApiPrincipal>()
|
||||
|
||||
if (principal == null) {
|
||||
@@ -59,29 +50,9 @@ fun Route.StoreApi() {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
|
||||
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
|
||||
|
||||
try {
|
||||
authentication {
|
||||
// "Implement API key auth (api_key) for parameter name 'api_key'."
|
||||
apiKeyAuth("api_key", "header") {
|
||||
// TODO: "Verify key here , accessible as it.value"
|
||||
if (it.value == "keyboardcat") {
|
||||
ApiPrincipal(it)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
|
||||
application.environment.log.warn("authentication block for '/store/inventory' is duplicated in code. " +
|
||||
"Generated endpoints may need to be merged under a 'route' entry.")
|
||||
}
|
||||
}
|
||||
|
||||
get<Paths.getOrderById> { it: Paths.getOrderById ->
|
||||
get<Paths.getOrderById> { _: Paths.getOrderById ->
|
||||
val exampleContentType = "application/json"
|
||||
val exampleContentString = """{
|
||||
"petId" : 6,
|
||||
@@ -98,7 +69,7 @@ fun Route.StoreApi() {
|
||||
else -> call.respondText(exampleContentString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
route("/store/order") {
|
||||
post {
|
||||
@@ -119,5 +90,5 @@ fun Route.StoreApi() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ import com.google.gson.Gson
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.UserIdPrincipal
|
||||
import io.ktor.auth.authentication
|
||||
import io.ktor.auth.basicAuthentication
|
||||
import io.ktor.auth.oauth
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.auth.OAuthAccessTokenResponse
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.http.ContentType
|
||||
@@ -26,21 +25,13 @@ import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.*
|
||||
|
||||
import kotlinx.coroutines.experimental.asCoroutineDispatcher
|
||||
|
||||
import org.openapitools.server.ApplicationAuthProviders
|
||||
import org.openapitools.server.Paths
|
||||
import org.openapitools.server.ApplicationExecutors
|
||||
import org.openapitools.server.HTTP.client
|
||||
import org.openapitools.server.infrastructure.ApiPrincipal
|
||||
import org.openapitools.server.infrastructure.apiKeyAuth
|
||||
|
||||
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
|
||||
// see https://github.com/ktorio/ktor/issues/288
|
||||
import org.openapitools.server.delete
|
||||
|
||||
import org.openapitools.server.models.User
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.UserApi() {
|
||||
val gson = Gson()
|
||||
val empty = mutableMapOf<String, Any?>()
|
||||
@@ -50,28 +41,28 @@ fun Route.UserApi() {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
route("/user/createWithArray") {
|
||||
post {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
route("/user/createWithList") {
|
||||
post {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
delete<Paths.deleteUser> { it: Paths.deleteUser ->
|
||||
|
||||
delete<Paths.deleteUser> { _: Paths.deleteUser ->
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
|
||||
|
||||
get<Paths.getUserByName> { it: Paths.getUserByName ->
|
||||
|
||||
get<Paths.getUserByName> { _: Paths.getUserByName ->
|
||||
val exampleContentType = "application/json"
|
||||
val exampleContentString = """{
|
||||
"firstName" : "firstName",
|
||||
@@ -90,22 +81,22 @@ fun Route.UserApi() {
|
||||
else -> call.respondText(exampleContentString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get<Paths.loginUser> { it: Paths.loginUser ->
|
||||
|
||||
get<Paths.loginUser> { _: Paths.loginUser ->
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
|
||||
|
||||
get<Paths.logoutUser> { it: Paths.logoutUser ->
|
||||
|
||||
get<Paths.logoutUser> { _: Paths.logoutUser ->
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
|
||||
|
||||
|
||||
route("/user/{username}") {
|
||||
put {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,38 +3,48 @@ package org.openapitools.server.infrastructure
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.http.auth.*
|
||||
import io.ktor.request.ApplicationRequest
|
||||
import io.ktor.response.respond
|
||||
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.pipeline.*
|
||||
import io.ktor.request.*
|
||||
import io.ktor.response.*
|
||||
import java.util.*
|
||||
|
||||
enum class ApiKeyLocation(val location: String) {
|
||||
QUERY("query"),
|
||||
HEADER("header")
|
||||
}
|
||||
data class ApiKey(val value: String): Credential
|
||||
data class ApiPrincipal(val apiKey: ApiKey?) : Principal
|
||||
fun ApplicationCall.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? = request.apiKey(key, keyLocation)
|
||||
fun ApplicationRequest.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? {
|
||||
val value: String? = when(keyLocation) {
|
||||
ApiKeyLocation.QUERY -> this.queryParameters[key]
|
||||
ApiKeyLocation.HEADER -> this.headers[key]
|
||||
}
|
||||
when (value) {
|
||||
null -> return null
|
||||
else -> return ApiKey(value)
|
||||
data class ApiKeyCredential(val value: String): Credential
|
||||
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents a Api Key authentication provider
|
||||
* @param name is the name of the provider, or `null` for a default provider
|
||||
*/
|
||||
class ApiKeyAuthenticationProvider(name: String?) : AuthenticationProvider(name) {
|
||||
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
|
||||
|
||||
var apiKeyName: String = "";
|
||||
|
||||
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
|
||||
|
||||
/**
|
||||
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
|
||||
* or null if credential does not correspond to an authenticated principal
|
||||
*/
|
||||
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
|
||||
authenticationFunction = body
|
||||
}
|
||||
}
|
||||
|
||||
fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String, validate: suspend (ApiKey) -> ApiPrincipal?) {
|
||||
intercept(AuthenticationPipeline.RequestAuthentication) { context ->
|
||||
val credentials = call.request.apiKey(apiKeyName, ApiKeyLocation.values().first { it.location == authLocation })
|
||||
val principal = credentials?.let { validate(it) }
|
||||
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
|
||||
val provider = ApiKeyAuthenticationProvider(name).apply(configure)
|
||||
val apiKeyName = provider.apiKeyName
|
||||
val apiKeyLocation = provider.apiKeyLocation
|
||||
val authenticate = provider.authenticationFunction
|
||||
|
||||
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
|
||||
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
|
||||
val principal = credentials?.let { authenticate(call, it) }
|
||||
|
||||
val cause = when {
|
||||
credentials == null -> AuthenticationFailedCause.NoCredentials
|
||||
@@ -49,9 +59,20 @@ fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String,
|
||||
it.complete()
|
||||
}
|
||||
}
|
||||
|
||||
if (principal != null) {
|
||||
context.principal(principal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
|
||||
val value: String? = when(apiKeyLocation) {
|
||||
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
|
||||
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
|
||||
}
|
||||
when (value) {
|
||||
null -> return null
|
||||
else -> return ApiKeyCredential(value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user