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.custom.passwordStrength;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.myfaces.renderkit.html.util.AddResource;
23  import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
24  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
25  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
26  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
27  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
28  import org.apache.myfaces.shared_tomahawk.util.MessageUtils;
29  
30  import javax.faces.component.UIComponent;
31  import javax.faces.component.UIInput;
32  import javax.faces.component.html.HtmlInputText;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  import javax.faces.render.Renderer;
36  import java.io.IOException;
37  import java.util.Map;
38  
39  /**
40   * @JSFRenderer renderKitId = "HTML_BASIC"
41   * family = "org.apache.myfaces.PasswordStrength"
42   * type = "org.apache.myfaces.PasswordStrength"
43   */
44  public class PasswordStrengthRenderer extends Renderer {
45  
46      /**
47       * Returns a valid javascript var name depending on the given
48       * client id of the component
49       *
50       * @param context
51       * @param component
52       * @return
53       */
54      private String getJavascriptVar(FacesContext context, UIComponent component) {
55          return JAVASCRIPT_VAR_PREFIX + component.getClientId(context).replaceAll("\\:", "_");
56      }
57  
58      ;
59  
60      private void addResources(FacesContext context, UIComponent component,
61                                ResponseWriter writer) throws IOException {
62          PasswordStrengthComponent passwordStrength = (PasswordStrengthComponent) component;
63          AddResource addResource = AddResourceFactory.getInstance(context);
64  
65          // Load the css style ...
66          String styleLocation = (String) component.getAttributes().get(
67                  JSFAttr.STYLE_LOCATION);
68          if (StringUtils.isNotBlank(styleLocation)) {
69              addResource.addStyleSheet(context, AddResource.HEADER_BEGIN,
70                      styleLocation + "/passwordStrength.css");
71          } else {
72              addResource.addStyleSheet(context, AddResource.HEADER_BEGIN,
73                      PasswordStrengthRenderer.class, "css/passwordStrength.css");
74          }
75  
76          // Load the JS file ...
77          String javascriptLocation = (String) component.getAttributes().get(
78                  JSFAttr.JAVASCRIPT_LOCATION);
79          if (javascriptLocation != null) {
80              addResource.addJavaScriptAtPosition(context,
81                      AddResource.HEADER_BEGIN, javascriptLocation
82                      + "/genericProgressbarProvider.js");
83              addResource.addJavaScriptAtPosition(context,
84                      AddResource.HEADER_BEGIN, javascriptLocation
85                      + "/passwordStrength.js");
86          } else {
87              addResource.addJavaScriptAtPosition(context,
88                      AddResource.HEADER_BEGIN, PasswordStrengthRenderer.class,
89                      "genericProgressbarProvider.js");
90              addResource.addJavaScriptAtPosition(context,
91                      AddResource.HEADER_BEGIN, PasswordStrengthRenderer.class,
92                      "passwordStrength.js");
93          }
94  
95  
96          //Add Initialization stuff ...
97          String messageId = getMessageID(context, passwordStrength);
98          writer.write("<script type=\"text/javascript\">");
99          writer.write("var " + getJavascriptVar(context, component) + " = new " + JAVASCRIPT_CLASS + "();");
100         //fixme onload handler
101         String addOnStartUP = "window.addEventListener('load',function() {"
102                 + getJavascriptVar(context, component)
103                 + ".startUpPasswordStrength('"
104                 + messageId
105                 + "'); }, false);";
106         writer.write(addOnStartUP);
107         writer.write("</script>");
108     }
109 
110     private String getMessageID(FacesContext context,
111                                 PasswordStrengthComponent passwordStrength) {
112         String clientID = passwordStrength.getClientId(context);
113         String messageId = "";
114         if (TextIndicatorType.TEXT
115                 .equalsIgnoreCase(getStrengthIndicatorTypeValue(passwordStrength))) {
116             messageId = getIndicatorMessageId(clientID);
117         } else {
118             messageId = getProgressBarContainerID(clientID);
119         }
120         return messageId;
121     }
122 
123 
124 
125     private void renderStartDiv(UIComponent component, ResponseWriter writer)
126             throws IOException {
127         writer.startElement(HTML.DIV_ELEM, component);
128         writer.startElement(HTML.TABLE_ELEM, component);
129         writer.startElement(HTML.TR_ELEM, component);
130         writer.startElement(HTML.TD_ELEM, component);
131     }
132 
133     private void renderEndDiv(UIComponent component, ResponseWriter writer)
134             throws IOException {
135         writer.endElement(HTML.TD_ELEM);
136         writer.endElement(HTML.TR_ELEM);
137         writer.endElement(HTML.TABLE_ELEM);
138         writer.endElement(HTML.DIV_ELEM);
139     }
140 
141     private String getDefaultTextDesc() {
142         return MessageUtils.getMessage(BUNDLE_BASE_NAME,
143                 MessageUtils.getCurrentLocale(),
144                 "org.apache.myfaces.custom.passwordStrength.DESC", null)
145                 .getDetail();
146     }
147 
148     private String getDefaultShowDetails() {
149         return "true";
150     }
151 
152     private String getDefaultUseCustomSecurity() {
153         return "false";
154     }
155 
156     private String getDefaultCustomSecurityRule() {
157         return "A1";
158     }
159 
160     private String getDefaultPenaltyRatio() {
161         return "50";
162     }
163 
164     private String getDefaultPrefix() {
165         return MessageUtils.getMessage(BUNDLE_BASE_NAME,
166                 MessageUtils.getCurrentLocale(),
167                 "org.apache.myfaces.custom.passwordStrength.PREFIX", null)
168                 .getDetail();
169     }
170 
171     private String getLeftCharactersString() {
172         return "'" + MessageUtils.getMessage(BUNDLE_BASE_NAME,
173                 MessageUtils.getCurrentLocale(),
174                 "org.apache.myfaces.custom.passwordStrength.LEFT_CHARS", null)
175                 .getDetail() + "'";
176     }
177 
178     private String getDefaultStrengthIndicatorType() {
179         return TextIndicatorType.TEXT;
180     }
181 
182     private void createTextSpan(PasswordStrengthComponent passwordStrength,
183                                 FacesContext context, String clientID) throws IOException {
184         ResponseWriter writer = context.getResponseWriter();
185         String preferredLength = passwordStrength.getPreferredPasswordLength();
186         String prefixText = (passwordStrength.getPrefixText() == null) ? "'" + getDefaultPrefix() + "'"
187                 : "'" + passwordStrength.getPrefixText() + "'";
188         String textStrengthDescriptions = (passwordStrength
189                 .getTextStrengthDescriptions() == null) ? "'"
190                 + getDefaultTextDesc() + "'" : "'"
191                 + passwordStrength.getTextStrengthDescriptions() + "'";
192         String textID = "'" + clientID + "'";
193         String showDetails = (passwordStrength.getShowDetails() == null) ? "'"
194                 + getDefaultShowDetails() + "'" : "'"
195                 + passwordStrength.getShowDetails().toLowerCase() + "'";
196         String useCustomSecurity = (passwordStrength.getUseCustomSecurity() == null) ? "'"
197                 + getDefaultUseCustomSecurity() + "'"
198                 : "'" + passwordStrength.getUseCustomSecurity().toLowerCase()
199                 + "'";
200         String customSecurityExpression = (passwordStrength
201                 .getCustomSecurityExpression() == null) ? "'"
202                 + getDefaultCustomSecurityRule() + "'" : "'"
203                 + passwordStrength.getCustomSecurityExpression() + "'";
204         String penaltyRatio = (passwordStrength
205                 .getPenaltyRatio() == null) ? "'"
206                 + getDefaultPenaltyRatio() + "'" : "'"
207                 + passwordStrength.getPenaltyRatio() + "'";
208         writer.startElement(HTML.SPAN_ELEM, passwordStrength);
209         writer.startElement(HTML.INPUT_ELEM, passwordStrength);
210         writer.writeAttribute(HTML.TYPE_ATTR, "password", HTML.TYPE_ATTR);
211         writer.writeAttribute(HTML.ID_ATTR, clientID, HTML.ID_ATTR);
212         writer.writeAttribute(HTML.NAME_ATTR, clientID, HTML.NAME_ATTR);
213         String value = "";
214         Object objValue = ((UIInput) passwordStrength).getSubmittedValue();
215         if (objValue != null) {
216             value = passwordStrength.getValue().toString();
217         }
218         writer.writeAttribute("value", value, "value");
219   
220         String onKeyUpString = createOnKeyUpString(context,
221                 passwordStrength, textID, preferredLength, prefixText,
222                 textStrengthDescriptions, true, showDetails, useCustomSecurity,
223                 customSecurityExpression, penaltyRatio);
224         String onKeyUpAttr = passwordStrength.getOnkeyup();
225         passwordStrength.setOnkeyup(constructJSFunction(onKeyUpString, onKeyUpAttr));
226 
227         String onBlurString = getOnBlurString(context, passwordStrength);
228         String onBlurAttr = passwordStrength.getOnblur();
229         passwordStrength.setOnblur(constructJSFunction(onBlurString, onBlurAttr));
230         
231         HtmlRendererUtils.renderHTMLAttributes(writer, passwordStrength, HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
232         if (isDisabled(context, passwordStrength))
233         {
234             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
235         }
236         
237         writer.endElement(HTML.INPUT_ELEM);
238         writer.endElement(HTML.SPAN_ELEM);
239     }
240     
241     /**
242      * Concatenates two javascript functions.  One of which is usually supplied as an attribute
243      * of this component's tag.  The other one is the script created by this component.
244      */
245     private String constructJSFunction(String function, String attrFunction)
246     {
247         if(attrFunction != null && attrFunction.length() > 0) 
248         {
249             if(!attrFunction.endsWith(";"))
250             {
251                 attrFunction += ";";
252             }
253             return (function+attrFunction);
254         }
255         else
256         {
257             return function;
258         }
259     }
260 
261     /**
262      * creates a custom progress bar
263      */
264     private void createProgressbar(UIComponent component,
265                                    FacesContext context, ResponseWriter writer) throws IOException {
266         String clientID = component.getClientId(context);
267         writer.startElement(HTML.DIV_ELEM, component);
268         writer.writeAttribute(HTML.ID_ATTR, getProgressBarContainerID(clientID),
269                 HTML.ID_ATTR);
270         writer.writeAttribute(HTML.CLASS_ATTR, "org_apache_myfaces_passwordStrength_progress",
271                 HTML.CLASS_ATTR);
272 
273         /*inner progress bar*/
274         writer.startElement(HTML.DIV_ELEM, component);
275         writer.writeAttribute(HTML.ID_ATTR, getProgressBarID(clientID),
276                 HTML.ID_ATTR);
277         /*subcomponent per css definition we can use a simple class*/
278         writer.writeAttribute(HTML.CLASS_ATTR, "progressStart",
279                 HTML.CLASS_ATTR);
280         /*inner progress bar end*/
281         writer.endElement(HTML.DIV_ELEM);
282         writer.endElement(HTML.DIV_ELEM);
283     }
284 
285     private void createTextIndicatorMessage(UIComponent component,
286                                             FacesContext context, ResponseWriter writer) throws IOException {
287         String clientID = component.getClientId(context);
288         writer.startElement(HTML.SPAN_ELEM, component);
289         writer.writeAttribute(HTML.ID_ATTR, getIndicatorMessageId(clientID),
290                 HTML.ID_ATTR);
291         writer.writeAttribute(HTML.CLASS_ATTR, "org_apache_myfaces_passwordStrength_progress_indicatorMessage",
292                 HTML.CLASS_ATTR);
293         writer.endElement(HTML.SPAN_ELEM);
294     }
295 
296     private void createIndicatorSpan(UIComponent component,
297                                      FacesContext context, ResponseWriter writer) throws IOException {
298         PasswordStrengthComponent passwordStrength = (PasswordStrengthComponent) component;
299         String clientID = passwordStrength.getClientId(context);
300         String strengthIndicatorType = getStrengthIndicatorTypeValue(passwordStrength);
301         writer.endElement(HTML.TD_ELEM);
302         writer.startElement(HTML.TD_ELEM, component);
303         if (TextIndicatorType.TEXT.equalsIgnoreCase(strengthIndicatorType)) { //It is a text ...
304             createTextIndicatorMessage(component, context, writer);
305         } else { //It is a progressbar ...     
306             //createProgressBarSpan(component, context, writer);
307             createProgressbar(component, context, writer);
308         }
309         writer.endElement(HTML.TD_ELEM);
310         writer.endElement(HTML.TR_ELEM);
311         writer.startElement(HTML.TR_ELEM, component);
312         writer.startElement(HTML.TD_ELEM, component);
313         writer.startElement("div", component);
314         writer.writeAttribute("id", getleftCharsMessageId(clientID), "id");
315         writer.endElement("div");
316     }
317 
318     private String getStrengthIndicatorType(
319             PasswordStrengthComponent passwordStrength) {
320         return (passwordStrength.getStrengthIndicatorType() == null) ? "'"
321                 + getDefaultStrengthIndicatorType() + "'" : "'"
322                 + passwordStrength.getStrengthIndicatorType() + "'";
323     }
324 
325     private String getStrengthIndicatorTypeValue(
326             PasswordStrengthComponent passwordStrength) {
327         return (passwordStrength.getStrengthIndicatorType() == null) ? getDefaultStrengthIndicatorType()
328                 : passwordStrength.getStrengthIndicatorType();
329     }
330 
331 
332     private void createHTMLComponents(FacesContext facesContext,
333                                       UIComponent component, ResponseWriter writer, String clientID)
334             throws IOException {
335         PasswordStrengthComponent passwordStrength = (PasswordStrengthComponent) component;
336         renderStartDiv(component, writer);
337         createTextSpan(passwordStrength, facesContext, clientID);
338         createIndicatorSpan(component, facesContext, writer);
339         renderEndDiv(component, writer);
340     }
341 
342     public void encodeBegin(FacesContext context, UIComponent component)
343             throws IOException {
344         RendererUtils.checkParamValidity(context, component,
345                 PasswordStrengthComponent.class);
346         if (HtmlRendererUtils.isDisplayValueOnly(component)
347                 || isDisabled(context, component)) {
348             super.encodeEnd(context, component);
349             return;
350         }
351         String clientID = component.getClientId(context);
352         ResponseWriter writer = context.getResponseWriter();
353         addResources(context, component, writer);
354         createHTMLComponents(context, component, writer, clientID);
355     }
356 
357     public void encodeEnd(FacesContext context, UIComponent component)
358             throws IOException {
359     }
360 
361     public void decode(FacesContext context, UIComponent component) {
362         Map requestMap = context.getExternalContext().getRequestParameterMap();
363         String clientID = component.getClientId(context);
364         String newValue = (String) requestMap.get(clientID);
365         ((UIInput) component).setSubmittedValue(newValue);
366     }
367 
368     protected boolean isDisabled(FacesContext facesContext,
369                                  UIComponent component) {
370         if (component instanceof HtmlInputText) {
371             return ((HtmlInputText) component).isDisabled();
372         } else {
373             return org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils
374                     .getBooleanAttribute(component, HTML.DISABLED_ATTR, false);
375         }
376     }
377 
378     private String createOnKeyUpString(FacesContext context,
379                                        UIComponent component, String textID, String preferredLength,
380                                        String prefix, String textStrengthDescriptions,
381                                        boolean showMessageIndicator, String showDetails,
382                                        String useCustomSecurity, String customSecurityExpression,
383                                        String penaltyRatio) {
384         PasswordStrengthComponent passwordStrength = (PasswordStrengthComponent) component;
385         String clientID = component.getClientId(context);
386         String showMessageIndicatorString = "";
387         String strengthIndicatorType = getStrengthIndicatorType(passwordStrength);
388         String progressBarId = "'" + getProgressBarID(clientID) + "'";
389         String indicatorMessageID = "'" + getIndicatorMessageId(clientID) + "'";
390         String leftCharsMessageID = "'" + getleftCharsMessageId(clientID) + "'";
391         if (showMessageIndicator == true) {
392             showMessageIndicatorString = getJavascriptVar(context, component) + ".show('"
393                     + getMessageID(context, passwordStrength) + "');";
394         }
395         return updateStatusValue(context, component, textID, preferredLength, prefix,
396                 textStrengthDescriptions, indicatorMessageID,
397                 leftCharsMessageID, showMessageIndicatorString,
398                 strengthIndicatorType, progressBarId, showDetails,
399                 getLeftCharactersString(), useCustomSecurity,
400                 customSecurityExpression, penaltyRatio);
401     }
402 
403     private String updateStatusValue(FacesContext context,
404                                      UIComponent component,
405                                      String textID, String preferredLength,
406                                      String prefix, String textStrengthDescriptions,
407                                      String indicatorMessageID, String leftCharsMessageID,
408                                      String showMessageIndicatorString,
409                                      String strengthIndicatorType, String progressBarId,
410                                      String showDetails, String leftCharactersString,
411                                      String useCustomSecurity, String customSecurityExpression,
412                                      String penaltyRatio) {
413         return getJavascriptVar(context, component) + ".updateStatusValue("
414                 + textID + "," + preferredLength + ", "
415                 + prefix + ", " + textStrengthDescriptions + ", "
416                 + indicatorMessageID + ", " + leftCharsMessageID + ", "
417                 + strengthIndicatorType + ", " + progressBarId + ", "
418                 + showDetails + ", " + leftCharactersString + ", "
419                 + useCustomSecurity + ", " + customSecurityExpression + ", "
420                 + penaltyRatio
421                 + ");"
422                 + showMessageIndicatorString;
423     }
424 
425     private String getIndicatorMessageId(String clientID) {
426         return clientID + "indicatorMessage";
427     }
428 
429     private String getleftCharsMessageId(String clientID) {
430         return clientID + "leftCharsMessage";
431     }
432 
433     private String getProgressBarID(String clientID) {
434         return clientID + PROGRESSBAR_SUFFIX;
435     }
436 
437     private String getProgressBarContainerID(String clientID) {
438         return getProgressBarID(clientID) + PROGRESSBAR_CONTAINER_SUFFIX;
439     }
440 
441     private String getOnBlurString(FacesContext context, UIComponent component) {
442         PasswordStrengthComponent passwordStrength = (PasswordStrengthComponent) component;
443         String clientID = passwordStrength.getClientId(context);
444         return getJavascriptVar(context, component) + ".hide('" + getMessageID(context, passwordStrength) + "');" + getJavascriptVar(context, component) + ".hide('"
445                 + getleftCharsMessageId(clientID) + "');";
446     }
447 
448     final String BUNDLE_BASE_NAME = "org.apache.myfaces.custom.passwordStrength.resource.PasswordStrength";
449     final String DEFAULT_PROGRESSBAR_WIDTH = "150";
450     final String DEFAULT_PROGRESSBAR_HEIGHT = "20";
451     final String PROGRESSBAR_SUFFIX = "_PROGRESSBAR";
452     final String PROGRESSBAR_CONTAINER_SUFFIX = "_CONTAINER";
453     final String DEFAULT_PROGRESSBAR_VALUE = "20";
454     /**
455      * Package and class declaration
456      * for the underlying namespaced
457      * javasript class
458      */
459     static final String JAVASCRIPT_CLASS = "org.apache.myfaces.passwordStrength";
460     /**
461      * pseudo namespacing of the javascript var to avoid potential conflicts
462      */
463     static final String JAVASCRIPT_VAR_PREFIX = "myfaces_";
464 }