Skip to content

Commit

Permalink
fix: Add prefix handling to admin permission repo (#3290)
Browse files Browse the repository at this point in the history
  • Loading branch information
seakayone authored Jun 19, 2024
1 parent bc07d66 commit 88e6b72
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import zio.ZIO
import zio.ZLayer

import org.knora.webapi.messages.OntologyConstants.KnoraAdmin
import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefix
import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion
import org.knora.webapi.messages.OntologyConstants.KnoraBase
import org.knora.webapi.slice.admin.AdminConstants.permissionsDataNamedGraph
import org.knora.webapi.slice.admin.domain.model.AdministrativePermission
Expand Down Expand Up @@ -125,14 +127,16 @@ object AdministrativePermissionRepoLive {
.andHas(Vocabulary.KnoraAdmin.forProject, Rdf.iri(entity.forProject.value))
.andHas(Vocabulary.KnoraBase.hasPermissions, toStringLiteral(entity.permissions))
}
private def toStringLiteral(permissions: Chunk[AdministrativePermissionPart]): String =
private def toStringLiteral(permissions: Chunk[AdministrativePermissionPart]): String = {
def useKnoraAdminPrefix(str: String) = str.replace(KnoraAdminPrefixExpansion, KnoraAdminPrefix)
permissions.map {
case AdministrativePermissionPart.Simple(permission) => permission.token
case AdministrativePermissionPart.ResourceCreateRestricted(iris) =>
s"${Permission.Administrative.ProjectResourceCreateRestricted.token} ${iris.map(_.value).mkString(",")}"
case AdministrativePermissionPart.ProjectAdminGroupRestricted(groups) =>
s"${Permission.Administrative.ProjectAdminGroupRestricted.token} ${groups.map(_.value).mkString(",")}"
s"${Permission.Administrative.ProjectAdminGroupRestricted.token} ${groups.map(_.value).map(useKnoraAdminPrefix).mkString(",")}"
}.mkString(permissionsDelimiter.toString)
}
}

val layer = ZLayer.succeed(mapper) >>> ZLayer.derive[AdministrativePermissionRepoLive]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package org.knora.webapi.store.triplestore.api
import org.eclipse.rdf4j.sparqlbuilder.core.query.ConstructQuery
import org.eclipse.rdf4j.sparqlbuilder.core.query.InsertDataQuery
import org.eclipse.rdf4j.sparqlbuilder.core.query.ModifyQuery
import org.eclipse.rdf4j.sparqlbuilder.core.query.SelectQuery
import play.twirl.api.TxtFormat
import zio.*

Expand Down Expand Up @@ -57,6 +58,7 @@ trait TriplestoreService {
* @return A [[SparqlSelectResult]].
*/
def query(sparql: Select): Task[SparqlSelectResult]
final def select(sparql: SelectQuery): Task[SparqlSelectResult] = query(Select(sparql))

/**
* Performs a SPARQL update operation, i.e. an INSERT or DELETE query.
Expand Down Expand Up @@ -169,6 +171,7 @@ object TriplestoreService {

case class Select(sparql: String, override val timeout: SparqlTimeout = SparqlTimeout.Standard) extends SparqlQuery
object Select {
def apply(sparql: SelectQuery): Select = Select(sparql.getQueryString)
def apply(sparql: TxtFormat.Appendable): Select = Select(sparql.toString)

def gravsearch(sparql: TxtFormat.Appendable): Select = Select.gravsearch(sparql.toString)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright © 2021 - 2024 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package org.knora.webapi.slice.admin.repo.service

import org.eclipse.rdf4j.sparqlbuilder.core.SparqlBuilder.`var` as variable
import org.eclipse.rdf4j.sparqlbuilder.core.query.Queries
import org.eclipse.rdf4j.sparqlbuilder.core.query.SelectQuery
import org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf
import zio.Chunk
import zio.ZIO
import zio.test.*

import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.slice.admin.domain.model.AdministrativePermission
import org.knora.webapi.slice.admin.domain.model.AdministrativePermissionPart
import org.knora.webapi.slice.admin.domain.model.AdministrativePermissionRepo
import org.knora.webapi.slice.admin.domain.model.GroupIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.Permission.Administrative.*
import org.knora.webapi.slice.admin.domain.model.PermissionIri
import org.knora.webapi.slice.admin.repo.rdf.Vocabulary
import org.knora.webapi.slice.resourceinfo.domain.InternalIri
import org.knora.webapi.store.triplestore.api.TriplestoreService
import org.knora.webapi.store.triplestore.api.TriplestoreServiceInMemory

object AdministrativePermissionRepoLiveSpec extends ZIOSpecDefault {
private val repo = ZIO.serviceWithZIO[AdministrativePermissionRepo]
private val db = ZIO.serviceWithZIO[TriplestoreService]
private val shortcode = Shortcode.unsafeFrom("0001")
private val groupIri = GroupIri.makeNew(shortcode)
private val projectIri = ProjectIri.makeNew
private def permission(permissions: Chunk[AdministrativePermissionPart]) =
AdministrativePermission(PermissionIri.makeNew(shortcode), groupIri, projectIri, permissions)

private val simpleAdminPermission = permission(
Chunk(AdministrativePermissionPart.Simple.unsafeFrom(ProjectResourceCreateAll)),
)
private val complexAdminPermission = permission(
Chunk(
AdministrativePermissionPart.ProjectAdminGroupRestricted(Chunk(groupIri, GroupIri.makeNew(shortcode))),
AdministrativePermissionPart.ResourceCreateRestricted(
Chunk(InternalIri("https://example.org/1"), InternalIri("https://example.org/2")),
),
),
)

val spec = suite("AdministrativePermissionRepoLive")(
test("should save and find") {
for {
saved <- repo(_.save(simpleAdminPermission))
found <- repo(_.findById(saved.id))
} yield assertTrue(found.contains(saved), saved == simpleAdminPermission)
},
test("should handle complex permission parts") {
val expected = complexAdminPermission
for {
saved <- repo(_.save(expected))
found <- repo(_.findById(saved.id))
} yield assertTrue(found.contains(saved), saved == expected)
},
test("should delete") {
val expected = complexAdminPermission
for {
saved <- repo(_.save(expected))
foundAfterSave <- repo(_.findById(saved.id)).map(_.nonEmpty)
_ <- repo(_.delete(expected))
notfoundAfterDelete <- repo(_.findById(saved.id)).map(_.isEmpty)
} yield assertTrue(foundAfterSave, notfoundAfterDelete)
},
test("should write valid permission literal with the 'knora-admin:' prefix") {
val expected = permission(
Chunk(
AdministrativePermissionPart.Simple.unsafeFrom(ProjectResourceCreateAll),
AdministrativePermissionPart.ProjectAdminGroupRestricted(
Chunk(
GroupIri.unsafeFrom(KnoraAdminPrefixExpansion + "Creator"),
GroupIri.unsafeFrom(KnoraAdminPrefixExpansion + "UnknownUser"),
groupIri,
),
),
),
)
def query(id: PermissionIri): SelectQuery = {
val hasPermissionsLiteral = variable("lit")
Queries
.SELECT()
.select(hasPermissionsLiteral)
.where(Rdf.iri(id.value).has(Vocabulary.KnoraBase.hasPermissions, hasPermissionsLiteral))
}
for {
saved <- repo(_.save(expected))
res <- db(_.select(query(saved.id)))
} yield assertTrue(
res
.getFirst("lit")
.head == s"ProjectResourceCreateAllPermission|ProjectAdminGroupRestrictedPermission knora-admin:Creator,knora-admin:UnknownUser,${groupIri.value}",
)
},
).provide(AdministrativePermissionRepoLive.layer, TriplestoreServiceInMemory.emptyLayer, StringFormatter.test)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright © 2021 - 2024 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package org.knora.webapi.slice.admin.repo.service

import org.eclipse.rdf4j.sparqlbuilder.core.SparqlBuilder.`var` as variable
import org.eclipse.rdf4j.sparqlbuilder.core.query.Queries
import org.eclipse.rdf4j.sparqlbuilder.core.query.SelectQuery
import org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf
import zio.Chunk
import zio.NonEmptyChunk
import zio.ZIO
import zio.test.*

import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.slice.admin.domain.model.DefaultObjectAccessPermission
import org.knora.webapi.slice.admin.domain.model.DefaultObjectAccessPermission.DefaultObjectAccessPermissionPart
import org.knora.webapi.slice.admin.domain.model.DefaultObjectAccessPermission.ForWhat
import org.knora.webapi.slice.admin.domain.model.DefaultObjectAccessPermissionRepo
import org.knora.webapi.slice.admin.domain.model.GroupIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.Permission.ObjectAccess.*
import org.knora.webapi.slice.admin.domain.model.PermissionIri
import org.knora.webapi.slice.admin.repo.rdf.Vocabulary
import org.knora.webapi.slice.resourceinfo.domain.InternalIri
import org.knora.webapi.store.triplestore.api.TriplestoreService
import org.knora.webapi.store.triplestore.api.TriplestoreServiceInMemory

object DefaultObjectAccessPermissionRepoLiveSpec extends ZIOSpecDefault {
private val repo = ZIO.serviceWithZIO[DefaultObjectAccessPermissionRepo]
private val db = ZIO.serviceWithZIO[TriplestoreService]
private val shortcode = Shortcode.unsafeFrom("0001")
private val groupIri = GroupIri.makeNew(shortcode)
private val projectIri = ProjectIri.makeNew

private def permission(forWhat: ForWhat, permissions: Chunk[DefaultObjectAccessPermissionPart]) =
DefaultObjectAccessPermission(PermissionIri.makeNew(shortcode), projectIri, forWhat, permissions)

private val expected = permission(
ForWhat.ResourceClassAndProperty(InternalIri("https://example.com/rc"), InternalIri("https://example.com/p")),
Chunk(
DefaultObjectAccessPermissionPart(RestrictedView, NonEmptyChunk(groupIri)),
DefaultObjectAccessPermissionPart(View, NonEmptyChunk(GroupIri.makeNew(shortcode), GroupIri.makeNew(shortcode))),
),
)

val spec = suite("AdministrativePermissionRepoLive")(
test("should save and find") {
for {
saved <- repo(_.save(expected))
found <- repo(_.findById(saved.id))
} yield assertTrue(found.contains(saved), saved == expected)
},
test("should delete") {
for {
saved <- repo(_.save(expected))
foundAfterSave <- repo(_.findById(saved.id)).map(_.nonEmpty)
_ <- repo(_.delete(expected))
notfoundAfterDelete <- repo(_.findById(saved.id)).map(_.isEmpty)
} yield assertTrue(foundAfterSave, notfoundAfterDelete)
},
test("should write valid permission literal with the 'knora-admin:' prefix") {
val expected = permission(
ForWhat.Group(groupIri),
Chunk(
DefaultObjectAccessPermissionPart(RestrictedView, NonEmptyChunk(groupIri)),
DefaultObjectAccessPermissionPart(
View,
NonEmptyChunk(
GroupIri.unsafeFrom(KnoraAdminPrefixExpansion + "Creator"),
GroupIri.unsafeFrom(KnoraAdminPrefixExpansion + "UnknownUser"),
),
),
),
)
def query(id: PermissionIri): SelectQuery = {
val hasPermissionsLiteral = variable("lit")
Queries
.SELECT()
.select(hasPermissionsLiteral)
.where(Rdf.iri(id.value).has(Vocabulary.KnoraBase.hasPermissions, hasPermissionsLiteral))
}
for {
saved <- repo(_.save(expected))
res <- db(_.select(query(saved.id)))
} yield assertTrue(
res
.getFirst("lit")
.head == s"RV ${groupIri.value}|V knora-admin:Creator,knora-admin:UnknownUser",
)
},
).provide(DefaultObjectAccessPermissionRepoLive.layer, TriplestoreServiceInMemory.emptyLayer, StringFormatter.test)
}

0 comments on commit 88e6b72

Please sign in to comment.