Skip to content

Commit

Permalink
Prohibit from using Nexus vocab types in generic resources (#5046)
Browse files Browse the repository at this point in the history
* Restrict from using Nexus vocab types in generic resources

* Address feedback

---------

Co-authored-by: Simon Dumas <[email protected]>
  • Loading branch information
imsdu and Simon Dumas authored Jun 28, 2024
1 parent 4882be8 commit 673a1b6
Show file tree
Hide file tree
Showing 39 changed files with 171 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ final class ResourcesRoutes(
implicit private def resourceFAJsonLdEncoder[A: JsonLdEncoder]: JsonLdEncoder[ResourceF[A]] =
ResourceF.resourceFAJsonLdEncoder(ContextValue.empty)

private val nonGenericResourceCandidate: PartialFunction[ResourceRejection, Boolean] = {
case _: ResourceNotFound | _: InvalidSchemaRejection | _: ReservedResourceTypes => true
}

def routes: Route =
baseUriPrefix(baseUri.prefix) {
pathPrefix("resources") {
Expand All @@ -76,6 +80,7 @@ final class ResourcesRoutes(
.flatTap(index(project, _, mode))
.map(_.void)
.attemptNarrow[ResourceRejection]
.rejectWhen(nonGenericResourceCandidate)
)
}
},
Expand All @@ -93,7 +98,7 @@ final class ResourcesRoutes(
.flatTap(index(project, _, mode))
.map(_.void)
.attemptNarrow[ResourceRejection]
.rejectOn[InvalidSchemaRejection]
.rejectWhen(nonGenericResourceCandidate)
)
}
},
Expand All @@ -114,7 +119,7 @@ final class ResourcesRoutes(
.flatTap(index(project, _, mode))
.map(_.void)
.attemptNarrow[ResourceRejection]
.rejectOn[InvalidSchemaRejection]
.rejectWhen(nonGenericResourceCandidate)
)
case (Some(rev), source, tag) =>
// Update a resource
Expand All @@ -124,7 +129,7 @@ final class ResourcesRoutes(
.flatTap(index(project, _, mode))
.map(_.void)
.attemptNarrow[ResourceRejection]
.rejectWhen { case _: ResourceNotFound | _: InvalidSchemaRejection => true }
.rejectWhen(nonGenericResourceCandidate)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
"value": {
"@context": [
{
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/metadata.json"
],
"@id": "https://bluebrain.github.io/nexus/vocabulary/success",
"@type": "Custom",
"@type": "schema:Custom",
"bool": false,
"name": "Alex",
"number": 24,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
"value": {
"@context": [
{
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/metadata.json"
],
"@id": "https://bluebrain.github.io/nexus/vocabulary/success",
"@type": "Custom",
"@type": "schema:Custom",
"bool": false,
"name": "Alex",
"number": 24,
Expand Down
5 changes: 3 additions & 2 deletions delta/app/src/test/resources/multi-fetch/source-response.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
"project": "org/proj1",
"value": {
"@context": {
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"@id": "https://bluebrain.github.io/nexus/vocabulary/success",
"@type": "Custom",
"@type": "schema:Custom",
"bool": false,
"name": "Alex",
"number": 24
Expand Down
5 changes: 3 additions & 2 deletions delta/app/src/test/resources/resolvers/resource-resolved.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"@context": [
{
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema": "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/metadata.json"
],
"@id": "https://bluebrain.github.io/nexus/vocabulary/resource",
"@type": "Custom",
"@type": "schema:Custom",
"_constrainedBy": "https://bluebrain.github.io/nexus/schemas/unconstrained.json",
"_createdAt": "1970-01-01T00:00:00Z",
"_createdBy": "http://localhost/v1/anonymous",
Expand Down
5 changes: 3 additions & 2 deletions delta/app/src/test/resources/resolvers/schema-resolved.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"@context": [
{
"nxv": "https://bluebrain.github.io/nexus/vocabulary/"
"nxv": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/shacl-20170720.json",
"https://bluebrain.github.io/nexus/contexts/schemas-metadata.json",
Expand Down Expand Up @@ -45,7 +46,7 @@
"path": "nxv:bool"
}
],
"targetClass": "nxv:Custom"
"targetClass": "schema:Custom"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"expanded": {
"@id": "nxv:wrong",
"@type": "Custom",
"@type": "schema:Custom",
"bool": false,
"number": "wrong",
"nxv:name": "Alex"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"https://bluebrain.github.io/nexus/contexts/schemas-metadata.json",
"https://bluebrain.github.io/nexus/contexts/metadata.json",
{
"nxv" : "https://bluebrain.github.io/nexus/vocabulary/"
"nxv" : "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/shacl-20170720.json"
],
Expand Down Expand Up @@ -45,7 +46,7 @@
"path" : "nxv:bool"
}
],
"targetClass" : "nxv:Custom"
"targetClass" : "schema:Custom"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"https://bluebrain.github.io/nexus/contexts/schemas-metadata.json",
"https://bluebrain.github.io/nexus/contexts/metadata.json",
{
"nxv": "https://bluebrain.github.io/nexus/vocabulary/"
"nxv": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/shacl-20170720.json"
],
Expand Down Expand Up @@ -45,7 +46,7 @@
"path": "nxv:bool"
}
],
"targetClass": "nxv:Custom"
"targetClass": "schema:Custom"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import cats.effect.IO
import cats.implicits._
import ch.epfl.bluebrain.nexus.delta.kernel.utils.{UUIDF, UrlUtils}
import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schema, schemas}
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schema => schemaOrg, schemas}
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords
import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ValidateShacl
import ch.epfl.bluebrain.nexus.delta.sdk.IndexingAction
Expand Down Expand Up @@ -54,7 +54,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {
private val asReader = addCredentials(OAuth2BearerToken("reader"))
private val asWriter = addCredentials(OAuth2BearerToken("writer"))

private val am = ApiMappings("nxv" -> nxv.base, "Person" -> schema.Person)
private val am = ApiMappings("nxv" -> nxv.base, "Person" -> schemaOrg.Person)
private val projBase = nxv.base
private val project = ProjectGen.resourceFor(
ProjectGen.project(
Expand All @@ -69,7 +69,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {
private val projectRef = project.value.ref
private val schemaSource = jsonContentOf("resources/schema.json").addContext(contexts.shacl, contexts.schemasMetadata)
private val schema1 = SchemaGen.schema(nxv + "myschema", project.value.ref, schemaSource.removeKeys(keywords.id))
private val schema2 = SchemaGen.schema(schema.Person, project.value.ref, schemaSource.removeKeys(keywords.id))
private val schema2 = SchemaGen.schema(schemaOrg.Person, project.value.ref, schemaSource.removeKeys(keywords.id))
private val schema3 = SchemaGen.schema(nxv + "otherSchema", project.value.ref, schemaSource.removeKeys(keywords.id))
private val tag = UserTag.unsafe("mytag")

Expand Down Expand Up @@ -137,7 +137,8 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {

private val varyHeader = RawHeader("Vary", "Accept,Accept-Encoding")

private val resourceCtx = json"""{"@context": ["${contexts.metadata}", {"@vocab": "${nxv.base}"}]}"""
private val resourceCtx =
json"""{"@context": ["${contexts.metadata}", {"@vocab": "${nxv.base}", "schema" : "${schemaOrg.base}"}]}"""

override def beforeAll(): Unit = {
super.beforeAll()
Expand Down Expand Up @@ -492,7 +493,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {
givenAResource { id =>
Get(s"/v1/resources/myorg/myproject/_/$id") ~> asReader ~> routes ~> check {
status shouldEqual StatusCodes.OK
val meta = standardWriterMetadata(id, tpe = "Custom")
val meta = standardWriterMetadata(id, tpe = "schema:Custom")
response.asJson shouldEqual simplePayload(id).deepMerge(meta).deepMerge(resourceCtx)
response.headers should contain(varyHeader)
}
Expand All @@ -508,7 +509,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {
s"/v1/resources/myorg/myproject/_/$id?rev=1",
s"/v1/resources/myorg/myproject/$mySchema/$id?tag=$myTag"
)
val meta = standardWriterMetadata(id, schema = schema1.id, tpe = "Custom")
val meta = standardWriterMetadata(id, schema = schema1.id, tpe = "schema:Custom")
forAll(endpoints) { endpoint =>
Get(endpoint) ~> asReader ~> routes ~> check {
status shouldEqual StatusCodes.OK
Expand All @@ -534,7 +535,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {
Get(s"/v1/resources/myorg/myproject/_/$id") ~> asReader ~> routes ~> check {
response.asJson shouldEqual
payloadWithNullField(id).dropNullValues
.deepMerge(standardWriterMetadata(id, tpe = "Custom"))
.deepMerge(standardWriterMetadata(id, tpe = "schema:Custom"))
.deepMerge(resourceCtx)
}
}
Expand Down Expand Up @@ -795,7 +796,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues {
rev: Int = 1,
schema: Iri = schemas.resources,
deprecated: Boolean = false,
tpe: String = (nxv + "Custom").toString
tpe: String = (schemaOrg + "Custom").toString
) =
resourceMetadata(
projectRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cats.effect.IO
import cats.syntax.all._
import ch.epfl.bluebrain.nexus.delta.kernel.syntax._
import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.contexts
import ch.epfl.bluebrain.nexus.delta.rdf.shacl.{ValidateShacl, ValidationReport}
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller
Expand All @@ -13,7 +14,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceRe
import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources.kamonComponent
import ch.epfl.bluebrain.nexus.delta.sdk.resources.SchemaClaim.SubmitOnDefinedSchema
import ch.epfl.bluebrain.nexus.delta.sdk.resources.ValidationResult._
import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{InvalidResource, InvalidSchemaRejection, NoTargetedNode, ReservedResourceId, ResourceShaclEngineRejection, SchemaIsDeprecated}
import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection._
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef}

Expand Down Expand Up @@ -63,7 +64,9 @@ object ValidateResource {
enforceSchema: Boolean
): IO[ValidationResult] = {
val submitOnDefinedSchema: SubmitOnDefinedSchema = resolveSchema(_, _, _).flatMap(apply(jsonld, _))
assertNotReservedId(jsonld.id) >> schemaClaim.validate(enforceSchema)(submitOnDefinedSchema)
assertNotReservedId(jsonld.id) >>
assertNotReservedTypes(jsonld.types) >>
schemaClaim.validate(enforceSchema)(submitOnDefinedSchema)
}

def apply(jsonld: JsonLdAssembly, schema: ResourceF[Schema]): IO[ValidationResult] = {
Expand Down Expand Up @@ -96,6 +99,10 @@ object ValidateResource {
IO.raiseWhen(resourceId.startsWith(contexts.base))(ReservedResourceId(resourceId))
}

private def assertNotReservedTypes(types: Set[Iri]) = {
IO.raiseWhen(types.exists(_.startsWith(Vocabulary.nxv.base)))(ReservedResourceTypes(types))
}

private def resolveSchema(project: ProjectRef, schema: ResourceRef, caller: Caller) = {
resourceResolution
.resolve(schema, project)(caller)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import akka.http.scaladsl.model.StatusCodes
import ch.epfl.bluebrain.nexus.delta.kernel.error.Rejection
import ch.epfl.bluebrain.nexus.delta.kernel.utils.ClassUtils
import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.contexts
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.ContextValue
Expand Down Expand Up @@ -80,6 +81,14 @@ object ResourceRejection {
final case class ReservedResourceId(id: Iri)
extends ResourceRejection(s"Resource identifier '$id' is reserved for the platform.")

/**
* Rejection returned when attempting to create/update a resource with a reserved id.
*/
final case class ReservedResourceTypes(types: Set[Iri])
extends ResourceRejection(
s"At least one of the types ${types.mkString("'", "', '", "'")} is starting with ${Vocabulary.nxv.base} which is reserved for non-generic resources."
)

/**
* Rejection returned when attempting to update a resource with an id that doesn't exist.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"@context": [
{
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"https://bluebrain.github.io/nexus/contexts/metadata.json"
],
"@id": "{{id}}",
"@type": "Custom",
"@type": "schema:Custom",
"bool": false,
"name": "Alex",
"number": 24,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"@context": {
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema" : "http://schema.org/"
},
"@id": "{{id}}",
"@type": "Custom",
"@type": "schema:Custom",
"name": "Alex",
"number": 24,
"bool": false,
Expand Down
5 changes: 3 additions & 2 deletions delta/sdk/src/test/resources/resources/resource.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"@context": {
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/",
"schema": "http://schema.org/"
},
"@id": "{{id}}",
"@type": "Custom",
"@type": "schema:Custom",
"name": "Alex",
"number": 24,
"bool": false
Expand Down
5 changes: 3 additions & 2 deletions delta/sdk/src/test/resources/resources/schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"@context": {
"nxv": "https://bluebrain.github.io/nexus/vocabulary/"
"nxv": "https://bluebrain.github.io/nexus/vocabulary/",
"schema": "http://schema.org/"
},
"@type": "Schema",
"@id": "nxv:myschema",
Expand All @@ -9,7 +10,7 @@
"@id": "nxv:MyShape",
"@type": "NodeShape",
"nodeKind": "sh:BlankNodeOrIRI",
"targetClass": "nxv:Custom",
"targetClass": "schema:Custom",
"property": [
{
"@id": "nxv:NameProperty",
Expand Down
Loading

0 comments on commit 673a1b6

Please sign in to comment.