Skip to content

Commit

Permalink
fix: Only allow System Administrators to create users (#3022)
Browse files Browse the repository at this point in the history
  • Loading branch information
seakayone committed Feb 5, 2024
1 parent 34fd51a commit 5ab6e35
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 10 deletions.
Expand Up @@ -296,6 +296,27 @@ class UsersADME2ESpec
}

"given a custom Iri" should {
"given no credentials in the request when creating a user it must be forbidden" in {
val validUserCreationRequest: String =
s"""{
| "id": "$customUserIri",
| "username": "userWithCustomIri",
| "email": "userWithCustomIri@example.org",
| "givenName": "a user",
| "familyName": "with a custom Iri",
| "password": "test",
| "status": true,
| "lang": "en",
| "systemAdmin": false
|}""".stripMargin
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, validUserCreationRequest)
)
val response: HttpResponse = singleAwaitingRequest(request)
response.status should be(StatusCodes.Forbidden)
}

"create a user with the provided custom IRI" in {
val createUserWithCustomIriRequest: String =
s"""{
Expand Down Expand Up @@ -323,7 +344,7 @@ class UsersADME2ESpec
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, createUserWithCustomIriRequest)
)
) ~> addRootUserCredentials()
val response: HttpResponse = singleAwaitingRequest(request)

response.status should be(StatusCodes.OK)
Expand Down Expand Up @@ -359,7 +380,10 @@ class UsersADME2ESpec
| "systemAdmin": false
|}""".stripMargin

val request = Post(baseApiUrl + s"/admin/users", HttpEntity(ContentTypes.`application/json`, params))
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, params)
) ~> addRootUserCredentials()
val response: HttpResponse = singleAwaitingRequest(request)
response.status should be(StatusCodes.BadRequest)

Expand Down Expand Up @@ -389,7 +413,7 @@ class UsersADME2ESpec
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, createUserWithApostropheRequest)
)
) ~> addRootUserCredentials()
val response: HttpResponse = singleAwaitingRequest(request)

response.status should be(StatusCodes.OK)
Expand Down Expand Up @@ -463,7 +487,11 @@ class UsersADME2ESpec
text = createUserRequest
)
)
val request = Post(baseApiUrl + s"/admin/users", HttpEntity(ContentTypes.`application/json`, createUserRequest))
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, createUserRequest)
) ~> addRootUserCredentials()

val response: HttpResponse = singleAwaitingRequest(request)

response.status should be(StatusCodes.OK)
Expand Down Expand Up @@ -513,7 +541,10 @@ class UsersADME2ESpec
text = createUserRequest
)
)
val request = Post(baseApiUrl + s"/admin/users", HttpEntity(ContentTypes.`application/json`, createUserRequest))
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, createUserRequest)
) ~> addRootUserCredentials()
val response: HttpResponse = singleAwaitingRequest(request)

response.status should be(StatusCodes.BadRequest)
Expand Down Expand Up @@ -553,7 +584,10 @@ class UsersADME2ESpec
text = createUserRequest
)
)
val request = Post(baseApiUrl + s"/admin/users", HttpEntity(ContentTypes.`application/json`, createUserRequest))
val request = Post(
baseApiUrl + s"/admin/users",
HttpEntity(ContentTypes.`application/json`, createUserRequest)
) ~> addRootUserCredentials()
val response: HttpResponse = singleAwaitingRequest(request)

response.status should be(StatusCodes.BadRequest)
Expand Down
Expand Up @@ -31,6 +31,7 @@ import org.knora.webapi.slice.admin.api.AdminApiRoutes
import org.knora.webapi.slice.admin.api.ProjectsEndpointsHandler
import org.knora.webapi.slice.admin.api.service.ProjectADMRestService
import org.knora.webapi.slice.admin.domain.service.KnoraProjectRepo
import org.knora.webapi.slice.common.api.AuthorizationRestService
import org.knora.webapi.slice.ontology.api.service.RestCardinalityService
import org.knora.webapi.slice.resourceinfo.api.ResourceInfoRoutes
import org.knora.webapi.slice.resourceinfo.api.service.RestResourceInfoService
Expand All @@ -48,7 +49,7 @@ object ApiRoutes {
* All routes composed together.
*/
val layer: URLayer[
ActorSystem & AdminApiRoutes & AppConfig & AppRouter & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & ProjectsEndpointsHandler & ResourceInfoRoutes & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UsersResponderADM & ValuesResponderV2,
ActorSystem & AuthorizationRestService & AdminApiRoutes & AppConfig & AppRouter & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & ProjectsEndpointsHandler & ResourceInfoRoutes & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UsersResponderADM & ValuesResponderV2,
ApiRoutes
] =
ZLayer {
Expand All @@ -62,7 +63,7 @@ object ApiRoutes {
routeData <- ZIO.succeed(KnoraRouteData(sys.system, router.ref, appConfig))
runtime <-
ZIO.runtime[
AppConfig & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UsersResponderADM & ValuesResponderV2
AppConfig & AuthorizationRestService & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UsersResponderADM & ValuesResponderV2
]
} yield ApiRoutesImpl(routeData, adminApiRoutes, resourceInfoRoutes, searchApiRoutes, appConfig, runtime)
}
Expand All @@ -82,7 +83,7 @@ private final case class ApiRoutesImpl(
searchApiRoutes: SearchApiRoutes,
appConfig: AppConfig,
implicit val runtime: Runtime[
AppConfig & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchResponderV2 & SipiService & StringFormatter & UsersResponderADM & ValuesResponderV2
AppConfig & AuthorizationRestService & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchResponderV2 & SipiService & StringFormatter & UsersResponderADM & ValuesResponderV2
]
) extends ApiRoutes
with AroundDirectives {
Expand Down
Expand Up @@ -23,12 +23,13 @@ import org.knora.webapi.routing.RouteUtilADM.getIriUserUuid
import org.knora.webapi.routing.RouteUtilADM.getUserUuid
import org.knora.webapi.routing.RouteUtilADM.runJsonRouteZ
import org.knora.webapi.slice.admin.domain.model.*
import org.knora.webapi.slice.common.api.AuthorizationRestService

/**
* Provides an pekko-http-routing function for API routes that deal with users.
*/
final case class UsersRouteADM()(
private implicit val runtime: Runtime[Authenticator & StringFormatter & MessageRelay]
private implicit val runtime: Runtime[Authenticator & AuthorizationRestService & StringFormatter & MessageRelay]
) {

private val usersBasePath: PathMatcher[Unit] = PathMatcher("admin" / "users")
Expand Down Expand Up @@ -58,6 +59,7 @@ final case class UsersRouteADM()(
val requestTask = for {
payload <- UserCreatePayloadADM.make(apiRequest).mapError(BadRequestException(_)).toZIO
r <- getUserUuid(ctx)
_ <- AuthorizationRestService.ensureSystemAdmin(r.user)
} yield UserCreateRequestADM(payload, r.user, r.uuid)
runJsonRouteZ(requestTask, ctx)
}
Expand Down

0 comments on commit 5ab6e35

Please sign in to comment.