View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.base;
23  
24  import java.util.logging.Logger;
25  
26  import javax.faces.FacesException;
27  import javax.faces.context.FacesContext;
28  
29  import org.primefaces.component.api.AbstractPrimeHtmlInputTextArea;
30  import org.primefaces.component.api.RTLAware;
31  import org.primefaces.component.api.Widget;
32  import org.primefaces.context.PrimeApplicationContext;
33  import org.primefaces.extensions.util.HtmlSanitizer;
34  
35  /**
36   * Abstract Editor for SunEditor and Markdown Editor.
37   */
38  public class AbstractEditorInputTextArea extends AbstractPrimeHtmlInputTextArea implements Widget, RTLAware {
39  
40      private static final Logger LOGGER = Logger.getLogger(AbstractEditorInputTextArea.class.getName());
41  
42      protected enum PropertyKeys {
43          // @formatter:off
44          widgetVar,
45          dir,
46          allowBlocks,
47          allowFormatting,
48          allowLinks,
49          allowStyles,
50          allowImages,
51          allowTables,
52          allowMedia,
53          secure,
54          extender,
55          toolbar
56          // @formatter:on
57      }
58  
59      public void setDir(final String _dir) {
60          getStateHelper().put(PropertyKeys.dir, _dir);
61      }
62  
63      @Override
64      public String getDir() {
65          return (String) getStateHelper().eval(PropertyKeys.dir, "ltr");
66      }
67  
68      public String getWidgetVar() {
69          return (String) getStateHelper().eval(PropertyKeys.widgetVar, null);
70      }
71  
72      public void setWidgetVar(String widgetVar) {
73          getStateHelper().put(PropertyKeys.widgetVar, widgetVar);
74      }
75  
76      public boolean isSecure() {
77          return (Boolean) getStateHelper().eval(PropertyKeys.secure, true);
78      }
79  
80      public void setSecure(boolean secure) {
81          getStateHelper().put(PropertyKeys.secure, secure);
82      }
83  
84      public boolean isAllowBlocks() {
85          return (Boolean) getStateHelper().eval(PropertyKeys.allowBlocks, true);
86      }
87  
88      public void setAllowBlocks(boolean allowBlocks) {
89          getStateHelper().put(PropertyKeys.allowBlocks, allowBlocks);
90      }
91  
92      public boolean isAllowFormatting() {
93          return (Boolean) getStateHelper().eval(PropertyKeys.allowFormatting, true);
94      }
95  
96      public void setAllowFormatting(boolean allowFormatting) {
97          getStateHelper().put(PropertyKeys.allowFormatting, allowFormatting);
98      }
99  
100     public boolean isAllowLinks() {
101         return (Boolean) getStateHelper().eval(PropertyKeys.allowLinks, true);
102     }
103 
104     public void setAllowLinks(boolean allowLinks) {
105         getStateHelper().put(PropertyKeys.allowLinks, allowLinks);
106     }
107 
108     public boolean isAllowStyles() {
109         return (Boolean) getStateHelper().eval(PropertyKeys.allowStyles, true);
110     }
111 
112     public void setAllowStyles(boolean allowStyles) {
113         getStateHelper().put(PropertyKeys.allowStyles, allowStyles);
114     }
115 
116     public boolean isAllowImages() {
117         return (Boolean) getStateHelper().eval(PropertyKeys.allowImages, true);
118     }
119 
120     public void setAllowImages(boolean allowImages) {
121         getStateHelper().put(PropertyKeys.allowImages, allowImages);
122     }
123 
124     public boolean isAllowTables() {
125         return (Boolean) getStateHelper().eval(PropertyKeys.allowTables, true);
126     }
127 
128     public void setAllowMedia(boolean allowMedia) {
129         getStateHelper().put(PropertyKeys.allowMedia, allowMedia);
130     }
131 
132     public boolean isAllowMedia() {
133         return (Boolean) getStateHelper().eval(PropertyKeys.allowMedia, true);
134     }
135 
136     public void setAllowTables(boolean allowTables) {
137         getStateHelper().put(PropertyKeys.allowTables, allowTables);
138     }
139 
140     public String getExtender() {
141         return (String) getStateHelper().eval(PropertyKeys.extender, null);
142     }
143 
144     public void setExtender(String extender) {
145         getStateHelper().put(PropertyKeys.extender, extender);
146     }
147 
148     public String getToolbar() {
149         return (String) getStateHelper().eval(PropertyKeys.toolbar, null);
150     }
151 
152     public void setToolbar(String toolbar) {
153         getStateHelper().put(PropertyKeys.toolbar, toolbar);
154     }
155 
156     /**
157      * Enforce security by default requiring the OWASP sanitizer on the classpath. Only if a user marks the editor with secure="false" will they opt-out of
158      * security.
159      *
160      * @param context the FacesContext
161      */
162     public void checkSecurity(FacesContext context) {
163         boolean sanitizerAvailable = PrimeApplicationContext.getCurrentInstance(context).getEnvironment()
164                     .isHtmlSanitizerAvailable();
165         if (this.isSecure() && !sanitizerAvailable) {
166             throw new FacesException(
167                         "Editor component is marked secure='true' but the HTML Sanitizer was not found on the classpath. "
168                                     + "Either add the HTML sanitizer to the classpath per the documentation"
169                                     + " or mark secure='false' if you would like to use the component without the sanitizer.");
170         }
171     }
172 
173     /**
174      * If security is enabled sanitize the Markdown string to prevent XSS.
175      *
176      * @param context the FacesContext
177      * @param value the value to sanitize
178      * @return the sanitized value
179      */
180     public String sanitizeHtml(FacesContext context, String value) {
181         String result = value;
182         if (this.isSecure()
183                     && PrimeApplicationContext.getCurrentInstance(context).getEnvironment().isHtmlSanitizerAvailable()) {
184             result = HtmlSanitizer.sanitizeHtml(value, this.isAllowBlocks(), this.isAllowFormatting(),
185                         this.isAllowLinks(), this.isAllowStyles(), this.isAllowImages(), this.isAllowTables(), this.isAllowMedia());
186         }
187         else {
188             if (!this.isAllowBlocks() || !this.isAllowFormatting() || !this.isAllowLinks()
189                         || !this.isAllowStyles() || !this.isAllowImages() || !this.isAllowTables()) {
190                 LOGGER.warning("HTML sanitizer not available - skip sanitizing....");
191             }
192         }
193         return result;
194     }
195 }