Thursday, June 12, 2014

JSF 2.2 Tip of the Day: Hidden Field Validation

Hidden Mines

Introduction

How often do you have validators on your hidden fields? I recently performed a security audit of an application that did not have validators associated with the hidden fields on the page. I suspect that the out of sight, out of mind mentality prevailed. Admittedly, I have often short circuited some development and put hidden fields in a JSF page without a validator. My expectation is that the data in the field would be used to provide information that may be needed by the application. However, if these fields have setters... and the results are stored in a database... I think you get the picture.

Methodology

I decided to create a more complex than really necessary example to show how to validate a <h:inputHidden /> field. In the example, you can enter names into an <h:inputText /> which will use JavaScript to update the hidden field. The validation will be activated on form submission. Additionally, a value change listener will update the planet name, and update the planet to the new planet. The validation will prevent putting in planets that don't exist in the enum for current Planets. You can confirm this by entering a bogus name, or poor Pluto that was kicked out of the planetary club.

Code

The code for this example was developed using NetBeans 8.0 IDE on GlassFish 4+ and Apache Tomcat 8+ using JSF 2.2 (Mojarra).

The code for the project can be downloaded from Bitbuckethidden-field-validation


Planets.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.bluelotussoftware.example;
 
import javax.json.Json;
import javax.json.JsonObject;
 
/**
 * An {@literal enum} that represents the planets in our solar system, and their
 * position from the Sun.
 *
 * @author John Yeary
 * @version 1.0
 */
public enum Planets {
 
    Mercury(1), Venus(2), Earth(3), Mars(4), Jupiter(5), Saturn(6), Uranus(7), Neptune(8);
 
    /**
     * Private constructor that sets solar position.
     *
     * @param position The planetary orbital position.
     */
    private Planets(final int position) {
        this.position = position;
    }
    private final int position;
 
    /**
     * {@inheritDoc}
     *
     * @return a JSON string representing the planet name and its position e.g. <pre>
     * {"planet":"Earth","position":2}
     * </pre>
*/
    @Override
    public String toString() {
        JsonObject job = Json.createObjectBuilder()
                .add("planet", this.name())
                .add("position", this.ordinal())
                .build();
        return job.toString();
    }
}

PlanetValidator.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.bluelotussoftware.example;
 
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
 
/**
 * This {@link Validator} is used to confirm if a submitted {@link Planets} name
 * is a valid planet.
 *
 * @author John Yeary
 * @version 1.0
 */
@FacesValidator(value = "com.bluelotussoftware.example.PlanetValidator")
public class PlanetValidator implements Validator {
 
    /**
     * {@inheritDoc}
     *
     * @param context
     * @param component
     * @param value
     * @throws ValidatorException
     */
    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        String planetName = (String) value;
        if (planetName != null && !planetName.isEmpty()) {
            System.out.println("Validating " + planetName);
            try {
                Planets.valueOf(planetName);
            } catch (IllegalArgumentException ex) {
                FacesMessage fm;
                if ("Pluto".equalsIgnoreCase(planetName)) {
                    fm = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                            "Planet invalid.",
                            "Poor Pluto... its a planetoid not a planet anymore.");
                } else {
                    fm = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                            "Planet invalid.", "The submitted planet is invalid.");
                }
                throw new ValidatorException(fm, ex);
            }
        }
    }
}

IndexBean.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.bluelotussoftware.example;
 
import java.io.Serializable;
import java.text.MessageFormat;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ValueChangeEvent;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
@ManagedBean
@ViewScoped
public class IndexBean implements Serializable {
 
    private static final long serialVersionUID = 7770102269253088141L;
    private Planets planet = Planets.Earth;
    private String planetName;
 
    public IndexBean() {
    }
 
    @PostConstruct
    private void init() {
        planetName = planet.name();
    }
 
    public String getPlanetName() {
        return planetName;
    }
 
    public void setPlanetName(String planetName) {
        this.planetName = planetName;
    }
 
    public void setPlanet(Planets planet) {
        this.planet = planet;
    }
 
    public Planets getPlanet() {
        return planet;
    }
 
    public String planetaryAction() {
        return null;
    }
 
    public void planetaryValueChangeListener(ValueChangeEvent event) {
        System.out.println(MessageFormat.format("Old: {0} New: {1}",
                event.getOldValue(), event.getNewValue()));
        String value = (String) event.getNewValue();
        if (value != null && !value.isEmpty()) {
            planetName = value;
            planet = Planets.valueOf(value);
        }
    }
}

index.xhtml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Home</title>
    </h:head>
    <h:body>
        <h:messages globalOnly="#{true}"/>
        <h:form id="form1">
            <h:outputText value="Current Planet: #{indexBean.planet.toString()}"/>
            <h:panelGrid columns="2">
                <h:inputText id="inputText1" value="#{indexBean.planetName}"
                             valueChangeListener="#{indexBean.planetaryValueChangeListener}"
                             onkeyup="document.getElementById('form1\:inputHidden1').value = this.value;">
                    <f:validator validatorId="com.bluelotussoftware.example.PlanetValidator"/>
                </h:inputText>
                <h:message for="inputText1"/>
                <h:inputHidden id="inputHidden1" value="#{indexBean.planetName}">
                    <f:validator validatorId="com.bluelotussoftware.example.PlanetValidator"/>
                </h:inputHidden>
                <h:message for="inputHidden1"/>
            </h:panelGrid>
            <h:commandButton value="Submit" action="#{indexBean.planetaryAction()}"/>
        </h:form>
    </h:body>
</html>

0 comments :

Popular Posts