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 javax.faces.context.ExternalContext;
22  import javax.faces.context.FacesContext;
23  
24  import org.apache.myfaces.application.StateCache;
25  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
26  import org.apache.myfaces.shared.util.WebConfigParamUtils;
27  
28  class ClientSideStateCacheImpl extends StateCache<Object, Object>
29  {
30      
31      /**
32       * Define the time in minutes where the view state is valid when
33       * client side state saving is used. By default it is set to 0
34       * (infinite).
35       */
36      @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="0", group="state")
37      public static final String INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT = 
38              "org.apache.myfaces.CLIENT_VIEW_STATE_TIMEOUT";
39      public static final Long INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT_DEFAULT = 0L;
40      
41      private static final int STATE_PARAM = 0;
42      private static final int VIEWID_PARAM = 1;
43      private static final int TIMESTAMP_PARAM = 2;
44      
45      private static final Object[] EMPTY_STATES = new Object[]{null, null};
46      
47      private Long _clientViewStateTimeout;
48      
49      private CsrfSessionTokenFactory csrfSessionTokenFactory;
50      
51      public ClientSideStateCacheImpl()
52      {
53          FacesContext facesContext = FacesContext.getCurrentInstance();
54          
55          String csrfRandomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
56                  RANDOM_KEY_IN_CSRF_SESSION_TOKEN_PARAM, 
57                  RANDOM_KEY_IN_CSRF_SESSION_TOKEN_PARAM_DEFAULT);
58          if (RANDOM_KEY_IN_CSRF_SESSION_TOKEN_SECURE_RANDOM.equals(csrfRandomMode))
59          {
60              csrfSessionTokenFactory = new SecureRandomCsrfSessionTokenFactory(facesContext);
61          }
62          else
63          {
64              csrfSessionTokenFactory = new RandomCsrfSessionTokenFactory(facesContext);
65          }
66      }
67  
68      @Override
69      public Object saveSerializedView(FacesContext facesContext,
70              Object serializedView)
71      {
72          // The state in this case the state is saved on the token sent to
73          // the client (isWriteStateAfterRenderViewRequired() is set to true).
74          // No additional operation is required here.
75          return encodeSerializedState(facesContext, serializedView);
76      }
77  
78      @Override
79      public Object restoreSerializedView(FacesContext facesContext,
80              String viewId, Object viewState)
81      {
82          Object[] state = (Object[]) viewState;
83          long clientViewStateTimeout = getClientViewStateTimeout(facesContext.getExternalContext());
84          
85          if (clientViewStateTimeout > 0L)
86          {
87              Long timeStamp = (Long) state[TIMESTAMP_PARAM];
88              if (timeStamp == null)
89              {
90                  //If no timestamp, state is invalid.
91                  return null;
92              }
93              long passedTime = (System.currentTimeMillis() - timeStamp.longValue()) / 60000;
94              
95              if (passedTime > clientViewStateTimeout)
96              {
97                  //expire
98                  return null;
99              }
100         }
101         
102         String restoredViewId = (String) state[VIEWID_PARAM];
103         
104         if (viewId != null && !viewId.equals(restoredViewId))
105         {
106             //invalid viewId, expire
107             return null;
108         }
109         
110         //return the state
111         if (state[STATE_PARAM] == null)
112         {
113             return EMPTY_STATES;
114         }
115         else
116         {
117             Object serializedView = state[STATE_PARAM];
118             if (serializedView instanceof Object[] &&
119             ((Object[])serializedView).length == 2 &&
120             ((Object[])serializedView)[0] == null &&
121             ((Object[])serializedView)[1] == null)
122             {
123                 // Remember inside the state null is stored as an empty array.
124                 return null;
125             }
126             
127             return state[STATE_PARAM];
128         }
129     }
130 
131     @Override
132     public Object encodeSerializedState(FacesContext facesContext,
133             Object serializedView)
134     {
135         Object[] state = null;
136         
137         if (getClientViewStateTimeout(facesContext.getExternalContext()).longValue() > 0L)
138         {
139             state = new Object[3];
140             state[TIMESTAMP_PARAM] = System.currentTimeMillis();
141         }
142         else
143         {
144             state = new Object[2];
145         }
146         
147         if (serializedView == null)
148         {
149             state[STATE_PARAM] = EMPTY_STATES;
150         }
151         else if (serializedView instanceof Object[] &&
152             ((Object[])serializedView).length == 2 &&
153             ((Object[])serializedView)[0] == null &&
154             ((Object[])serializedView)[1] == null)
155         {
156             // The generated state can be considered zero, set it as null
157             // into the map.
158             state[STATE_PARAM] = null;
159         }
160         else
161         {
162             state[STATE_PARAM] = serializedView;
163         }
164 
165         state[VIEWID_PARAM] = facesContext.getViewRoot().getViewId();
166         
167         return state;
168     }
169 
170     @Override
171     public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
172     {
173         return true;
174     }
175 
176     /**
177      * @return the _clientViewStateTimeout
178      */
179     protected Long getClientViewStateTimeout(ExternalContext context)
180     {
181         if (_clientViewStateTimeout == null)
182         {
183             _clientViewStateTimeout = WebConfigParamUtils.getLongInitParameter(
184                     context, INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT,
185                     INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT_DEFAULT);
186             if (_clientViewStateTimeout.longValue() < 0L)
187             {
188                 _clientViewStateTimeout = 0L;
189             }
190         }
191         return _clientViewStateTimeout;
192     }
193 
194     @Override
195     public String createCryptographicallyStrongTokenFromSession(FacesContext context)
196     {
197         return csrfSessionTokenFactory.createCryptographicallyStrongTokenFromSession(context);
198     }
199 }