From 219c78069c553afe993fbdad0aa382e684958813 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 26 Nov 2024 10:09:25 +0100 Subject: [PATCH] Upgrade to Ktor Server 3.0.1 Upgrading to Ktor Server 3.0.1 means letting go of the 'principal' parameter to the controller functions. The [Principal interface](https://api.ktor.io/ktor-server/ktor-server-plugins/ktor-server-auth/io.ktor.server.auth/-principal/index.html) is no longer available and thus we cannot provide a common supertype of the different Principals. Instead the user should get the principal with `call.principal

()` in the implementation. --- build.gradle.kts | 2 +- .../servers/ktor/KtorAuthenticationTest.kt | 18 +++++----- .../KtorControllerInterfaceGenerator.kt | 36 ------------------- .../controllers/ktor/Controllers.kt | 32 ++++------------- 4 files changed, 15 insertions(+), 73 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e1692fc3..e6b6817f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,8 +40,8 @@ allprojects { val jacksonVersion by extra { "2.15.1" } val junitVersion by extra { "5.9.2" } -val ktorVersion by extra { "2.3.9" } val kotlinxSerializationVersion by extra { "1.6.3" } +val ktorVersion by extra { "3.0.1" } val kotlinxDateTimeVersion by extra { "0.6.1" } dependencies { diff --git a/end2end-tests/ktor/src/test/kotlin/com/cjbooms/fabrikt/servers/ktor/KtorAuthenticationTest.kt b/end2end-tests/ktor/src/test/kotlin/com/cjbooms/fabrikt/servers/ktor/KtorAuthenticationTest.kt index 97baf059..cd43dac6 100644 --- a/end2end-tests/ktor/src/test/kotlin/com/cjbooms/fabrikt/servers/ktor/KtorAuthenticationTest.kt +++ b/end2end-tests/ktor/src/test/kotlin/com/cjbooms/fabrikt/servers/ktor/KtorAuthenticationTest.kt @@ -41,10 +41,9 @@ class KtorAuthenticationTest { optionalRoutes(object : OptionalController { override suspend fun testPath( testString: String, - principal: Principal?, call: ApplicationCall ) { - principalCaptureSlot.captured = principal as? UserIdPrincipal + principalCaptureSlot.captured = call.principal() call.respond(HttpStatusCode.OK) } }) @@ -70,10 +69,9 @@ class KtorAuthenticationTest { optionalRoutes(object : OptionalController { override suspend fun testPath( testString: String, - principal: Principal?, call: ApplicationCall ) { - principalCaptureSlot.captured = principal as? UserIdPrincipal + principalCaptureSlot.captured = call.principal() call.respond(HttpStatusCode.OK) } }) @@ -97,8 +95,8 @@ class KtorAuthenticationTest { routing { requiredRoutes(object : RequiredController { - override suspend fun testPath(testString: String, principal: Principal, call: ApplicationCall) { - principalCaptureSlot.captured = principal as UserIdPrincipal + override suspend fun testPath(testString: String, call: ApplicationCall) { + principalCaptureSlot.captured = call.principal() call.respond(HttpStatusCode.OK) } }) @@ -122,8 +120,8 @@ class KtorAuthenticationTest { routing { requiredRoutes(object : RequiredController { - override suspend fun testPath(testString: String, principal: Principal, call: ApplicationCall) { - principalCaptureSlot.captured = principal as UserIdPrincipal // should not get called + override suspend fun testPath(testString: String, call: ApplicationCall) { + principalCaptureSlot.captured = call.principal() // should not get called call.respond(HttpStatusCode.OK) } }) @@ -170,8 +168,8 @@ class KtorAuthenticationTest { routing { defaultRoutes(object : DefaultController { - override suspend fun testPath(testString: String, principal: Principal, call: ApplicationCall) { - principalCaptureSlot.captured = principal as UserIdPrincipal + override suspend fun testPath(testString: String, call: ApplicationCall) { + principalCaptureSlot.captured = call.principal() call.respond(HttpStatusCode.OK) } }) diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/KtorControllerInterfaceGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/KtorControllerInterfaceGenerator.kt index 81b29d3b..0c4ce770 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/KtorControllerInterfaceGenerator.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/KtorControllerInterfaceGenerator.kt @@ -136,18 +136,6 @@ class KtorControllerInterfaceGenerator( builder.addParameter(param.toParameterSpecBuilder().build()) } - val securityOption = operation.securitySupport(globalSecurity) - val addAuth = securityOption.allowsAuthenticated && options.contains(ControllerCodeGenOptionType.AUTHENTICATION) - if (addAuth) { - builder.addParameter( - "principal", - ClassName( - "io.ktor.server.auth", - "Principal" - ).copy(nullable = securityOption == SecuritySupport.AUTHENTICATION_OPTIONAL) - ).build() - } - builder.addKdoc(buildControllerFunKdoc(operation, params)) if (operation.happyPathResponse(packages.base).isUnit()) { @@ -204,28 +192,6 @@ class KtorControllerInterfaceGenerator( ) .indent() - if (addAuth) { - if (securityOption == SecuritySupport.AUTHENTICATION_OPTIONAL) { - builder - .addStatement( - "val principal = %M.%M<%T>()", - MemberName("io.ktor.server.application", "call"), - MemberName("io.ktor.server.auth", "principal", isExtension = true), - ClassName("io.ktor.server.auth", "Principal") - ) - } else { - builder - .addStatement( - "val principal = %M.%M<%T>() ?: throw %M(%S)", // should not happen as authenticate { ... } ensures principal is present - MemberName("io.ktor.server.application", "call"), - MemberName("io.ktor.server.auth", "principal", isExtension = true), - ClassName("io.ktor.server.auth", "Principal"), - MemberName("kotlin", "IllegalStateException"), - "Principal not found" - ) - } - } - pathParams.forEach { param -> builder.addStatement( "val ${param.name} = %M.parameters.%M<${param.type}>(\"${param.originalName}\")", @@ -276,8 +242,6 @@ class KtorControllerInterfaceGenerator( val methodParameters = listOf(headerParams, pathParams, queryParams, bodyParams).asSequence().flatten().map { it.name } - .plus(if (addAuth) "principal" else null) - .filterNotNull() .joinToString(", ") if (operation.happyPathResponse(packages.base).isUnit()) { diff --git a/src/test/resources/examples/authentication/controllers/ktor/Controllers.kt b/src/test/resources/examples/authentication/controllers/ktor/Controllers.kt index 64515a2d..4be27da9 100644 --- a/src/test/resources/examples/authentication/controllers/ktor/Controllers.kt +++ b/src/test/resources/examples/authentication/controllers/ktor/Controllers.kt @@ -5,9 +5,7 @@ import io.ktor.http.HttpStatusCode import io.ktor.http.Parameters import io.ktor.server.application.ApplicationCall import io.ktor.server.application.call -import io.ktor.server.auth.Principal import io.ktor.server.auth.authenticate -import io.ktor.server.auth.principal import io.ktor.server.plugins.BadRequestException import io.ktor.server.plugins.ParameterConversionException import io.ktor.server.response.respond @@ -17,7 +15,6 @@ import io.ktor.server.util.getOrFail import io.ktor.util.converters.DefaultConversionService import io.ktor.util.reflect.typeInfo import kotlin.Any -import kotlin.IllegalStateException import kotlin.String import kotlin.Suppress @@ -29,11 +26,7 @@ public interface RequiredController { * @param testString * @param call The Ktor application call */ - public suspend fun testPath( - testString: String, - principal: Principal, - call: ApplicationCall, - ) + public suspend fun testPath(testString: String, call: ApplicationCall) public companion object { /** @@ -44,10 +37,8 @@ public interface RequiredController { public fun Route.requiredRoutes(controller: RequiredController) { authenticate("BasicAuth", "BearerAuth", optional = false) { `get`("/required") { - val principal = call.principal() ?: throw - IllegalStateException("Principal not found") val testString = call.request.queryParameters.getOrFail("testString") - controller.testPath(testString, principal, call) + controller.testPath(testString, call) } } } @@ -151,11 +142,7 @@ public interface OptionalController { * @param testString * @param call The Ktor application call */ - public suspend fun testPath( - testString: String, - principal: Principal?, - call: ApplicationCall, - ) + public suspend fun testPath(testString: String, call: ApplicationCall) public companion object { /** @@ -166,9 +153,8 @@ public interface OptionalController { public fun Route.optionalRoutes(controller: OptionalController) { authenticate("BasicAuth", optional = true) { `get`("/optional") { - val principal = call.principal() val testString = call.request.queryParameters.getOrFail("testString") - controller.testPath(testString, principal, call) + controller.testPath(testString, call) } } } @@ -272,11 +258,7 @@ public interface DefaultController { * @param testString * @param call The Ktor application call */ - public suspend fun testPath( - testString: String, - principal: Principal, - call: ApplicationCall, - ) + public suspend fun testPath(testString: String, call: ApplicationCall) public companion object { /** @@ -287,10 +269,8 @@ public interface DefaultController { public fun Route.defaultRoutes(controller: DefaultController) { authenticate("basicAuth", optional = false) { `get`("/default") { - val principal = call.principal() ?: throw - IllegalStateException("Principal not found") val testString = call.request.queryParameters.getOrFail("testString") - controller.testPath(testString, principal, call) + controller.testPath(testString, call) } } }