From b3957efafb061060af9c68a4bbdda1c3140257d8 Mon Sep 17 00:00:00 2001 From: Jyrki Keisala Date: Wed, 23 Oct 2024 13:23:22 +0300 Subject: [PATCH] fix(api): Fix create user endpoint The User data model includes also first name, last name and an e-mail address, but the request object for creating new users was missing those, in effect preventing to set the corr. attributes when creating new users. Signed-off-by: Jyrki Keisala --- api/v1/model/src/commonMain/kotlin/User.kt | 3 ++ core/src/main/kotlin/api/AdminRoute.kt | 9 +++++- core/src/main/kotlin/apiDocs/AdminDocs.kt | 11 +++++-- .../kotlin/api/AdminRouteIntegrationTest.kt | 32 +++++++++++++++++-- .../src/main/kotlin/UserService.kt | 18 +++++++++-- 5 files changed, 65 insertions(+), 8 deletions(-) diff --git a/api/v1/model/src/commonMain/kotlin/User.kt b/api/v1/model/src/commonMain/kotlin/User.kt index 28e4ef8e1..457dca81b 100644 --- a/api/v1/model/src/commonMain/kotlin/User.kt +++ b/api/v1/model/src/commonMain/kotlin/User.kt @@ -42,6 +42,9 @@ data class User( @Serializable data class CreateUser( val username: String, + val firstName: String? = null, + val lastName: String? = null, + val email: String? = null, val password: String? = null, /** Specifies whether the password is for one-time use only */ diff --git a/core/src/main/kotlin/api/AdminRoute.kt b/core/src/main/kotlin/api/AdminRoute.kt index 3041e8fe1..5a115c378 100644 --- a/core/src/main/kotlin/api/AdminRoute.kt +++ b/core/src/main/kotlin/api/AdminRoute.kt @@ -80,7 +80,14 @@ fun Route.admin() = route("admin") { requireSuperuser() val createUser = call.receive() - userService.createUser(createUser.username, createUser.password, createUser.temporary) + userService.createUser( + username = createUser.username, + firstName = createUser.firstName, + lastName = createUser.lastName, + email = createUser.email, + password = createUser.password, + temporary = createUser.temporary + ) call.respond(HttpStatusCode.Created) } diff --git a/core/src/main/kotlin/apiDocs/AdminDocs.kt b/core/src/main/kotlin/apiDocs/AdminDocs.kt index 4a9377051..8ce7e0af1 100644 --- a/core/src/main/kotlin/apiDocs/AdminDocs.kt +++ b/core/src/main/kotlin/apiDocs/AdminDocs.kt @@ -80,13 +80,20 @@ val getUsers: OpenApiRoute.() -> Unit = { val postUsers: OpenApiRoute.() -> Unit = { operationId = "postUsers" - summary = "Create a user, possibly with a password. This is enabled for server administrators only." + summary = "Create a user, possibly with a password." tags = listOf("Admin") request { jsonBody { example("Create User") { - value = CreateUser(username = "newUser", password = "password", temporary = true) + value = CreateUser( + username = "newUser", + firstName = "First", + lastName = "Last", + email = "first.last@mail.com", + password = "password", + temporary = true + ) description = "temporary=true means the password is for one-time use only and needs to be changed " + "on first login. If password is not set, temporary is ignored." } diff --git a/core/src/test/kotlin/api/AdminRouteIntegrationTest.kt b/core/src/test/kotlin/api/AdminRouteIntegrationTest.kt index 1fc0431c8..516770219 100644 --- a/core/src/test/kotlin/api/AdminRouteIntegrationTest.kt +++ b/core/src/test/kotlin/api/AdminRouteIntegrationTest.kt @@ -44,6 +44,9 @@ class AdminRouteIntegrationTest : AbstractIntegrationTest({ tags(Integration) val testUsername = "test123" + val testFirstName = "FirstName" + val testLastName = "LastName" + val testEmail = "test@test.com" val testPassword = "password123" val testTemporary = true @@ -89,7 +92,14 @@ class AdminRouteIntegrationTest : AbstractIntegrationTest({ "POST /admin/users" should { "create a new user" { integrationTestApplication { - val user = CreateUser(testUsername, testPassword, testTemporary) + val user = CreateUser( + username = testUsername, + firstName = testFirstName, + lastName = testLastName, + email = testEmail, + password = testPassword, + temporary = testTemporary + ) val response = superuserClient.post("/api/v1/admin/users") { setBody(user) @@ -101,7 +111,14 @@ class AdminRouteIntegrationTest : AbstractIntegrationTest({ "respond with an internal error if the user already exists" { integrationTestApplication { - val user = CreateUser(testUsername, testPassword, testTemporary) + val user = CreateUser( + username = testUsername, + firstName = testFirstName, + lastName = testLastName, + email = testEmail, + password = testPassword, + temporary = testTemporary + ) superuserClient.post("/api/v1/admin/users") { setBody(user) @@ -117,7 +134,16 @@ class AdminRouteIntegrationTest : AbstractIntegrationTest({ "require superuser role" { requestShouldRequireRole(Superuser.ROLE_NAME, HttpStatusCode.Created) { post("/api/v1/admin/users") { - setBody(CreateUser(testUsername, testPassword, testTemporary)) + setBody( + CreateUser( + username = testUsername, + firstName = testFirstName, + lastName = testLastName, + email = testEmail, + password = testPassword, + temporary = testTemporary + ) + ) } } } diff --git a/services/authorization/src/main/kotlin/UserService.kt b/services/authorization/src/main/kotlin/UserService.kt index c7ad1c883..8c754225f 100644 --- a/services/authorization/src/main/kotlin/UserService.kt +++ b/services/authorization/src/main/kotlin/UserService.kt @@ -32,8 +32,22 @@ class UserService( /** * Create a user. If "password" is null, then "temporary" is ignored. */ - suspend fun createUser(username: String, password: String?, temporary: Boolean) = run { - keycloakClient.createUser(username = UserName(username), password = password, temporary = temporary) + suspend fun createUser( + username: String, + firstName: String?, + lastName: String?, + email: String?, + password: String?, + temporary: Boolean + ) = run { + keycloakClient.createUser( + username = UserName(username), + firstName = firstName, + lastName = lastName, + email = email, + password = password, + temporary = temporary + ) } /**