Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
TokenProcessor |
|
| 4.333333333333333;4.333 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.shale.util; | |
19 | ||
20 | import java.security.MessageDigest; | |
21 | import java.security.NoSuchAlgorithmException; | |
22 | import java.util.HashSet; | |
23 | import java.util.Set; | |
24 | ||
25 | import javax.faces.FacesException; | |
26 | import javax.faces.context.FacesContext; | |
27 | import javax.faces.el.PropertyResolver; | |
28 | ||
29 | import org.apache.shale.faces.ShaleConstants; | |
30 | ||
31 | /** | |
32 | * <p>Utility methods supporting the generation and validation of transaction | |
33 | * tokens, used to avoid duplicate form submits.</p> | |
34 | * | |
35 | * $Id: TokenProcessor.java 464373 2006-10-16 04:21:54Z rahul $ | |
36 | */ | |
37 | 10 | public class TokenProcessor { |
38 | ||
39 | ||
40 | // ------------------------------------------------------ Instance Variables | |
41 | ||
42 | ||
43 | /** | |
44 | * <p>Timestamp most recently used to generate a transaction token value.</p> | |
45 | */ | |
46 | private long previous; | |
47 | ||
48 | ||
49 | // ----------------------------------------------------------- Pubic Methods | |
50 | ||
51 | ||
52 | /** | |
53 | * <p>Generate and return the next transaction token value, and store it | |
54 | * so that it may be verified on a subsequent form submit.</p> | |
55 | * | |
56 | * @param context <code>FacesContext</code> for the current request | |
57 | */ | |
58 | public synchronized String generate(FacesContext context) { | |
59 | ||
60 | // Acquire the session identifier for this request | |
61 | // (creating the session if necessary) | |
62 | 1003 | Object session = context.getExternalContext().getSession(true); |
63 | 1003 | assert session != null; |
64 | 1003 | PropertyResolver pr = context.getApplication().getPropertyResolver(); |
65 | 1003 | assert pr != null; |
66 | 1003 | byte id[] = ((String) pr.getValue(session, "id")).getBytes(); |
67 | ||
68 | // Acquire the timestamp we will use for this request | |
69 | 1003 | long current = System.currentTimeMillis(); |
70 | 1003 | if (current <= previous) { |
71 | 997 | current = previous + 1; |
72 | } | |
73 | 1003 | previous = current; |
74 | // byte now[] = new Long(current).toString().getBytes(); | |
75 | 1003 | byte now[] = Long.toString(current).getBytes(); |
76 | ||
77 | // Calculate the new transaction token value | |
78 | 1003 | String token = null; |
79 | try { | |
80 | 1003 | MessageDigest md = MessageDigest.getInstance("MD5"); |
81 | 1003 | md.update(id); |
82 | 1003 | md.update(now); |
83 | 1003 | token = toHex(md.digest()); |
84 | } catch (NoSuchAlgorithmException e) { | |
85 | throw new FacesException(e); | |
86 | 1003 | } |
87 | ||
88 | // Store the generated value for later verification | |
89 | 1003 | Set set = (Set) |
90 | context.getExternalContext().getSessionMap().get(ShaleConstants.TOKENS); | |
91 | 1003 | if (set == null) { |
92 | 4 | set = new HashSet(); |
93 | 4 | context.getExternalContext().getSessionMap().put(ShaleConstants.TOKENS, set); |
94 | } | |
95 | 1003 | set.add(token); |
96 | ||
97 | // Return the generated and cached value | |
98 | 1003 | return token; |
99 | ||
100 | } | |
101 | ||
102 | ||
103 | /** | |
104 | * <p>Verify that the specified transaction token value (retrieved from an | |
105 | * incoming request) is a valid transaaction token. In addition, remove it | |
106 | * from any stored cache of tokens, so that it may not be reused.</p> | |
107 | * | |
108 | * @param context <code>FacesContext</code> for the current request | |
109 | * @param token Transaction token to be verified | |
110 | * | |
111 | * @return <code>True</code> if this token has been verified, | |
112 | * else <code>false</code> | |
113 | */ | |
114 | public synchronized boolean verify(FacesContext context, String token) { | |
115 | ||
116 | 1006 | Set set = (Set) |
117 | context.getExternalContext().getSessionMap().get(ShaleConstants.TOKENS); | |
118 | 1006 | if (set == null) { |
119 | 3 | return false; |
120 | } | |
121 | 1003 | if (set.contains(token)) { |
122 | 1003 | set.remove(token); |
123 | 1003 | if (set.size() < 1) { |
124 | 4 | context.getExternalContext().getSessionMap().remove(ShaleConstants.TOKENS); |
125 | } | |
126 | 1003 | return true; |
127 | } | |
128 | return false; | |
129 | ||
130 | } | |
131 | ||
132 | ||
133 | // --------------------------------------------------------- Private Methods | |
134 | ||
135 | ||
136 | /** | |
137 | * <p>Convert the specified byte array into a String of hexadecimal | |
138 | * digit characters.</p> | |
139 | * | |
140 | * @param buffer Byte array to be converted | |
141 | */ | |
142 | private String toHex(byte buffer[]) { | |
143 | ||
144 | 1003 | StringBuffer sb = new StringBuffer(buffer.length * 2); |
145 | 17051 | for (int i = 0; i < buffer.length; i++) { |
146 | 16048 | sb.append(Character.forDigit((buffer[i] & 0xf0) >> 4, 16)); |
147 | 16048 | sb.append(Character.forDigit((buffer[i] & 0x0f), 16)); |
148 | } | |
149 | 1003 | return sb.toString(); |
150 | ||
151 | } | |
152 | ||
153 | ||
154 | } |