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