Skip to content

Commit 4215152

Browse files
committed
feat(Authorization): Add an endpoint to check superuser status
This endpoint is going to be used by the UI to adapt itself according to the status of the current user. Make a function of AbstractAuthorizationTest public which can be reused here to access the authorization infrastructure. Signed-off-by: Oliver Heger <[email protected]>
1 parent f91bf1d commit 4215152

File tree

5 files changed

+175
-1
lines changed

5 files changed

+175
-1
lines changed

components/authorization/backend/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ dependencies {
4040
implementation(libs.exposedCore)
4141
implementation(libs.exposedJdbc)
4242

43+
routesImplementation(libs.ktorOpenApi)
44+
4345
testImplementation(testFixtures(projects.dao))
46+
testImplementation(testFixtures(projects.shared.ktorUtils))
4447

4548
testImplementation(ktorLibs.client.contentNegotiation)
4649
testImplementation(ktorLibs.client.core)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Server Authors (See <https://github.com/eclipse-apoapsis/ort-server/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
package org.eclipse.apoapsis.ortserver.components.authorization.routes
21+
22+
import io.ktor.server.routing.Route
23+
24+
import org.eclipse.apoapsis.ortserver.components.authorization.routes.userinfo.getSuperuser
25+
26+
fun Route.authorizationRoutes() {
27+
getSuperuser()
28+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Server Authors (See <https://github.com/eclipse-apoapsis/ort-server/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
package org.eclipse.apoapsis.ortserver.components.authorization.routes.userinfo
21+
22+
import io.ktor.http.HttpStatusCode
23+
import io.ktor.server.application.ApplicationCall
24+
import io.ktor.server.response.respond
25+
import io.ktor.server.routing.Route
26+
27+
import org.eclipse.apoapsis.ortserver.components.authorization.rights.EffectiveRole
28+
import org.eclipse.apoapsis.ortserver.components.authorization.routes.AuthorizationChecker
29+
import org.eclipse.apoapsis.ortserver.components.authorization.routes.OrtServerPrincipal.Companion.requirePrincipal
30+
import org.eclipse.apoapsis.ortserver.components.authorization.routes.get
31+
import org.eclipse.apoapsis.ortserver.components.authorization.service.AuthorizationService
32+
import org.eclipse.apoapsis.ortserver.model.CompoundHierarchyId
33+
34+
internal fun Route.getSuperuser() = get("/authorization/superuser", {
35+
operationId = "getSuperuser"
36+
summary = "Check if the current user is a superuser"
37+
tags = listOf("Authorization")
38+
39+
response {
40+
HttpStatusCode.OK to {
41+
description = "Success"
42+
body<Boolean> {
43+
description = "Whether the current user is a superuser."
44+
}
45+
}
46+
}
47+
}, userInfoChecker()) {
48+
val isSuperuser = requirePrincipal().effectiveRole.isSuperuser
49+
call.respond(HttpStatusCode.OK, isSuperuser.toString())
50+
}
51+
52+
private fun userInfoChecker(): AuthorizationChecker =
53+
object : AuthorizationChecker {
54+
override suspend fun loadEffectiveRole(
55+
service: AuthorizationService,
56+
userId: String,
57+
call: ApplicationCall
58+
): EffectiveRole {
59+
return service.getEffectiveRole(userId, CompoundHierarchyId.WILDCARD)
60+
}
61+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Server Authors (See <https://github.com/eclipse-apoapsis/ort-server/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
package org.eclipse.apoapsis.ortserver.components.authorization.routes.userinfo
21+
22+
import io.kotest.assertions.ktor.client.shouldHaveStatus
23+
import io.kotest.matchers.shouldBe
24+
25+
import io.ktor.client.request.get
26+
import io.ktor.client.statement.bodyAsText
27+
import io.ktor.http.HttpStatusCode
28+
29+
import org.eclipse.apoapsis.ortserver.components.authorization.rights.OrganizationRole
30+
import org.eclipse.apoapsis.ortserver.components.authorization.routes.authorizationRoutes
31+
import org.eclipse.apoapsis.ortserver.components.authorization.service.AuthorizationService
32+
import org.eclipse.apoapsis.ortserver.components.authorization.service.DbAuthorizationService
33+
import org.eclipse.apoapsis.ortserver.model.CompoundHierarchyId
34+
import org.eclipse.apoapsis.ortserver.model.OrganizationId
35+
import org.eclipse.apoapsis.ortserver.shared.ktorutils.AbstractAuthorizationTest
36+
37+
class GetSuperuserTest : AbstractAuthorizationTest({
38+
lateinit var authorizationService: AuthorizationService
39+
40+
beforeEach {
41+
authorizationService = DbAuthorizationService(dbExtension.db)
42+
}
43+
44+
"GET /authorization/superuser" should {
45+
"return 'true' for a superuser" {
46+
authorizationTestApplication(routes = { authorizationRoutes() }) { _, client ->
47+
authorizationService.assignRole(
48+
TEST_USER,
49+
OrganizationRole.ADMIN,
50+
CompoundHierarchyId.WILDCARD
51+
)
52+
53+
val response = client.get("/authorization/superuser")
54+
response shouldHaveStatus HttpStatusCode.OK
55+
response.bodyAsText() shouldBe "true"
56+
}
57+
}
58+
59+
"return 'false' for a normal user" {
60+
authorizationTestApplication(routes = { authorizationRoutes() }) { _, client ->
61+
val orgId = CompoundHierarchyId.forOrganization(OrganizationId(dbExtension.fixtures.organization.id))
62+
authorizationService.assignRole(
63+
TEST_USER,
64+
OrganizationRole.ADMIN,
65+
orgId
66+
)
67+
68+
val response = client.get("/authorization/superuser")
69+
response shouldHaveStatus HttpStatusCode.OK
70+
response.bodyAsText() shouldBe "false"
71+
}
72+
}
73+
74+
"require an authenticated user" {
75+
requestShouldRequireAuthentication({ authorizationRoutes() }) {
76+
get("/authorization/superuser")
77+
}
78+
}
79+
}
80+
})
81+
82+
private const val TEST_USER = "test-user"

shared/ktor-utils/src/testFixtures/kotlin/AbstractAuthorizationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ abstract class AbstractAuthorizationTest(body: AbstractAuthorizationTest.() -> U
111111
authorizationService = DbAuthorizationService(dbExtension.db)
112112
}
113113

114-
private fun authorizationTestApplication(
114+
fun authorizationTestApplication(
115115
routes: Route.() -> Unit,
116116
block: suspend ApplicationTestBuilder.(unauthenticatedClient: HttpClient, testUserClient: HttpClient) -> Unit
117117
) {

0 commit comments

Comments
 (0)