Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(OntologiesRouteV2): Reject internal ontology names in external sc…
…hema (DSP-1394) (#1827)
  • Loading branch information
Benjamin Geer committed Mar 11, 2021
1 parent c934bdd commit e392bf1
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 15 deletions.
Expand Up @@ -158,6 +158,14 @@ object OntologyConstants {
OntologyConstants.Rdfs.Label -> OntologyConstants.Xsd.String
)

/**
* Ontology labels that are used only in the internal schema.
*/
val InternalOntologyLabels: Set[String] = Set(
KnoraBase.KnoraBaseOntologyLabel,
KnoraAdmin.KnoraAdminOntologyLabel
)

/**
* Ontology labels that are reserved for built-in ontologies.
*/
Expand Down
Expand Up @@ -3236,4 +3236,16 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No

customValueIri
}

/**
* Throws [[BadRequestException]] if a Knora API v2 definition API has an ontology name that can only be used
* in the internal schema.
*
* @param iri the IRI to be checked.
*/
def checkExternalOntologyName(iri: SmartIri): Unit = {
if (iri.isKnoraApiV2DefinitionIri && OntologyConstants.InternalOntologyLabels.contains(iri.getOntologyName)) {
throw BadRequestException(s"Internal ontology <$iri> cannot be served")
}
}
}
Expand Up @@ -93,6 +93,8 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
val requestedOntology = requestedOntologyStr.toSmartIriWithErr(
throw BadRequestException(s"Invalid ontology IRI: $requestedOntologyStr"))

stringFormatter.checkExternalOntologyName(requestedOntology)

val targetSchema = requestedOntology.getOntologySchema match {
case Some(apiV2Schema: ApiV2Schema) => apiV2Schema
case _ => throw BadRequestException(s"Invalid ontology IRI: $requestedOntologyStr")
Expand Down Expand Up @@ -163,14 +165,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
put {
entity(as[String]) { jsonRequest => requestContext =>
{

val requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

val requestMessageFuture: Future[ChangeOntologyMetadataRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestMessage: ChangeOntologyMetadataRequestV2 <- ChangeOntologyMetadataRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -208,6 +210,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

validatedProjectIris = projectIris
.map(iri => iri.toSmartIriWithErr(throw BadRequestException(s"Invalid project IRI: $iri")))
.toSet
Expand Down Expand Up @@ -235,6 +238,8 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
val requestedOntologyIri = externalOntologyIriStr.toSmartIriWithErr(
throw BadRequestException(s"Invalid ontology IRI: $externalOntologyIriStr"))

stringFormatter.checkExternalOntologyName(requestedOntologyIri)

val targetSchema = requestedOntologyIri.getOntologySchema match {
case Some(apiV2Schema: ApiV2Schema) => apiV2Schema
case _ => throw BadRequestException(s"Invalid ontology IRI: $externalOntologyIriStr")
Expand Down Expand Up @@ -277,13 +282,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
// Create a new class.
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[CreateClassRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage: CreateClassRequestV2 <- CreateClassRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -316,13 +322,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
// Change the labels or comments of a class.
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[ChangeClassLabelsOrCommentsRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage <- ChangeClassLabelsOrCommentsRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -356,13 +363,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
// Add cardinalities to a class.
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[AddCardinalitiesToClassRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage: AddCardinalitiesToClassRequestV2 <- AddCardinalitiesToClassRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -396,13 +404,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
// Change a class's cardinalities.
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[ChangeCardinalitiesRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage: ChangeCardinalitiesRequestV2 <- ChangeCardinalitiesRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -434,11 +443,12 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
path(OntologiesBasePath / "classes" / Segments) { externalResourceClassIris: List[IRI] =>
get { requestContext =>
{

val classesAndSchemas: Set[(SmartIri, ApiV2Schema)] = externalResourceClassIris.map { classIriStr: IRI =>
val requestedClassIri: SmartIri =
classIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid class IRI: $classIriStr"))

stringFormatter.checkExternalOntologyName(requestedClassIri)

if (!requestedClassIri.isKnoraApiV2EntityIri) {
throw BadRequestException(s"Invalid class IRI: $classIriStr")
}
Expand Down Expand Up @@ -508,6 +518,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
}

val classIri = classIriStr.toSmartIri
stringFormatter.checkExternalOntologyName(classIri)

if (!classIri.getOntologySchema.contains(ApiV2Complex)) {
throw BadRequestException(s"Invalid class IRI for request: $classIriStr")
Expand Down Expand Up @@ -555,13 +566,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
// Create a new property.
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[CreatePropertyRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage: CreatePropertyRequestV2 <- CreatePropertyRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -595,13 +607,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
// Change the labels or comments of a property.
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[ChangePropertyLabelsOrCommentsRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage: ChangePropertyLabelsOrCommentsRequestV2 <- ChangePropertyLabelsOrCommentsRequestV2
.fromJsonLD(
jsonLDDocument = requestDoc,
Expand Down Expand Up @@ -634,11 +647,12 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
path(OntologiesBasePath / "properties" / Segments) { externalPropertyIris: List[IRI] =>
get { requestContext =>
{

val propsAndSchemas: Set[(SmartIri, ApiV2Schema)] = externalPropertyIris.map { propIriStr: IRI =>
val requestedPropIri: SmartIri =
propIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: $propIriStr"))

stringFormatter.checkExternalOntologyName(requestedPropIri)

if (!requestedPropIri.isKnoraApiV2EntityIri) {
throw BadRequestException(s"Invalid property IRI: $propIriStr")
}
Expand Down Expand Up @@ -701,13 +715,13 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
path(OntologiesBasePath / "properties" / Segments) { externalPropertyIris: List[IRI] =>
delete { requestContext =>
{

val propertyIriStr = externalPropertyIris match {
case List(str) => str
case _ => throw BadRequestException(s"Only one property can be deleted at a time")
}

val propertyIri = propertyIriStr.toSmartIri
stringFormatter.checkExternalOntologyName(propertyIri)

if (!propertyIri.getOntologySchema.contains(ApiV2Complex)) {
throw BadRequestException(s"Invalid property IRI for request: $propertyIri")
Expand Down Expand Up @@ -754,13 +768,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
post {
entity(as[String]) { jsonRequest => requestContext =>
{

val requestMessageFuture: Future[CreateOntologyRequestV2] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
featureFactoryConfig = featureFactoryConfig
)

requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

requestMessage: CreateOntologyRequestV2 <- CreateOntologyRequestV2.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
Expand Down Expand Up @@ -792,8 +807,8 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
ontologyIriStr =>
delete { requestContext =>
{

val ontologyIri = ontologyIriStr.toSmartIri
stringFormatter.checkExternalOntologyName(ontologyIri)

if (!ontologyIri.isKnoraOntologyIri || ontologyIri.isKnoraBuiltInDefinitionIri || !ontologyIri.getOntologySchema
.contains(ApiV2Complex)) {
Expand Down
@@ -1,7 +1,6 @@
package org.knora.webapi.e2e.v2

import java.nio.file.{Path, Paths, Files}

import java.nio.file.{Files, Path, Paths}
import java.net.URLEncoder
import java.time.Instant

Expand All @@ -12,6 +11,7 @@ import akka.http.scaladsl.testkit.RouteTestTimeout
import org.knora.webapi._
import org.knora.webapi.e2e.{ClientTestDataCollector, TestDataFileContent, TestDataFilePath}
import org.knora.webapi.exceptions.AssertionException
import org.knora.webapi.http.directives.DSPApiDirectives
import org.knora.webapi.messages.IriConversions._
import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject
import org.knora.webapi.messages.util.rdf._
Expand Down Expand Up @@ -49,7 +49,7 @@ class OntologyV2R2RSpec extends R2RSpec {

private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance

private val ontologiesPath = new OntologiesRouteV2(routeData).knoraApiPath
private val ontologiesPath = DSPApiDirectives.handleErrors(system) { new OntologiesRouteV2(routeData).knoraApiPath }

implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout)

Expand Down Expand Up @@ -384,6 +384,20 @@ class OntologyV2R2RSpec extends R2RSpec {
}
}

"not allow the user to request the knora-base ontology" in {
Get("/v2/ontologies/allentities/http%3A%2F%2Fapi.knora.org%2Fontology%2Fknora-base%2Fv2") ~> ontologiesPath ~> check {
val responseStr: String = responseAs[String]
assert(response.status == StatusCodes.BadRequest, responseStr)
}
}

"not allow the user to request the knora-admin ontology" in {
Get("/v2/ontologies/allentities/http%3A%2F%2Fapi.knora.org%2Fontology%2Fknora-admin%2Fv2") ~> ontologiesPath ~> check {
val responseStr: String = responseAs[String]
assert(response.status == StatusCodes.BadRequest, responseStr)
}
}

"create an empty ontology called 'foo' with a project code" in {
val label = "The foo ontology"

Expand Down

0 comments on commit e392bf1

Please sign in to comment.