Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api): Fix users endpoints #1278

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1/model/src/commonMain/kotlin/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
9 changes: 8 additions & 1 deletion core/src/main/kotlin/api/AdminRoute.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ fun Route.admin() = route("admin") {
requireSuperuser()

val createUser = call.receive<CreateUser>()
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)
}
Expand Down
30 changes: 28 additions & 2 deletions core/src/main/kotlin/apiDocs/AdminDocs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute
import io.ktor.http.HttpStatusCode

import org.eclipse.apoapsis.ortserver.api.v1.model.CreateUser
import org.eclipse.apoapsis.ortserver.api.v1.model.User

val runPermissionsSync: OpenApiRoute.() -> Unit = {
operationId = "runPermissionsSync"
Expand Down Expand Up @@ -55,19 +56,44 @@ val getUsers: OpenApiRoute.() -> Unit = {
response {
HttpStatusCode.OK to {
description = "Successfully retrieved the users."
jsonBody<List<User>> {
example("Get all users of the server") {
value = listOf(
User(
username = "user1",
firstName = "First1",
lastName = "Last1",
email = "[email protected]"
),
User(
username = "user2",
firstName = "First2",
lastName = "Last2",
email = "[email protected]"
)
)
}
}
}
}
}

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<CreateUser> {
example("Create User") {
value = CreateUser(username = "newUser", password = "password", temporary = true)
value = CreateUser(
username = "newUser",
firstName = "First",
lastName = "Last",
email = "[email protected]",
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."
}
Expand Down
32 changes: 29 additions & 3 deletions core/src/test/kotlin/api/AdminRouteIntegrationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class AdminRouteIntegrationTest : AbstractIntegrationTest({
tags(Integration)

val testUsername = "test123"
val testFirstName = "FirstName"
val testLastName = "LastName"
val testEmail = "[email protected]"
val testPassword = "password123"
val testTemporary = true

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
)
)
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions services/authorization/src/main/kotlin/UserService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}

/**
Expand Down