diff --git a/dsp-shared/src/main/scala/dsp/valueobjects/Iri.scala b/dsp-shared/src/main/scala/dsp/valueobjects/Iri.scala index 1aaa3b7e5b..f9850e1611 100644 --- a/dsp-shared/src/main/scala/dsp/valueobjects/Iri.scala +++ b/dsp-shared/src/main/scala/dsp/valueobjects/Iri.scala @@ -49,7 +49,7 @@ object Iri { if (!V2IriValidation.isKnoraGroupIriStr(value)) { Validation.fail(BadRequestException(IriErrorMessages.GroupIriInvalid)) - } else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) { + } else if (isUuid && !V2UuidValidation.isUuidSupported(value)) { Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid)) } else { val validatedValue = Validation( @@ -80,7 +80,7 @@ object Iri { if (!V2IriValidation.isKnoraListIriStr(value)) { Validation.fail(BadRequestException(IriErrorMessages.ListIriInvalid)) - } else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) { + } else if (isUuid && !V2UuidValidation.isUuidSupported(value)) { Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid)) } else { val validatedValue = Validation( @@ -112,7 +112,7 @@ object Iri { } else { if (!V2IriValidation.isKnoraProjectIriStr(value)) { Validation.fail(ValidationException(IriErrorMessages.ProjectIriInvalid)) - } else if (!V2IriValidation.isKnoraBuiltInProjectIriStr(value) && !V2UuidValidation.isUuidVersion4Or5(value)) { + } else if (!V2IriValidation.isKnoraBuiltInProjectIriStr(value) && !V2UuidValidation.isUuidSupported(value)) { Validation.fail(ValidationException(IriErrorMessages.UuidVersionInvalid)) } else { val eitherValue = Try( @@ -147,7 +147,7 @@ object Iri { Validation.fail(ValidationException(IriErrorMessages.UuidMissing)) } else if (!V2UuidValidation.hasUuidLength(value)) { Validation.fail(ValidationException(IriErrorMessages.UuidInvalid(value))) - } else if (!V2UuidValidation.isUuidVersion4Or5(value)) { + } else if (!V2UuidValidation.isUuidSupported(value)) { Validation.fail(ValidationException(IriErrorMessages.UuidVersionInvalid)) } else Validation.succeed(new Base64Uuid(value) {}) } @@ -165,7 +165,7 @@ object Iri { if (!V2IriValidation.isKnoraRoleIriStr(value)) { Validation.fail(BadRequestException(IriErrorMessages.RoleIriInvalid(value))) - } else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) { + } else if (isUuid && !V2UuidValidation.isUuidSupported(value)) { Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid)) } else { val validatedValue = Validation( @@ -198,7 +198,7 @@ object Iri { if (!V2IriValidation.isKnoraUserIriStr(value)) { Validation.fail(BadRequestException(IriErrorMessages.UserIriInvalid(value))) - } else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) { + } else if (isUuid && !V2UuidValidation.isUuidSupported(value)) { Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid)) } else { val validatedValue = Validation( diff --git a/dsp-shared/src/main/scala/dsp/valueobjects/V2.scala b/dsp-shared/src/main/scala/dsp/valueobjects/V2.scala index 59574a87d5..c6afce6ba0 100644 --- a/dsp-shared/src/main/scala/dsp/valueobjects/V2.scala +++ b/dsp-shared/src/main/scala/dsp/valueobjects/V2.scala @@ -217,15 +217,20 @@ object V2UuidValidation { s.length == CanonicalUuidLength || s.length == Base64UuidLength /** - * Checks if UUID used to create IRI has correct version (4 and 5 are allowed). + * Checks if UUID used to create IRI has supported version (4 and 5 are allowed). + * With an exception of BEOL project IRI `http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF`. + * * @param s the string (IRI) to be checked. - * @return TRUE for correct versions, FALSE for incorrect. + * @return TRUE for supported versions, FALSE for not supported. */ - def isUuidVersion4Or5(s: String): Boolean = - getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5 + def isUuidSupported(s: String): Boolean = + if (s != "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF") { + getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5 + } else true /** * Decodes Base64 encoded UUID and gets its version. + * * @param uuid the Base64 encoded UUID as [[String]] to be checked. * @return UUID version. */ @@ -236,6 +241,7 @@ object V2UuidValidation { /** * Gets the last IRI segment - Base64 encoded UUID. + * * @param iri the IRI [[String]] to get the UUID from. * @return Base64 encoded UUID as [[String]] */ diff --git a/dsp-shared/src/test/scala/dsp/valueobjects/IriSpec.scala b/dsp-shared/src/test/scala/dsp/valueobjects/IriSpec.scala index 4fc99826bf..164000eb93 100644 --- a/dsp-shared/src/test/scala/dsp/valueobjects/IriSpec.scala +++ b/dsp-shared/src/test/scala/dsp/valueobjects/IriSpec.scala @@ -27,6 +27,7 @@ object IriSpec extends ZIOSpecDefault { val invalidProjectIri = "http://rdfh.ch/projects/0001" val validProjectIri = "http://rdfh.ch/projects/CwQ8hXF9Qlm1gl2QE6pTpg" + val beolProjectIri = "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF" val projectIriWithUUIDVersion3 = "http://rdfh.ch/projects/tZjZhGSZMeCLA5VeUmwAmg" val builtInProjectIri = "http://www.knora.org/ontology/knora-admin#SystemProject" @@ -183,9 +184,11 @@ object IriSpec extends ZIOSpecDefault { (for { iri <- makeProjectIri(validProjectIri) iri2 <- makeProjectIri(builtInProjectIri) + beolIri <- makeProjectIri(beolProjectIri) maybeIri <- maybeProjectIri } yield assertTrue(iri.value == validProjectIri) && assertTrue(iri2.value == builtInProjectIri) && + assertTrue(beolIri.value == beolProjectIri) && assert(maybeIri)(isSome(equalTo(iri)))).toZIO }, test("successfully validate passing None") { @@ -199,7 +202,7 @@ object IriSpec extends ZIOSpecDefault { test("pass an empty value and return an error") { assertTrue(Base64Uuid.make("") == Validation.fail(ValidationException(IriErrorMessages.UuidMissing))) }, - test("pass an invalid UUID and return an error") { + test("pass an invalid UUID and return an error") { assertTrue( Base64Uuid.make(invalidIri) == Validation.fail(ValidationException(IriErrorMessages.UuidInvalid(invalidIri))) ) diff --git a/webapi/src/it/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala b/webapi/src/it/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala index f665fba4ef..94c01ce330 100644 --- a/webapi/src/it/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala @@ -264,6 +264,27 @@ class CreateListItemsRouteADME2ESpec newListIri.set(listInfo.id) } + "create a list using bad project IRI, created in the old way with bad UUID version" in { + val createListRequest: String = + s"""{ + | "projectIri": "${SharedTestDataADM.BEOL_PROJECT_IRI}", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [{ "value": "XXXXX", "language": "en"}] + |}""".stripMargin + + val request = Post( + baseApiUrl + s"/admin/lists", + HttpEntity(ContentTypes.`application/json`, createListRequest) + ) ~> addCredentials(beolAdminUserCreds.basicHttpCredentials) + val response: HttpResponse = singleAwaitingRequest(request) + response.status should be(StatusCodes.OK) + + val receivedList: ListADM = AkkaHttpUtils.httpResponseToJson(response).fields("list").convertTo[ListADM] + + val listInfo = receivedList.listinfo + listInfo.projectIri should be(SharedTestDataADM.BEOL_PROJECT_IRI) + } + "return a ForbiddenException if the user creating the list is not project or system admin" in { val params = s""" diff --git a/webapi/src/it/scala/org/knora/webapi/messages/StringFormatterSpec.scala b/webapi/src/it/scala/org/knora/webapi/messages/StringFormatterSpec.scala index 741436e8b6..a6699e2586 100644 --- a/webapi/src/it/scala/org/knora/webapi/messages/StringFormatterSpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/messages/StringFormatterSpec.scala @@ -1259,29 +1259,21 @@ class StringFormatterSpec extends CoreSpec { uuid should be(base4DecodedUuid) } - "return TRUE if IRI contains UUID version 4 or 5, otherwise return FALSE" in { - val iri3 = "http://rdfh.ch/0000/rKAU0FNjPUKWqOT8MEW_UQ" - val iri4 = "http://rdfh.ch/0001/cmfk1DMHRBiR4-_6HXpEFA" - val iri5 = "http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q" + "return TRUE for IRIs BEOL project IRI and other which contain UUID version 4 or 5, otherwise return FALSE" in { + val iri3 = "http://rdfh.ch/0000/rKAU0FNjPUKWqOT8MEW_UQ" + val iri4 = "http://rdfh.ch/0001/cmfk1DMHRBiR4-_6HXpEFA" + val iri5 = "http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q" + val beolIri = "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF" - val testIRIFromVersion3UUID = stringFormatter.isUuidVersion4Or5(iri3) - val testIRIFromVersion4UUID = stringFormatter.isUuidVersion4Or5(iri4) - val testIRIFromVersion5UUID = stringFormatter.isUuidVersion4Or5(iri5) - - val iri = "http://rdfh.ch/0001/rYAMw7wSTbGw3boYHefByg" - - println( - 777, - stringFormatter.makeRandomBase64EncodedUuid, - stringFormatter.makeRandomBase64EncodedUuid, - stringFormatter.getUUIDVersion(iri), - stringFormatter.hasUuidLength(iri.split("/").last), - stringFormatter.isUuidVersion4Or5(iri) - ) + val testIRIFromVersion3UUID = stringFormatter.isUuidSupported(iri3) + val testIRIFromVersion4UUID = stringFormatter.isUuidSupported(iri4) + val testIRIFromVersion5UUID = stringFormatter.isUuidSupported(iri5) + val testBeolIri = stringFormatter.isUuidSupported(beolIri) testIRIFromVersion3UUID should be(false) testIRIFromVersion4UUID should be(true) testIRIFromVersion5UUID should be(true) + testBeolIri should be(true) } } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala index 71574203a8..8d7131b792 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala @@ -2776,6 +2776,7 @@ class StringFormatter private ( /** * Gets the last segment of IRI, decodes UUID and gets the version. + * * @param s the string (IRI) to be checked. * @return UUID version. */ @@ -2785,12 +2786,16 @@ class StringFormatter private ( } /** - * Checks if UUID used to create IRI has correct version (4 and 5 are allowed). + * Checks if UUID used to create IRI has supported version (4 and 5 are allowed). + * With an exception of BEOL project IRI `http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF`. + * * @param s the string (IRI) to be checked. - * @return TRUE for correct versions, FALSE for incorrect. + * @return TRUE for supported versions, FALSE for not supported. */ - def isUuidVersion4Or5(s: IRI): Boolean = - getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5 + def isUuidSupported(s: String): Boolean = + if (s != "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF") { + getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5 + } else true /** * Checks if a string is the right length to be a canonical or Base64-encoded UUID. @@ -2803,19 +2808,21 @@ class StringFormatter private ( /** * Validates resource IRI + * * @param iri to be validated */ def validateUUIDOfResourceIRI(iri: SmartIri): Unit = - if (iri.isKnoraResourceIri && hasUuidLength(iri.toString.split("/").last) && !isUuidVersion4Or5(iri.toString)) { + if (iri.isKnoraResourceIri && hasUuidLength(iri.toString.split("/").last) && !isUuidSupported(iri.toString)) { throw BadRequestException(IriErrorMessages.UuidVersionInvalid) } /** * Validates permission IRI + * * @param iri to be validated. */ def validatePermissionIRI(iri: IRI): Unit = - if (isKnoraPermissionIriStr(iri) && !isUuidVersion4Or5(iri)) { + if (isKnoraPermissionIriStr(iri) && !isUuidSupported(iri)) { throw BadRequestException(IriErrorMessages.UuidVersionInvalid) } else { validatePermissionIri(iri, throw BadRequestException(s"Invalid permission IRI ${iri} is given.")) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala index daa5e3e256..0062834d53 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala @@ -494,7 +494,7 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques if ( stringFormatter.hasUuidLength(valueIri.toString.split("/").last) - && !stringFormatter.isUuidVersion4Or5(valueIri.toString) + && !stringFormatter.isUuidSupported(valueIri.toString) ) { throw BadRequestException(IriErrorMessages.UuidVersionInvalid) }