View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.application.viewstate;
20  
21  import java.util.Map;
22  import javax.faces.context.ExternalContext;
23  import javax.faces.context.FacesContext;
24  import org.apache.commons.codec.DecoderException;
25  import org.apache.commons.codec.binary.Hex;
26  import org.apache.myfaces.shared.renderkit.RendererUtils;
27  import org.apache.myfaces.shared.util.WebConfigParamUtils;
28  
29  /**
30   * This factory generate a key composed by a counter and a random number. The
31   * counter ensures uniqueness, and the random number prevents guess the next
32   * session token.
33   */
34  class SecureRandomKeyFactory extends KeyFactory<byte[], String>
35  {
36      private final SessionIdGenerator sessionIdGenerator;
37      private final int length;
38  
39      public SecureRandomKeyFactory(FacesContext facesContext)
40      {
41          length = WebConfigParamUtils.getIntegerInitParameter(
42              facesContext.getExternalContext(), 
43              ServerSideStateCacheImpl.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM, 
44              ServerSideStateCacheImpl.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT);
45          sessionIdGenerator = new SessionIdGenerator();
46          sessionIdGenerator.setSessionIdLength(length);
47          String secureRandomClass = WebConfigParamUtils.getStringInitParameter(
48              facesContext.getExternalContext(), 
49              ServerSideStateCacheImpl.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS_PARAM);
50          if (secureRandomClass != null)
51          {
52              sessionIdGenerator.setSecureRandomClass(secureRandomClass);
53          }
54          String secureRandomProvider = WebConfigParamUtils.getStringInitParameter(
55              facesContext.getExternalContext(), 
56              ServerSideStateCacheImpl.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER_PARAM);
57          if (secureRandomProvider != null)
58          {
59              sessionIdGenerator.setSecureRandomProvider(secureRandomProvider);
60          }
61          String secureRandomAlgorithm = WebConfigParamUtils.getStringInitParameter(
62              facesContext.getExternalContext(), 
63              ServerSideStateCacheImpl.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM_PARAM);
64          if (secureRandomAlgorithm != null)
65          {
66              sessionIdGenerator.setSecureRandomAlgorithm(secureRandomAlgorithm);
67          }
68      }
69  
70      public Integer generateCounterKey(FacesContext facesContext)
71      {
72          ExternalContext externalContext = facesContext.getExternalContext();
73          Object sessionObj = externalContext.getSession(true);
74          Integer sequence = null;
75          synchronized (sessionObj) // are handled at the same time for the session
76          {
77              Map<String, Object> map = externalContext.getSessionMap();
78              sequence = (Integer) map.get(RendererUtils.SEQUENCE_PARAM);
79              if (sequence == null || sequence.intValue() == Integer.MAX_VALUE)
80              {
81                  sequence = Integer.valueOf(1);
82              }
83              else
84              {
85                  sequence = Integer.valueOf(sequence.intValue() + 1);
86              }
87              map.put(RendererUtils.SEQUENCE_PARAM, sequence);
88          }
89          return sequence;
90      }
91  
92      @Override
93      public byte[] generateKey(FacesContext facesContext)
94      {
95          byte[] array = new byte[length];
96          byte[] key = new byte[length + 4];
97          sessionIdGenerator.getRandomBytes(array);
98          for (int i = 0; i < array.length; i++)
99          {
100             key[i] = array[i];
101         }
102         int value = generateCounterKey(facesContext);
103         key[array.length] = (byte) (value >>> 24);
104         key[array.length + 1] = (byte) (value >>> 16);
105         key[array.length + 2] = (byte) (value >>> 8);
106         key[array.length + 3] = (byte) (value);
107         return key;
108     }
109 
110     @Override
111     public String encode(byte[] key)
112     {
113         return new String(Hex.encodeHex(key));
114     }
115 
116     @Override
117     public byte[] decode(String value)
118     {
119         try
120         {
121             return Hex.decodeHex(value.toCharArray());
122         }
123         catch (DecoderException ex)
124         {
125             // Cannot decode, ignore silently, later it will be handled as
126             // ViewExpiredException
127             // Cannot decode, ignore silently, later it will be handled as
128             // ViewExpiredException
129         }
130         return null;
131     }
132     
133 }