]> WPIA git - gigi.git/blob - src/org/cacert/gigi/output/template/Form.java
chg: Proper runtime type checking for retrieved forms
[gigi.git] / src / org / cacert / gigi / output / template / Form.java
1 package org.cacert.gigi.output.template;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.Map;
6
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpSession;
9
10 import org.cacert.gigi.GigiApiException;
11 import org.cacert.gigi.localisation.Language;
12 import org.cacert.gigi.util.RandomToken;
13
14 /**
15  * A generic HTML-form that handles CSRF-token creation.
16  */
17 public abstract class Form implements Outputable {
18
19     public static final String CSRF_FIELD = "csrf";
20
21     private final String csrf;
22
23     private final String action;
24
25     /**
26      * Creates a new {@link Form}.
27      * 
28      * @param hsr
29      *            the request to register the form against.
30      */
31     public Form(HttpServletRequest hsr) {
32         this(hsr, null);
33     }
34
35     /**
36      * Creates a new {@link Form}.
37      * 
38      * @param hsr
39      *            the request to register the form against.
40      * @param action
41      *            the target path where the form should be submitted.
42      */
43     public Form(HttpServletRequest hsr, String action) {
44         csrf = RandomToken.generateToken(32);
45         this.action = action;
46         HttpSession hs = hsr.getSession();
47         hs.setAttribute("form/" + getClass().getName() + "/" + csrf, this);
48     }
49
50     /**
51      * Update the forms internal state based on submitted data.
52      * 
53      * @param out
54      *            the stream to the user.
55      * @param req
56      *            the request to take the initial data from.
57      * @return true, iff the form succeeded and the user should be redirected.
58      * @throws GigiApiException
59      *             if internal operations went wrong.
60      */
61     public abstract boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException;
62
63     protected String getCsrfFieldName() {
64         return CSRF_FIELD;
65     }
66
67     @Override
68     public void output(PrintWriter out, Language l, Map<String, Object> vars) {
69         if (action == null) {
70             out.println("<form method='POST'>");
71         } else {
72             out.println("<form method='POST' action='" + action + "'>");
73         }
74         outputContent(out, l, vars);
75         out.print("<input type='hidden' name='" + CSRF_FIELD + "' value='");
76         out.print(getCSRFToken());
77         out.println("'></form>");
78     }
79
80     /**
81      * Outputs the forms contents.
82      * 
83      * @param out
84      *            Stream to the user.
85      * @param l
86      *            {@link Language} to translate text to.
87      * @param vars
88      *            Variables supplied from the outside.
89      */
90     protected abstract void outputContent(PrintWriter out, Language l, Map<String, Object> vars);
91
92     protected String getCSRFToken() {
93         return csrf;
94     }
95
96     /**
97      * Re-fetches a form e.g. when a Post-request is received.
98      * 
99      * @param req
100      *            the request that is directed to the form.
101      * @param target
102      *            the {@link Class} of the expected form.
103      * @return the form where this request is directed to.
104      * @throws CSRFException
105      *             if no CSRF-token is found or the token is wrong.
106      */
107     @SuppressWarnings("unchecked")
108     public static <T extends Form> T getForm(HttpServletRequest req, Class<T> target) throws CSRFException {
109         String csrf = req.getParameter(CSRF_FIELD);
110         if (csrf == null) {
111             throw new CSRFException();
112         }
113         HttpSession hs = req.getSession();
114         if (hs == null) {
115             throw new CSRFException();
116         }
117         Object f = hs.getAttribute("form/" + target.getName() + "/" + csrf);
118         if (f == null) {
119             throw new CSRFException();
120         }
121         if ( !(f instanceof Form)) {
122             throw new CSRFException();
123         }
124         if ( !target.isInstance(f)) {
125             throw new CSRFException();
126         }
127         // Dynamic Cast checked by previous if statement
128         return (T) f;
129     }
130
131     public static class CSRFException extends IOException {
132
133         private static final long serialVersionUID = 59708247477988362L;
134
135     }
136 }