From 0c5d69e25e79a1052d0ec2456f756e3f39897dad Mon Sep 17 00:00:00 2001 From: Tim Worrall Date: Tue, 7 Apr 2015 14:00:30 -0400 Subject: [PATCH] Related to VIVO-921. Server-side validation for default data property form. --- .../DefaultDataPropertyFormGenerator.java | 2 + .../DefaultDataPropertyFormValidator.java | 122 ++++++++++++++++++ webapp/web/css/individual/individual.css | 6 +- .../individual/propStatement-dataDefault.ftl | 46 ++++--- .../freemarker/edit/forms/css/customForm.css | 14 +- 5 files changed, 170 insertions(+), 20 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDataPropertyFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDataPropertyFormGenerator.java index ef263cf5a..6a935e835 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDataPropertyFormGenerator.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDataPropertyFormGenerator.java @@ -25,6 +25,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.RdfLiteralHash; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.DefaultDataPropEmptyField; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.DefaultDataPropertyFormValidator; public class DefaultDataPropertyFormGenerator extends BaseEditConfigurationGenerator implements EditConfigurationGenerator { @@ -97,6 +98,7 @@ public class DefaultDataPropertyFormGenerator extends BaseEditConfigurationGener literalField.setValidators(list( "nonempty" )); editConfiguration.setN3Required(Arrays.asList( dataPropN3 )); } + editConfiguration.addValidator(new DefaultDataPropertyFormValidator(rangeDatatypeUri, vreq)); //prepare prepare(vreq, editConfiguration); return editConfiguration; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java new file mode 100644 index 000000000..a2eb46a5f --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java @@ -0,0 +1,122 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ +package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.hp.hpl.jena.rdf.model.Literal; + +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.N3ValidatorVTwo; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.MultiValueEditSubmission; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; + +/** + * Check if the submitted text has potential XSS problems. + * Error messages from this validator always start with XSS_ERROR_MESSAGE + * + * @author bdc34 + */ +public class DefaultDataPropertyFormValidator implements N3ValidatorVTwo{ + private Log log = LogFactory.getLog(DefaultDataPropertyFormValidator.class); + + VitroRequest vreq; + private String datatype; + private final I18nBundle i18n; + private final String dtRegex = "^([0-9]{4})-((0[1-9])|(1[0-2]))-((0[1-9])|([1-2][0-9])|(3[0-1]))(T|\\s)(([0-1][0-9])|(2[0-3])):([0-5][0-9]):([0-5][0-9])"; + private final Pattern dtPattern = Pattern.compile(dtRegex); + private final String dateRegex = "^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})"; + private final Pattern datePattern = Pattern.compile(dateRegex); + private final String timeRegex = "^(([0-1][0-9])|(2[0-3])):([0-5][0-9]):([0-5][0-9])"; + private final Pattern timePattern = Pattern.compile(timeRegex); + private final String yearRegex = "^\\d{4}"; + private final Pattern yearPattern = Pattern.compile(yearRegex); + private final String ymRegex = "^([0-9]{4})-(0[1-9]|1[012])"; + private final Pattern ymPattern = Pattern.compile(ymRegex); + private final String monthRegex = "^--(0[1-9]|1[012])"; + private final Pattern monthPattern = Pattern.compile(monthRegex); + private final String floatRegex = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?."; + private final Pattern floatPattern = Pattern.compile(floatRegex); + private final String intRegex = "^-?\\d+$"; + private final Pattern intPattern = Pattern.compile(intRegex); + + public DefaultDataPropertyFormValidator(String datatype, VitroRequest vreq) { + this.datatype = datatype; + this.vreq = vreq; + this.i18n = I18n.bundle(vreq); + } + + @Override + public Map validate(EditConfigurationVTwo editConfig, + MultiValueEditSubmission editSub) { + + Map> literalsFromForm = editSub.getLiteralsFromForm(); + + Map errors = new HashMap(); + + List formLiterals = literalsFromForm.get("literal"); + Literal literal = null; + if(formLiterals != null && formLiterals.size() > 0) { + literal = formLiterals.get(0); + } + String literalValue = ""; + if (literal != null) { + literalValue = literal.getLexicalForm(); + if( "".equals(literalValue) ) { + literal = null; + } + } + + if ( literal != null ) { + + if ( datatype.indexOf("#dateTime") > -1 ) { + if ( !dtPattern.matcher(literalValue).matches() ) { + errors.put("dateTime", i18n.text("minimum_ymd")); + } + } + else if ( datatype.indexOf("#date") > -1 ) { + if ( !datePattern.matcher(literalValue).matches() ) { + errors.put("date", i18n.text("year_month_day")); + } + } + else if ( datatype.indexOf("#time") > -1 ) { + if ( !timePattern.matcher(literalValue).matches() ) { + errors.put("time", i18n.text("minimum_hour")); + } + } + else if ( datatype.indexOf("#gYearMonth") > -1 ) { + if ( !ymPattern.matcher(literalValue).matches() ) { + errors.put("yearMonth", i18n.text("year_month")); + } + } + else if ( datatype.indexOf("#gYear") > -1 ) { + if ( !yearPattern.matcher(literalValue).matches() ) { + errors.put("year", i18n.text("four_digit_year")); + } + } + else if ( datatype.indexOf("#float") > -1 ) { + if ( !floatPattern.matcher(literalValue).matches() ) { + errors.put("float", i18n.text("decimal_only")); + } + } + else if ( datatype.indexOf("#int") > -1 ) { + if ( !intPattern.matcher(literalValue).matches() ) { + errors.put("integer", i18n.text("whole_number")); + } + } + } + else { + return null; + } + + return errors.size() != 0 ? errors : null; + } +} \ No newline at end of file diff --git a/webapp/web/css/individual/individual.css b/webapp/web/css/individual/individual.css index 2eafc11d3..8e66d9dd2 100644 --- a/webapp/web/css/individual/individual.css +++ b/webapp/web/css/individual/individual.css @@ -296,7 +296,11 @@ ol.tinyMCENumeric li { padding: 0 !important; margin-left: 30px; } -img.invalidFormat { +img.invalidFormatImg { padding-left:8px; vertical-align:middle; +} +.invalidFormatText { + font-size:10px; + color:#A12424; } \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/partials/individual/propStatement-dataDefault.ftl b/webapp/web/templates/freemarker/body/partials/individual/propStatement-dataDefault.ftl index e9ba1eead..9fd45ed7d 100644 --- a/webapp/web/templates/freemarker/body/partials/individual/propStatement-dataDefault.ftl +++ b/webapp/web/templates/freemarker/body/partials/individual/propStatement-dataDefault.ftl @@ -46,47 +46,57 @@ <#if datatype?? > <#switch datatype> <#case "date"> - ${i18n().invalid_format} <#-- validated above --> - <#break> + ${i18n().invalid_format} <#-- validated above --> + invalid format + <#break> <#case "dateTime"> -  ${i18n().invalid_format} <#-- validated above --> - <#break> +  ${i18n().invalid_format} <#-- validated above --> + invalid format + <#break> <#case "time"> <#if !value?matches("(([0-1][0-9])|(2[0-3])):([0-5][0-9]):([0-5][0-9])") > -  ${i18n().invalid_format} +  ${i18n().invalid_format} + invalid format <#break> <#case "gYear"> <#if !value?matches("^\\d{4}") > -  ${i18n().invalid_format} +  ${i18n().invalid_format} + invalid format <#break> <#case "gMonth"> -  ${i18n().invalid_format} <#-- validated above --> - <#break> +  ${i18n().invalid_format} <#-- validated above --> + invalid format + <#break> <#case "gYearMonth"> -  ${i18n().invalid_format} <#-- validated above --> - <#break> +  ${i18n().invalid_format} <#-- validated above --> + invalid format + <#break> <#case "float"> <#if !value?matches("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?.") > -  ${i18n().invalid_format} +  ${i18n().invalid_format} + invalid format - <#break> + <#break> <#case "integer"> <#if !value?matches("^-?\\d+$") > -  ${i18n().invalid_format} +  ${i18n().invalid_format} + invalid format - <#break> + <#break> <#case "int"> <#if !value?matches("^-?\\d+$") > -  ${i18n().invalid_format} +  ${i18n().invalid_format} + invalid format - <#break> + <#break> <#case "boolean"> <#if !value?matches("false") && !value?matches("true") > -  ${i18n().invalid_format} +  ${i18n().invalid_format} + invalid format - <#break> + <#break> <#default> diff --git a/webapp/web/templates/freemarker/edit/forms/css/customForm.css b/webapp/web/templates/freemarker/edit/forms/css/customForm.css index c9e8b6117..d4ca1fd16 100644 --- a/webapp/web/templates/freemarker/edit/forms/css/customForm.css +++ b/webapp/web/templates/freemarker/edit/forms/css/customForm.css @@ -137,4 +137,16 @@ section#pubsContainer input { img#indicator { padding-left:60px; } - +/* for placeholder text */ +::-webkit-input-placeholder { /* WebKit browsers */ + opacity: .25; +} +:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ + opacity: .25; +} +::-moz-placeholder { /* Mozilla Firefox 19+ */ + opacity: .25; +} +:-ms-input-placeholder { /* Internet Explorer 10+ */ + opacity: .25; +}