2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

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  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          Object session = context.getExternalContext().getSession(true);
63          assert session != null;
64          PropertyResolver pr = context.getApplication().getPropertyResolver();
65          assert pr != null;
66          byte id[] = ((String) pr.getValue(session, "id")).getBytes();
67  
68          // Acquire the timestamp we will use for this request
69          long current = System.currentTimeMillis();
70          if (current <= previous) {
71              current = previous + 1;
72          }
73          previous = current;
74          // byte now[] = new Long(current).toString().getBytes();
75          byte now[] = Long.toString(current).getBytes();
76  
77          // Calculate the new transaction token value
78          String token = null;
79          try {
80              MessageDigest md = MessageDigest.getInstance("MD5");
81              md.update(id);
82              md.update(now);
83              token = toHex(md.digest());
84          } catch (NoSuchAlgorithmException e) {
85              throw new FacesException(e);
86          }
87  
88          // Store the generated value for later verification
89          Set set = (Set)
90            context.getExternalContext().getSessionMap().get(ShaleConstants.TOKENS);
91          if (set == null) {
92              set = new HashSet();
93              context.getExternalContext().getSessionMap().put(ShaleConstants.TOKENS, set);
94          }
95          set.add(token);
96  
97          // Return the generated and cached value
98          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         Set set = (Set)
117           context.getExternalContext().getSessionMap().get(ShaleConstants.TOKENS);
118         if (set == null) {
119             return false;
120         }
121         if (set.contains(token)) {
122             set.remove(token);
123             if (set.size() < 1) {
124                 context.getExternalContext().getSessionMap().remove(ShaleConstants.TOKENS);
125             }
126             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         StringBuffer sb = new StringBuffer(buffer.length * 2);
145         for (int i = 0; i < buffer.length; i++) {
146             sb.append(Character.forDigit((buffer[i] & 0xf0) >> 4, 16));
147             sb.append(Character.forDigit((buffer[i] & 0x0f), 16));
148         }
149         return sb.toString();
150 
151     }
152 
153 
154 }