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