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: Add prefix handling to admin permission repo #3290

Merged
merged 12 commits into from
Jun 19, 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
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)
}
Loading