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  
20  package org.apache.myfaces.tobago.internal.config;
21  
22  import org.apache.myfaces.tobago.config.TobagoConfig;
23  import org.apache.myfaces.tobago.context.Theme;
24  import org.apache.myfaces.tobago.context.ThemeImpl;
25  import org.apache.myfaces.tobago.sanitizer.Sanitizer;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import javax.faces.application.Application;
30  import javax.faces.application.ProjectStage;
31  import javax.faces.context.FacesContext;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  
40  /**
41   * <p>
42   * Implementation of the Tobago configuration.
43   * </p>
44   * <p>
45   * All setters must are protected, so EL can't modify this config.
46   * </p>
47   */
48  public class TobagoConfigImpl extends TobagoConfig {
49  
50    private static final Logger LOG = LoggerFactory.getLogger(TobagoConfigImpl.class);
51  
52    private List<Theme> supportedThemes;
53    private List<String> supportedThemeNames;
54    private Theme defaultTheme;
55    private String defaultThemeName;
56    private Map<String, ThemeImpl> availableThemes;
57    private RenderersConfig renderersConfig;
58    private boolean createSessionSecret;
59    private boolean checkSessionSecret;
60    private boolean preventFrameAttacks;
61    private ContentSecurityPolicy contentSecurityPolicy;
62    private SecurityAnnotation securityAnnotation;
63    private boolean setNosniffHeader;
64    private Map<String, String> defaultValidatorInfo;
65    private Sanitizer sanitizer;
66    private boolean decodeLineFeed;
67    private Map<String, String> mimeTypes;
68  
69    private boolean unmodifiable = false;
70  
71    protected TobagoConfigImpl() {
72      supportedThemeNames = new ArrayList<String>();
73      supportedThemes = new ArrayList<Theme>();
74      availableThemes = new HashMap<String, ThemeImpl>();
75      createSessionSecret = true;
76      checkSessionSecret = true;
77      preventFrameAttacks = true;
78      setNosniffHeader = true;
79      securityAnnotation = SecurityAnnotation.disable;
80      decodeLineFeed = true;
81      contentSecurityPolicy = new ContentSecurityPolicy(ContentSecurityPolicy.Mode.OFF.getValue());
82      mimeTypes = new HashMap<String, String>();
83    }
84  
85    /**
86     * Lock the configuration, so it cannot be modified any more.
87     */
88    protected void lock() {
89      unmodifiable = true;
90      supportedThemes = Collections.unmodifiableList(supportedThemes);
91      for (Theme theme : supportedThemes) {
92        ((ThemeImpl) theme).lock();
93      }
94      supportedThemeNames = Collections.unmodifiableList(supportedThemeNames);
95      availableThemes = Collections.unmodifiableMap(availableThemes);
96  
97      if (renderersConfig instanceof RenderersConfigImpl) {
98        ((RenderersConfigImpl) renderersConfig).lock();
99      }
100     contentSecurityPolicy.lock();
101 
102     mimeTypes = Collections.unmodifiableMap(mimeTypes);
103   }
104 
105   private void checkLocked() throws IllegalStateException {
106     if (unmodifiable) {
107       throw new RuntimeException("The configuration must not be changed after initialization!");
108     }
109   }
110 
111   protected void addSupportedThemeName(final String name) {
112     checkLocked();
113     supportedThemeNames.add(name);
114   }
115 
116   // TODO one init method
117   protected void resolveThemes() {
118     checkLocked();
119 
120     if (defaultThemeName != null) {
121       defaultTheme = availableThemes.get(defaultThemeName);
122       checkThemeIsAvailable(defaultThemeName, defaultTheme);
123       if (LOG.isDebugEnabled()) {
124         LOG.debug("name = '{}'", defaultThemeName);
125         LOG.debug("defaultTheme = '{}'", defaultTheme);
126       }
127     } else {
128       int deep = 0;
129       for (final Map.Entry<String, ThemeImpl> entry : availableThemes.entrySet()) {
130         final Theme theme = entry.getValue();
131         if (theme.getFallbackList().size() > deep) {
132           defaultTheme = theme;
133           deep = theme.getFallbackList().size();
134         }
135       }
136       if (defaultTheme == null) {
137         final String error = "Did not found any theme! "
138             + "Please ensure you have a tobago-config.xml with a theme-definition in your "
139             + "theme JAR. Please add a theme JAR to your WEB-INF/lib";
140         LOG.error(error);
141         throw new RuntimeException(error);
142       } else {
143         if (LOG.isInfoEnabled()) {
144           LOG.info("Using default Theme {}", defaultTheme.getName());
145         }
146       }
147     }
148     if (!supportedThemeNames.isEmpty()) {
149       for (final String name : supportedThemeNames) {
150         final Theme theme = availableThemes.get(name);
151         checkThemeIsAvailable(name, theme);
152         supportedThemes.add(theme);
153         if (LOG.isDebugEnabled()) {
154           LOG.debug("name = '{}'", name);
155           LOG.debug("supportedThemes.last() = '{}'", supportedThemes.get(supportedThemes.size() - 1));
156         }
157       }
158     }
159   }
160 
161   private void checkThemeIsAvailable(final String name, final Theme theme) {
162     if (theme == null) {
163       final String error = "Theme not found! name: '" + name + "'. "
164           + "Please ensure you have a tobago-config.xml with a theme-definition in your "
165           + "theme JAR. Found the following themes: " + availableThemes.keySet();
166       LOG.error(error);
167       throw new RuntimeException(error);
168     }
169   }
170 
171   @Override
172   public Theme getTheme(final String name) {
173     if (name == null) {
174       LOG.debug("searching theme: null");
175       return defaultTheme;
176     }
177     if (defaultTheme != null && defaultTheme.getName().equals(name)) {
178       return defaultTheme;
179     }
180     for (final Theme theme : supportedThemes) {
181       if (theme.getName().equals(name)) {
182         return theme;
183       }
184     }
185     LOG.debug("searching theme '{}' not found. Using default: {}", name, defaultTheme);
186     return defaultTheme;
187   }
188 
189   protected void setDefaultThemeName(final String defaultThemeName) {
190     checkLocked();
191     this.defaultThemeName = defaultThemeName;
192   }
193 
194   @Override
195   public List<Theme> getSupportedThemes() {
196     return supportedThemes;
197   }
198 
199   @Override
200   public Theme getDefaultTheme() {
201     return defaultTheme;
202   }
203 
204   protected void addAvailableTheme(ThemeImpl availableTheme) {
205     checkLocked();
206     availableThemes.put(availableTheme.getName(), availableTheme);
207   }
208 
209   public Map<String, ThemeImpl> getAvailableThemes() {
210     return availableThemes;
211   }
212 
213   protected RenderersConfig getRenderersConfig() {
214     return renderersConfig;
215   }
216 
217   protected void setRenderersConfig(final RenderersConfig renderersConfig) {
218     checkLocked();
219     this.renderersConfig = renderersConfig;
220   }
221 
222   /**
223    * @deprecated use FacesContext.isProjectStage
224    * @return the ProjectStage
225    */
226   @Override
227   @Deprecated
228   public ProjectStage getProjectStage() {
229     return FacesContext.getCurrentInstance().getApplication().getProjectStage();
230   }
231 
232   protected synchronized void initDefaultValidatorInfo() {
233     if (defaultValidatorInfo != null) {
234       checkLocked();
235     }
236     final FacesContext facesContext = FacesContext.getCurrentInstance();
237     if (facesContext != null) {
238       try {
239         final Application application = facesContext.getApplication();
240         final Map<String, String> map = application.getDefaultValidatorInfo();
241         if (map.size() > 0) {
242           defaultValidatorInfo = Collections.unmodifiableMap(map);
243         } else {
244           defaultValidatorInfo = Collections.emptyMap();
245         }
246       } catch (final Exception e) {
247         LOG.error("Can't initialize default validators (this happens with JBoss GateIn 3.6.0).", e);
248         defaultValidatorInfo = Collections.emptyMap();
249       }
250     }
251   }
252 
253   @Override
254   public boolean isCreateSessionSecret() {
255     return createSessionSecret;
256   }
257 
258   protected void setCreateSessionSecret(final boolean createSessionSecret) {
259     checkLocked();
260     this.createSessionSecret = createSessionSecret;
261   }
262 
263   @Override
264   public boolean isCheckSessionSecret() {
265     return checkSessionSecret;
266   }
267 
268   protected void setCheckSessionSecret(final boolean checkSessionSecret) {
269     checkLocked();
270     this.checkSessionSecret = checkSessionSecret;
271   }
272 
273 
274   @Override
275   public boolean isPreventFrameAttacks() {
276     return preventFrameAttacks;
277   }
278 
279   protected void setPreventFrameAttacks(final boolean preventFrameAttacks) {
280     checkLocked();
281     this.preventFrameAttacks = preventFrameAttacks;
282   }
283 
284   @Override
285   public ContentSecurityPolicy getContentSecurityPolicy() {
286     return contentSecurityPolicy;
287   }
288 
289   @Override
290   public boolean isSetNosniffHeader() {
291     return setNosniffHeader;
292   }
293 
294   protected void setSetNosniffHeader(final boolean setNosniffHeader) {
295     checkLocked();
296     this.setNosniffHeader = setNosniffHeader;
297   }
298 
299   @Override
300   public SecurityAnnotation getSecurityAnnotation() {
301     return securityAnnotation;
302   }
303 
304   public void setSecurityAnnotation(final SecurityAnnotation securityAnnotation) {
305     checkLocked();
306     this.securityAnnotation = securityAnnotation;
307   }
308 
309   public Map<String, String> getDefaultValidatorInfo() {
310     // TODO: if the startup hasn't found a FacesContext and Application, this may depend on the order of the listeners.
311     if (defaultValidatorInfo == null) {
312       initDefaultValidatorInfo();
313     }
314     return defaultValidatorInfo;
315   }
316 
317   @Override
318   public Sanitizer getSanitizer() {
319     return sanitizer;
320   }
321 
322   protected void setSanitizer(final Sanitizer sanitizer) {
323     checkLocked();
324     this.sanitizer = sanitizer;
325   }
326 
327   @Override
328   public boolean isDecodeLineFeed() {
329     return decodeLineFeed;
330   }
331 
332   public void setDecodeLineFeed(boolean decodeLineFeed) {
333     checkLocked();
334     this.decodeLineFeed = decodeLineFeed;
335   }
336 
337   @Override
338   public Map<String, String> getMimeTypes() {
339     return mimeTypes;
340   }
341 
342   /**
343    * {@inheritDoc}
344    */
345   @Override
346   @Deprecated
347   public boolean isClassicDateTimePicker() {
348     return false;
349   }
350 
351   @Override
352   public String toString() {
353     final StringBuilder builder = new StringBuilder();
354     builder.append("TobagoConfigImpl{");
355     builder.append("\nsupportedThemes=[");
356     for (final Theme supportedTheme : supportedThemes) {
357       builder.append(supportedTheme.getName());
358       builder.append(", ");
359     }
360     builder.append("], \ndefaultTheme=");
361     builder.append(defaultTheme != null ? defaultTheme.getName() : null);
362     builder.append(", \navailableThemes=");
363     builder.append(availableThemes.keySet());
364     builder.append(", \ncreateSessionSecret=");
365     builder.append(createSessionSecret);
366     builder.append(", \ncheckSessionSecret=");
367     builder.append(checkSessionSecret);
368     builder.append(", \npreventFrameAttacks=");
369     builder.append(preventFrameAttacks);
370     builder.append(", \ncontentSecurityPolicy=");
371     builder.append(contentSecurityPolicy);
372     builder.append(", \nsecurityAnnotation=");
373     builder.append(securityAnnotation);
374     builder.append(", \nsetNosniffHeader=");
375     builder.append(setNosniffHeader);
376     builder.append(", \ndefaultValidatorInfo=");
377     builder.append(defaultValidatorInfo);
378     builder.append(", \nsanitizer=");
379     builder.append(sanitizer);
380     // to see only different (ignore alternative names for the same theme)
381     builder.append(", \nthemes=");
382     final Set<Theme> all = new HashSet<Theme>(availableThemes.values());
383     builder.append(all);
384     builder.append(", \nmimeTypes=");
385     builder.append(mimeTypes);
386     builder.append('}');
387     return builder.toString();
388   }
389 }