Skip to content

Commit ed34df1

Browse files
authored
refactor: Add BEOL exception to UUID validation (DEV-1570) (#2349)
1 parent 560b84f commit ed34df1

7 files changed

Lines changed: 65 additions & 36 deletions

File tree

‎dsp-shared/src/main/scala/dsp/valueobjects/Iri.scala‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object Iri {
4949

5050
if (!V2IriValidation.isKnoraGroupIriStr(value)) {
5151
Validation.fail(BadRequestException(IriErrorMessages.GroupIriInvalid))
52-
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
52+
} else if (isUuid && !V2UuidValidation.isUuidSupported(value)) {
5353
Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid))
5454
} else {
5555
val validatedValue = Validation(
@@ -80,7 +80,7 @@ object Iri {
8080

8181
if (!V2IriValidation.isKnoraListIriStr(value)) {
8282
Validation.fail(BadRequestException(IriErrorMessages.ListIriInvalid))
83-
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
83+
} else if (isUuid && !V2UuidValidation.isUuidSupported(value)) {
8484
Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid))
8585
} else {
8686
val validatedValue = Validation(
@@ -112,7 +112,7 @@ object Iri {
112112
} else {
113113
if (!V2IriValidation.isKnoraProjectIriStr(value)) {
114114
Validation.fail(ValidationException(IriErrorMessages.ProjectIriInvalid))
115-
} else if (!V2IriValidation.isKnoraBuiltInProjectIriStr(value) && !V2UuidValidation.isUuidVersion4Or5(value)) {
115+
} else if (!V2IriValidation.isKnoraBuiltInProjectIriStr(value) && !V2UuidValidation.isUuidSupported(value)) {
116116
Validation.fail(ValidationException(IriErrorMessages.UuidVersionInvalid))
117117
} else {
118118
val eitherValue = Try(
@@ -147,7 +147,7 @@ object Iri {
147147
Validation.fail(ValidationException(IriErrorMessages.UuidMissing))
148148
} else if (!V2UuidValidation.hasUuidLength(value)) {
149149
Validation.fail(ValidationException(IriErrorMessages.UuidInvalid(value)))
150-
} else if (!V2UuidValidation.isUuidVersion4Or5(value)) {
150+
} else if (!V2UuidValidation.isUuidSupported(value)) {
151151
Validation.fail(ValidationException(IriErrorMessages.UuidVersionInvalid))
152152
} else Validation.succeed(new Base64Uuid(value) {})
153153
}
@@ -165,7 +165,7 @@ object Iri {
165165

166166
if (!V2IriValidation.isKnoraRoleIriStr(value)) {
167167
Validation.fail(BadRequestException(IriErrorMessages.RoleIriInvalid(value)))
168-
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
168+
} else if (isUuid && !V2UuidValidation.isUuidSupported(value)) {
169169
Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid))
170170
} else {
171171
val validatedValue = Validation(
@@ -198,7 +198,7 @@ object Iri {
198198

199199
if (!V2IriValidation.isKnoraUserIriStr(value)) {
200200
Validation.fail(BadRequestException(IriErrorMessages.UserIriInvalid(value)))
201-
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
201+
} else if (isUuid && !V2UuidValidation.isUuidSupported(value)) {
202202
Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid))
203203
} else {
204204
val validatedValue = Validation(

‎dsp-shared/src/main/scala/dsp/valueobjects/V2.scala‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,20 @@ object V2UuidValidation {
217217
s.length == CanonicalUuidLength || s.length == Base64UuidLength
218218

219219
/**
220-
* Checks if UUID used to create IRI has correct version (4 and 5 are allowed).
220+
* Checks if UUID used to create IRI has supported version (4 and 5 are allowed).
221+
* With an exception of BEOL project IRI `http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF`.
222+
*
221223
* @param s the string (IRI) to be checked.
222-
* @return TRUE for correct versions, FALSE for incorrect.
224+
* @return TRUE for supported versions, FALSE for not supported.
223225
*/
224-
def isUuidVersion4Or5(s: String): Boolean =
225-
getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5
226+
def isUuidSupported(s: String): Boolean =
227+
if (s != "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF") {
228+
getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5
229+
} else true
226230

227231
/**
228232
* Decodes Base64 encoded UUID and gets its version.
233+
*
229234
* @param uuid the Base64 encoded UUID as [[String]] to be checked.
230235
* @return UUID version.
231236
*/
@@ -236,6 +241,7 @@ object V2UuidValidation {
236241

237242
/**
238243
* Gets the last IRI segment - Base64 encoded UUID.
244+
*
239245
* @param iri the IRI [[String]] to get the UUID from.
240246
* @return Base64 encoded UUID as [[String]]
241247
*/

‎dsp-shared/src/test/scala/dsp/valueobjects/IriSpec.scala‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ object IriSpec extends ZIOSpecDefault {
2727

2828
val invalidProjectIri = "http://rdfh.ch/projects/0001"
2929
val validProjectIri = "http://rdfh.ch/projects/CwQ8hXF9Qlm1gl2QE6pTpg"
30+
val beolProjectIri = "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF"
3031
val projectIriWithUUIDVersion3 = "http://rdfh.ch/projects/tZjZhGSZMeCLA5VeUmwAmg"
3132
val builtInProjectIri = "http://www.knora.org/ontology/knora-admin#SystemProject"
3233

@@ -183,9 +184,11 @@ object IriSpec extends ZIOSpecDefault {
183184
(for {
184185
iri <- makeProjectIri(validProjectIri)
185186
iri2 <- makeProjectIri(builtInProjectIri)
187+
beolIri <- makeProjectIri(beolProjectIri)
186188
maybeIri <- maybeProjectIri
187189
} yield assertTrue(iri.value == validProjectIri) &&
188190
assertTrue(iri2.value == builtInProjectIri) &&
191+
assertTrue(beolIri.value == beolProjectIri) &&
189192
assert(maybeIri)(isSome(equalTo(iri)))).toZIO
190193
},
191194
test("successfully validate passing None") {
@@ -199,7 +202,7 @@ object IriSpec extends ZIOSpecDefault {
199202
test("pass an empty value and return an error") {
200203
assertTrue(Base64Uuid.make("") == Validation.fail(ValidationException(IriErrorMessages.UuidMissing)))
201204
},
202-
test("pass an invalid UUID and return an error") {
205+
test("pass an invalid UUID and return an error") {
203206
assertTrue(
204207
Base64Uuid.make(invalidIri) == Validation.fail(ValidationException(IriErrorMessages.UuidInvalid(invalidIri)))
205208
)

‎webapi/src/it/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,27 @@ class CreateListItemsRouteADME2ESpec
264264
newListIri.set(listInfo.id)
265265
}
266266

267+
"create a list using bad project IRI, created in the old way with bad UUID version" in {
268+
val createListRequest: String =
269+
s"""{
270+
| "projectIri": "${SharedTestDataADM.BEOL_PROJECT_IRI}",
271+
| "labels": [{ "value": "Neue Liste", "language": "de"}],
272+
| "comments": [{ "value": "XXXXX", "language": "en"}]
273+
|}""".stripMargin
274+
275+
val request = Post(
276+
baseApiUrl + s"/admin/lists",
277+
HttpEntity(ContentTypes.`application/json`, createListRequest)
278+
) ~> addCredentials(beolAdminUserCreds.basicHttpCredentials)
279+
val response: HttpResponse = singleAwaitingRequest(request)
280+
response.status should be(StatusCodes.OK)
281+
282+
val receivedList: ListADM = AkkaHttpUtils.httpResponseToJson(response).fields("list").convertTo[ListADM]
283+
284+
val listInfo = receivedList.listinfo
285+
listInfo.projectIri should be(SharedTestDataADM.BEOL_PROJECT_IRI)
286+
}
287+
267288
"return a ForbiddenException if the user creating the list is not project or system admin" in {
268289
val params =
269290
s"""

‎webapi/src/it/scala/org/knora/webapi/messages/StringFormatterSpec.scala‎

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,29 +1259,21 @@ class StringFormatterSpec extends CoreSpec {
12591259
uuid should be(base4DecodedUuid)
12601260
}
12611261

1262-
"return TRUE if IRI contains UUID version 4 or 5, otherwise return FALSE" in {
1263-
val iri3 = "http://rdfh.ch/0000/rKAU0FNjPUKWqOT8MEW_UQ"
1264-
val iri4 = "http://rdfh.ch/0001/cmfk1DMHRBiR4-_6HXpEFA"
1265-
val iri5 = "http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q"
1262+
"return TRUE for IRIs BEOL project IRI and other which contain UUID version 4 or 5, otherwise return FALSE" in {
1263+
val iri3 = "http://rdfh.ch/0000/rKAU0FNjPUKWqOT8MEW_UQ"
1264+
val iri4 = "http://rdfh.ch/0001/cmfk1DMHRBiR4-_6HXpEFA"
1265+
val iri5 = "http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q"
1266+
val beolIri = "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF"
12661267

1267-
val testIRIFromVersion3UUID = stringFormatter.isUuidVersion4Or5(iri3)
1268-
val testIRIFromVersion4UUID = stringFormatter.isUuidVersion4Or5(iri4)
1269-
val testIRIFromVersion5UUID = stringFormatter.isUuidVersion4Or5(iri5)
1270-
1271-
val iri = "http://rdfh.ch/0001/rYAMw7wSTbGw3boYHefByg"
1272-
1273-
println(
1274-
777,
1275-
stringFormatter.makeRandomBase64EncodedUuid,
1276-
stringFormatter.makeRandomBase64EncodedUuid,
1277-
stringFormatter.getUUIDVersion(iri),
1278-
stringFormatter.hasUuidLength(iri.split("/").last),
1279-
stringFormatter.isUuidVersion4Or5(iri)
1280-
)
1268+
val testIRIFromVersion3UUID = stringFormatter.isUuidSupported(iri3)
1269+
val testIRIFromVersion4UUID = stringFormatter.isUuidSupported(iri4)
1270+
val testIRIFromVersion5UUID = stringFormatter.isUuidSupported(iri5)
1271+
val testBeolIri = stringFormatter.isUuidSupported(beolIri)
12811272

12821273
testIRIFromVersion3UUID should be(false)
12831274
testIRIFromVersion4UUID should be(true)
12841275
testIRIFromVersion5UUID should be(true)
1276+
testBeolIri should be(true)
12851277
}
12861278
}
12871279
}

‎webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2776,6 +2776,7 @@ class StringFormatter private (
27762776

27772777
/**
27782778
* Gets the last segment of IRI, decodes UUID and gets the version.
2779+
*
27792780
* @param s the string (IRI) to be checked.
27802781
* @return UUID version.
27812782
*/
@@ -2785,12 +2786,16 @@ class StringFormatter private (
27852786
}
27862787

27872788
/**
2788-
* Checks if UUID used to create IRI has correct version (4 and 5 are allowed).
2789+
* Checks if UUID used to create IRI has supported version (4 and 5 are allowed).
2790+
* With an exception of BEOL project IRI `http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF`.
2791+
*
27892792
* @param s the string (IRI) to be checked.
2790-
* @return TRUE for correct versions, FALSE for incorrect.
2793+
* @return TRUE for supported versions, FALSE for not supported.
27912794
*/
2792-
def isUuidVersion4Or5(s: IRI): Boolean =
2793-
getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5
2795+
def isUuidSupported(s: String): Boolean =
2796+
if (s != "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF") {
2797+
getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5
2798+
} else true
27942799

27952800
/**
27962801
* Checks if a string is the right length to be a canonical or Base64-encoded UUID.
@@ -2803,19 +2808,21 @@ class StringFormatter private (
28032808

28042809
/**
28052810
* Validates resource IRI
2811+
*
28062812
* @param iri to be validated
28072813
*/
28082814
def validateUUIDOfResourceIRI(iri: SmartIri): Unit =
2809-
if (iri.isKnoraResourceIri && hasUuidLength(iri.toString.split("/").last) && !isUuidVersion4Or5(iri.toString)) {
2815+
if (iri.isKnoraResourceIri && hasUuidLength(iri.toString.split("/").last) && !isUuidSupported(iri.toString)) {
28102816
throw BadRequestException(IriErrorMessages.UuidVersionInvalid)
28112817
}
28122818

28132819
/**
28142820
* Validates permission IRI
2821+
*
28152822
* @param iri to be validated.
28162823
*/
28172824
def validatePermissionIRI(iri: IRI): Unit =
2818-
if (isKnoraPermissionIriStr(iri) && !isUuidVersion4Or5(iri)) {
2825+
if (isKnoraPermissionIriStr(iri) && !isUuidSupported(iri)) {
28192826
throw BadRequestException(IriErrorMessages.UuidVersionInvalid)
28202827
} else {
28212828
validatePermissionIri(iri, throw BadRequestException(s"Invalid permission IRI ${iri} is given."))

‎webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques
494494

495495
if (
496496
stringFormatter.hasUuidLength(valueIri.toString.split("/").last)
497-
&& !stringFormatter.isUuidVersion4Or5(valueIri.toString)
497+
&& !stringFormatter.isUuidSupported(valueIri.toString)
498498
) {
499499
throw BadRequestException(IriErrorMessages.UuidVersionInvalid)
500500
}

0 commit comments

Comments
 (0)