Navigation Menu

Skip to content

Commit

Permalink
refactor: Add BEOL exception to UUID validation (DEV-1570) (#2349)
Browse files Browse the repository at this point in the history
  • Loading branch information
mpro7 committed Dec 21, 2022
1 parent 560b84f commit ed34df1
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 36 deletions.
12 changes: 6 additions & 6 deletions dsp-shared/src/main/scala/dsp/valueobjects/Iri.scala
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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) {})
}
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
14 changes: 10 additions & 4 deletions dsp-shared/src/main/scala/dsp/valueobjects/V2.scala
Expand Up @@ -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.
*/
Expand All @@ -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]]
*/
Expand Down
5 changes: 4 additions & 1 deletion dsp-shared/src/test/scala/dsp/valueobjects/IriSpec.scala
Expand Up @@ -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"

Expand Down Expand Up @@ -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") {
Expand All @@ -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)))
)
Expand Down
Expand Up @@ -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"""
Expand Down
Expand Up @@ -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)
}
}
}
Expand Up @@ -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.
*/
Expand All @@ -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.
Expand All @@ -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."))
Expand Down
Expand Up @@ -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)
}
Expand Down

0 comments on commit ed34df1

Please sign in to comment.