Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api-v2, api-admin): ontology name and project name should be URL safe (DSP-1749) #1889

Merged
merged 2 commits into from Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/03-apis/api-admin/projects.md
Expand Up @@ -61,7 +61,7 @@ License along with DSP. If not, see <http://www.gnu.org/licenses/>.
- Required information:
- shortcode (unique, 4-digits)
- shortname (unique; it should be in the form of a
[xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName))
[xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName) and it should be URL safe.)
- description (collection of descriptions as strings with language tag.)
- keywords (collection of keywords)
- status (true, if project is active. false, if project is inactive)
Expand Down
2 changes: 1 addition & 1 deletion docs/03-apis/api-v2/knora-iris.md
Expand Up @@ -68,7 +68,7 @@ http://www.knora.org/ontology/0001/example
```

An ontology name must be a valid XML
[NCName](https://www.w3.org/TR/xml-names/#NT-NCName).
[NCName](https://www.w3.org/TR/xml-names/#NT-NCName) and must be URL safe.
The following names are reserved for built-in internal DSP ontologies:

- `knora-base`
Expand Down
Expand Up @@ -852,6 +852,8 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No
// RFC 4648.
private val Base64UrlPattern = "[A-Za-z0-9_-]+"

private val Base64UrlPatternRegex: Regex = ("^" + Base64UrlPattern + "$").r

// Calculates check digits for resource IDs in ARK URLs.
private val base64UrlCheckDigit = new Base64UrlCheckDigit

Expand Down Expand Up @@ -2176,11 +2178,18 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No
* @return the same ontology name.
*/
def validateProjectSpecificOntologyName(ontologyName: String, errorFun: => Nothing): String = {
// Check that ontology name matched NCName regex pattern
ontologyName match {
case NCNameRegex(_*) => ()
case _ => errorFun
}

// Check that ontology name is URL safe
ontologyName match {
case Base64UrlPatternRegex(_*) => ()
case _ => errorFun
}

val lowerCaseOntologyName = ontologyName.toLowerCase

lowerCaseOntologyName match {
Expand Down Expand Up @@ -2481,8 +2490,14 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No
* project shortname.
* @return the same string.
*/
def validateAndEscapeProjectShortname(value: String, errorFun: => Nothing): String = {
NCNameRegex.findFirstIn(value) match {
def validateAndEscapeProjectShortname(shortname: String, errorFun: => Nothing): String = {
// Check that shortname matches NCName pattern
val ncNameMatch = NCNameRegex.findFirstIn(shortname) match {
case Some(value) => value
case None => errorFun
}
// Check that shortname is URL safe
Base64UrlPatternRegex.findFirstIn(ncNameMatch) match {
case Some(shortname) => toSparqlEncodedString(shortname, errorFun)
case None => errorFun
}
Expand Down
Expand Up @@ -108,6 +108,40 @@ class ProjectsMessagesADMSpec extends CoreSpec(ProjectsMessagesADMSpec.config) {
)
assert(caught.getMessage === "Project shortcode must be supplied.")
}

"return 'BadRequestException' if project 'shortname' is not NCName valid" in {
val invalidShortName = "abd%2"
val caught = intercept[BadRequestException](
CreateProjectApiRequestADM(
shortname = invalidShortName,
shortcode = "1114",
longname = Some("project longname"),
description = Seq(StringLiteralV2(value = "project description", language = Some("en"))),
keywords = Seq("keywords"),
logo = Some("/fu/bar/baz.jpg"),
status = true,
selfjoin = false
).validateAndEscape
)
assert(caught.getMessage === s"The supplied short name: '$invalidShortName' is not valid.")
}

"return 'BadRequestException' if project 'shortname' is not URL safe" in {
val invalidShortName = "äbd2"
val caught = intercept[BadRequestException](
CreateProjectApiRequestADM(
shortname = invalidShortName,
shortcode = "1114",
longname = Some("project longname"),
description = Seq(StringLiteralV2(value = "project description", language = Some("en"))),
keywords = Seq("keywords"),
logo = Some("/fu/bar/baz.jpg"),
status = true,
selfjoin = false
).validateAndEscape
)
assert(caught.getMessage === s"The supplied short name: '$invalidShortName' is not valid.")
}
}

"The ChangeProjectApiRequestADM case class" should {
Expand Down
Expand Up @@ -245,6 +245,23 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {
fooLastModDate = newFooLastModDate
}

"not create an ontology if the given name matches NCName pattern but is not URL safe" in {
responderManager ! CreateOntologyRequestV2(
ontologyName = "bär",
projectIri = imagesProjectIri,
label = "The bär ontology",
comment = Some("some comment"),
apiRequestID = UUID.randomUUID,
featureFactoryConfig = defaultFeatureFactoryConfig,
requestingUser = imagesUser
)

expectMsgPF(timeout) {
case msg: akka.actor.Status.Failure =>
msg.cause.isInstanceOf[BadRequestException] should ===(true)
}
}

"create an empty ontology called 'bar' with a comment" in {
responderManager ! CreateOntologyRequestV2(
ontologyName = "bar",
Expand Down