Hi everybody,

this patch would add a bootstrapified password strength meter to all pages where
user details are being created or edited (create the initial admin user,
create/edit normal users and create organization).
There is also a tick icon on the side of the password input fields, which checks if the value in the desired password field will be accepted by the server and if the value in the confirm password field matches the value in the desired password field.

We had this implemented in SUSE Manager before, and now completely reworked it. It would be useful due to spacewalk being a systems administrations tool and
due to the security implications that come with it.

It is based on https://github.com/ablanco/jquery.pwstrength.bootstrap
where I also contributed to during the process.

Thanks
Maximilian

--
--
Mit freundlichen Grüßen,
Maximilian Meister
Systems Management Department

SUSE LINUX Products GmbH
Maxfeldstr. 5
D-90409 Nuremberg, Germany

http://www.suse.com

GF: Jeff Hawn, Jennifer Guild, Felix Imendoerffer, HRB 21284 (AG Nuremberg)

>From 74756cc3d26ac4bb64f322eaf82d30376d69466c Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeis...@suse.de>
Date: Thu, 19 Dec 2013 10:53:33 +0100
Subject: [PATCH 1/3] Styles for the password strength meter.

---
 branding/css/spacewalk-tools.less | 5 +++++
 branding/css/spacewalk.less       | 2 ++
 2 files changed, 7 insertions(+)
 create mode 100644 branding/css/spacewalk-tools.less

diff --git a/branding/css/spacewalk-tools.less b/branding/css/spacewalk-tools.less
new file mode 100644
index 0000000..0383b3d
--- /dev/null
+++ b/branding/css/spacewalk-tools.less
@@ -0,0 +1,5 @@
+/* Progress Bar for the password strength check */
+.progress-pwstrength {
+    margin-bottom: 0;
+    margin-top: 4px;
+}
diff --git a/branding/css/spacewalk.less b/branding/css/spacewalk.less
index 3afa2f0..cb69e25 100644
--- a/branding/css/spacewalk.less
+++ b/branding/css/spacewalk.less
@@ -11,6 +11,8 @@
 @import url(spacewalk-mixins.less);
 /*Import of Tables style*/
 @import url(spacewalk-tables.less);
+/* Import of spacewalk-tools */
+@import url(spacewalk-tools.less);
 /* Import of the Theme */
 @import url(spacewalk-theme.less);
 
--
1.8.4

>From 1b7dde2ec3b25c448cd5f6d050703e4ff3d03ae8 Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeis...@suse.de>
Date: Thu, 19 Dec 2013 10:57:14 +0100
Subject: [PATCH 2/3] Adding modified pwstrength.js library. Adding
 function to setup the password strength meter. Adding function to handle the
 tick icon on the side of a password input field.

---
 web/html/javascript/spacewalk-pwstrength.js | 477 ++++++++++++++++++++++++++++
 1 file changed, 477 insertions(+)
 create mode 100644 web/html/javascript/spacewalk-pwstrength.js

diff --git a/web/html/javascript/spacewalk-pwstrength.js b/web/html/javascript/spacewalk-pwstrength.js
new file mode 100644
index 0000000..e7837fb
--- /dev/null
+++ b/web/html/javascript/spacewalk-pwstrength.js
@@ -0,0 +1,477 @@
+/*jslint browser: true, regexp: true, unparam: true */
+/*global jQuery */
+
+/*
+* jQuery Password Strength plugin for Twitter Bootstrap
+*
+* Copyright (c) 2008-2013 Tane Piper
+* Copyright (c) 2013 Alejandro Blanco
+* Dual licensed under the MIT and GPL licenses.
+*
+*/
+
+(function ($) {
+    "use strict";
+
+    var options = {
+            errors: [],
+            // Options
+            minChar: 8,
+            bootstrap3: false,
+            errorMessages: {
+                password_too_short: '<span style="color: #d52929">The Password is too short</span>',
+                email_as_password: '<span style="color: #d52929">Do not use your email as your password</span>',
+                same_as_username: '<span style="color: #d52929">Your password cannot contain your username</span>',
+                repeated_character: '<span style="color: #d52929">The password should not contain repetitions</span>',
+                no_character_classes: '<span style="color: #d52929">Use different character classes</span>'
+            },
+            scores: [17, 26, 40, 50],
+            verdicts: ["Weak", "Normal", "Medium", "Strong", "Very Strong"],
+            showVerdicts: true,
+            showVerdictsInitially: false,
+            raisePower: 1.4,
+            usernameField: "#username",
+            onLoad: undefined,
+            onKeyUp: undefined,
+            container: undefined,
+            viewports: {
+                progress: undefined,
+                verdict: undefined,
+                errors: undefined
+            },
+            // Rules stuff
+            ruleScores: {
+                wordNotEmail: -100,
+                wordLength: -100,
+                wordSimilarToUsername: -100,
+                wordRepetition: -30,
+                wordLowercase: 1,
+                wordUppercase: 3,
+                wordOneNumber: 3,
+                wordThreeNumbers: 5,
+                wordOneSpecialChar: 3,
+                wordTwoSpecialChar: 5,
+                wordUpperLowerCombo: 2,
+                wordLetterNumberCombo: 2,
+                wordLetterNumberCharCombo: 2,
+                wordTwoCharacterClasses: -5
+            },
+            rules: {
+                wordNotEmail: true,
+                wordLength: true,
+                wordSimilarToUsername: true,
+                wordRepetition: true,
+                wordLowercase: true,
+                wordUppercase: true,
+                wordOneNumber: true,
+                wordThreeNumbers: true,
+                wordOneSpecialChar: true,
+                wordTwoSpecialChar: true,
+                wordUpperLowerCombo: true,
+                wordLetterNumberCombo: true,
+                wordLetterNumberCharCombo: true,
+                wordTwoCharacterClasses: true
+            },
+            validationRules: {
+                wordNotEmail: function (options, word, score) {
+                    if (word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i)) {
+                        options.errors.push(options.errorMessages.email_as_password);
+                        return score;
+                    }
+                },
+                wordLength: function (options, word, score) {
+                    var wordlen = word.length,
+                        lenScore = Math.pow(wordlen, options.raisePower);
+                    if (wordlen < options.minChar) {
+                        lenScore = (lenScore + score);
+                        options.errors.push(options.errorMessages.password_too_short);
+                    }
+                    return lenScore;
+                },
+                wordSimilarToUsername: function (options, word, score) {
+                    var username = $(options.usernameField).val() || $(options.usernameField).text();
+                    if (username && word.toLowerCase().match(username.toLowerCase())) {
+                        options.errors.push(options.errorMessages.same_as_username);
+                        return score;
+                    }
+                    return true;
+                },
+                wordRepetition: function (options, word, score) {
+                    if (word.match(/(.)\1\1/)) {
+                        options.errors.push(options.errorMessages.repeated_character);
+                        return score;
+                    }
+                },
+                wordLowercase: function (options, word, score) {
+                    return word.match(/[a-z]/) && score;
+                },
+                wordUppercase: function (options, word, score) {
+                    return word.match(/[A-Z]/) && score;
+                },
+                wordOneNumber : function (options, word, score) {
+                    return word.match(/\d+/) && score;
+                },
+                wordThreeNumbers : function (options, word, score) {
+                    return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
+                },
+                wordOneSpecialChar : function (options, word, score) {
+                    return word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])|(^[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
+                },
+                wordTwoSpecialChar : function (options, word, score) {
+                    return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
+                },
+                wordUpperLowerCombo : function (options, word, score) {
+                    return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
+                },
+                wordLetterNumberCombo : function (options, word, score) {
+                    return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
+                },
+                wordLetterNumberCharCombo : function (options, word, score) {
+                    return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score;
+                },
+                wordTwoCharacterClasses: function (options, word, score) {
+                    if (word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) ||
+                       (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) ||
+                       (word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])/) && word.match(/[a-zA-Z0-9_]/))) {
+                        return score;
+                    }
+                    else {
+                        options.errors.push(options.errorMessages.no_character_classes);
+                    }
+                }
+            }
+        },
+
+        getContainer = function (container, $el) {
+            var $container = $(container);
+
+            if (!($container && $container.length === 1)) {
+                $container = $el.parent();
+            }
+
+            return $container;
+        },
+
+        progressWidget = function (localOptions) {
+            var html = '<div class="progress progress-pwstrength"><div class="';
+
+            if (localOptions.bootstrap3) {
+                html += 'progress-';
+            }
+
+            return html + 'bar"></div></div>';
+        },
+
+        initProgressBar = function (options, $el) {
+            var $progressbar = $(progressWidget(options)),
+                $container = getContainer(options.container, $el);
+
+            if (options.viewports.progress) {
+                $container.find(options.viewports.progress).append($progressbar);
+            } else {
+                $progressbar.insertAfter('#desiredpassword-input-group');
+            }
+
+            if (options.bootstrap3) {
+                $progressbar.find(".progress-bar").css("width", "0%");
+            } else {
+                $progressbar.find(".bar").css("width", "0%");
+            }
+
+            $el.data("pwstrength").progressbar = $progressbar;
+        },
+
+        initVerdict = function (localOptions, $el, initial) {
+            var $container = $(localOptions.container),
+                $verdict;
+
+            if (!($container && $container.length === 1)) {
+                $container = $el.parent();
+            }
+
+            if (localOptions.viewports.verdict) {
+                $verdict = $container.find(localOptions.viewports.verdict).find(".password-verdict");
+            } else {
+                $verdict = $container.find(".password-verdict");
+            }
+
+            if ($verdict.length === 0) {
+                $verdict = $('<span class="password-verdict" style="display: none">' + initial + '</span>');
+                if (localOptions.viewports.verdict) {
+                    $container.find(localOptions.viewports.verdict).append($verdict);
+                } else {
+                    $verdict.insertAfter($el);
+                }
+            }
+
+            return $verdict;
+        },
+
+        possibleProgressBarClasses = ["danger", "warning", "success"],
+
+        updateProgressBarHTML = function ($bar, cssPrefix, cssClass, percentage) {
+            var aux, i;
+
+            $bar.addClass(cssPrefix + "bar-" + cssClass);
+            for (i = 0; i < possibleProgressBarClasses.length; i += 1) {
+                aux = possibleProgressBarClasses[i];
+                if (aux !== cssClass) {
+                    $bar.removeClass(cssPrefix + "bar-" + aux);
+                }
+            }
+            $bar.css("width", percentage);
+        },
+
+        setProgressBar = function ($el, score) {
+            var localOptions = $el.data("pwstrength"),
+                $progressbar = localOptions.progressbar,
+                cssPrefix = "",
+                $bar = $progressbar.find(".bar"),
+                $verdict,
+                verdictText;
+
+            if (localOptions.bootstrap3) {
+                $bar = $progressbar.find(".progress-bar");
+                cssPrefix = "progress-";
+            }
+
+            if (localOptions.showVerdicts) {
+                $verdict = initVerdict(localOptions, $el, "");
+            }
+
+            if (score < localOptions.scores[0]) {
+                updateProgressBarHTML($bar, cssPrefix, "danger", "5%");
+                verdictText = localOptions.verdicts[0];
+            } else if (score >= localOptions.scores[0] && score < localOptions.scores[1]) {
+                updateProgressBarHTML($bar, cssPrefix, "warning", "25%");
+                verdictText = localOptions.verdicts[1];
+            } else if (score >= localOptions.scores[1] && score < localOptions.scores[2]) {
+                updateProgressBarHTML($bar, cssPrefix, "warning", "50%");
+                verdictText = localOptions.verdicts[2];
+            } else if (score >= localOptions.scores[2] && score < localOptions.scores[3]) {
+                updateProgressBarHTML($bar, cssPrefix, "warning", "75%");
+                verdictText = localOptions.verdicts[3];
+            } else if (score >= localOptions.scores[3]) {
+                updateProgressBarHTML($bar, cssPrefix, "success", "100%");
+                verdictText = localOptions.verdicts[4];
+            }
+
+            if (localOptions.showVerdicts) {
+                $verdict.text(verdictText);
+            }
+        },
+
+        calculateScore = function ($el) {
+            var word = $el.val(),
+                totalScore = 0,
+                localOptions = $el.data("pwstrength");
+
+            $.each(localOptions.rules, function (rule, active) {
+                if (active === true) {
+                    var score = localOptions.ruleScores[rule],
+                        result = localOptions.validationRules[rule](localOptions, word, score);
+                    if (result) {
+                        totalScore += result;
+                    }
+                }
+            });
+            setProgressBar($el, totalScore);
+            return totalScore;
+        },
+
+        methods = {
+            init: function (settings) {
+                var self = this,
+                    allOptions = $.extend(options, settings);
+
+                this.each(function (idx, el) {
+                    var $el = $(el);
+
+                    $el.data("pwstrength", $.extend({}, allOptions));
+
+                    $el.on("keyup", function (event) {
+                        var localOptions = $el.data("pwstrength");
+                        localOptions.errors = [];
+                        calculateScore.call(self, $el);
+                        if ($.isFunction(localOptions.onKeyUp)) {
+                            localOptions.onKeyUp(event);
+                        }
+                    });
+
+                    initProgressBar(allOptions, $el);
+
+                    if (allOptions.showVerdictsInitially) {
+                        initVerdict(allOptions, $el, allOptions.verdicts[0]);
+                    }
+
+                    if ($.isFunction(allOptions.onLoad)) {
+                        allOptions.onLoad();
+                    }
+                });
+
+                return this;
+            },
+
+            destroy: function () {
+                this.each(function (idx, el) {
+                    var $el = $(el),
+                        localOptions = $el.data("pwstrength"),
+                        $container = getContainer(localOptions.container, $el);
+
+                    $container.find("span.password-verdict").remove();
+                    $container.find("div.progress").remove();
+                    $container.find("ul.error-list").remove();
+                    $el.removeData("pwstrength");
+                });
+            },
+
+            forceUpdate: function () {
+                var self = this;
+
+                this.each(function (idx, el) {
+                    var $el = $(el),
+                        localOptions = $el.data("pwstrength");
+
+                    localOptions.errors = [];
+                    calculateScore.call(self, $el);
+                });
+            },
+
+            outputErrorList: function () {
+                this.each(function (idx, el) {
+                    var output = '<ul class="error-list" style="display: none">',
+                        $el = $(el),
+                        localOptions = $el.data("pwstrength"),
+                        $container = getContainer(localOptions.container, $el),
+                        verdict;
+
+                    $container.find("ul.error-list").remove();
+                    if (localOptions.errors.length > 0) {
+                        $.each(localOptions.errors, function (i, item) {
+                            output += '<p>' + item + '</p>';
+                        });
+                        output += '</ul>';
+                        if (localOptions.viewports.errors) {
+                            $container.find(localOptions.viewports.errors).html(output);
+                        } else {
+                            output = $(output);
+                            verdict = $container.find("span.password-verdict");
+                            if (verdict.length > 0) {
+                                el = verdict;
+                            }
+                            output.insertAfter(el);
+                        }
+                    }
+                });
+            },
+
+            addRule: function (name, method, score, active) {
+                this.each(function (idx, el) {
+                    var localOptions = $(el).data("pwstrength");
+
+                    localOptions.rules[name] = active;
+                    localOptions.ruleScores[name] = score;
+                    localOptions.validationRules[name] = method;
+                });
+            },
+
+            changeScore: function (rule, score) {
+                this.each(function (idx, el) {
+                    $(el).data("pwstrength").ruleScores[rule] = score;
+                });
+            },
+
+            ruleActive: function (rule, active) {
+                this.each(function (idx, el) {
+                    $(el).data("pwstrength").rules[rule] = active;
+                });
+            }
+        };
+
+    $.fn.pwstrength = function (method) {
+        var result;
+
+        if (methods[method]) {
+            result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+        } else if (typeof method === "object" || !method) {
+            result = methods.init.apply(this, arguments);
+        } else {
+            $.error("Method " +  method + " does not exist on jQuery.pwstrength");
+        }
+
+        return result;
+    };
+}(jQuery));
+
+// Setup the password strength meter
+function setupPasswordStrengthMeter () {
+    "use strict";
+    var options = {
+        scores: [10, 26, 40, 50],
+        minChar: 5,
+        bootstrap3: true,
+        showVerdicts: false,
+        usernameField: "#loginname",
+        errorMessages:
+        {
+            password_too_short: '<dl><dt><i class="fa fa-exclamation-circle fa-1-5x text-danger"></i>The Password is too short.</dt><dd>must be at least 5 characters</dd></dl>',
+            same_as_username: '<dl><dt><i class="fa fa-exclamation-triangle fa-1-5x text-warning"></i>Password contains username</dt></dl>',
+            email_as_password: '<dl><dt><i class="fa fa-exclamation-triangle fa-1-5x text-warning"></i>Password contains email address</dt></dl>',
+            repeated_character: '<dl><dt><i class="fa fa-exclamation-triangle fa-1-5x text-warning"></i>Try to avoid repetitions</dt></dl>',
+            no_character_classes: '<dl><dt><i class="fa fa-exclamation-triangle fa-1-5x text-warning"></i>Use different character classes</dt></dl>'
+        },
+        onKeyUp: function (evt) {
+            $('input[name="desiredpassword"]').popover('destroy');
+            $(evt.target).pwstrength("outputErrorList");
+            $('input[name="desiredpassword"]').popover({
+                'html': true,
+                'content': $('ul.error-list').html(),
+                'trigger': 'focus',
+                'placement': 'auto top'
+            });
+            $('input[name="desiredpassword"]').popover('show');
+            //when there is no input the progressbar goes empty
+            //and the popover disappears
+            var desiredpassval = $.trim($('input[name="desiredpassword"]').val());
+            if (desiredpassval.length == 0) {
+                $('input[name="desiredpassword"]').popover('destroy');
+                $('.progress-bar').css('width','0%');
+            }
+            //update the tick next to the desiredpassword input field
+            updateTickIcon();
+        }
+    };
+    $('input[name="desiredpassword"]').pwstrength(options);
+}
+
+// check if password >= 5 characters
+// check if confirm password input field matches with password input field
+// swap icons in the input-group-addon
+function updateTickIcon() {
+    var desiredpassval = $.trim($('input[name="desiredpassword"]').val());
+    function success(element) {
+        element.removeClass("fa-times-circle text-danger");
+        element.addClass("fa-check-circle text-success");
+    }
+    function danger(element) {
+        element.removeClass("fa-check-circle text-success");
+        element.addClass("fa-times-circle text-danger");
+    }
+    if (desiredpassval.length < 5 && $("#desiredtick").hasClass("text-success")) {
+        danger($("#desiredtick"));
+    }
+    else if (desiredpassval.length >= 5 && $("#desiredtick").hasClass("text-danger")) {
+        success($("#desiredtick"));
+    }
+    if ($("#confirmpass").val() == desiredpassval && desiredpassval.length >= 5) {
+        success($("#confirmtick"));
+    }
+    else if ($("#confirmtick").hasClass("text-success")) {
+        danger($("#confirmtick"));
+    }
+}
+
+// document ready handler
+$(document).ready(function() {
+    setupPasswordStrengthMeter();
+});
--
1.8.4

>From 0088f80ece466d6e61466e5fb940032d3929d1f0 Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeis...@suse.de>
Date: Thu, 19 Dec 2013 10:59:05 +0100
Subject: [PATCH 3/3] Including password strength meter script. Adding a
 tick icon as a bootstrap input-group-addon to the password input fields.

---
 .../WEB-INF/pages/admin/multiorg/orgcreate.jsp     | 31 +++++++++++++++-------
 .../fragments/user/edit_user_table_rows.jspf       | 17 +++++++++---
 .../WEB-INF/pages/user/create/usercreate.jsp       | 17 +++++++++---
 3 files changed, 50 insertions(+), 15 deletions(-)

diff --git a/java/code/webapp/WEB-INF/pages/admin/multiorg/orgcreate.jsp b/java/code/webapp/WEB-INF/pages/admin/multiorg/orgcreate.jsp
index 0fa4bd5..3f16d34 100644
--- a/java/code/webapp/WEB-INF/pages/admin/multiorg/orgcreate.jsp
+++ b/java/code/webapp/WEB-INF/pages/admin/multiorg/orgcreate.jsp
@@ -38,7 +38,7 @@
                 <div class="col-lg-6">
                     <html:text property="login" size="15"
                                styleClass="form-control"
-                               maxlength="45" styleId="login" />
+                               maxlength="45" styleId="loginname" />
                     <span class="help-block">
                         <strong><bean:message key="tip" /></strong>
                         <bean:message key="org.login.tip" arg0="${rhn:getConfig('java.min_user_len')}" /><br/>
@@ -47,17 +47,23 @@
                 </div>
             </div>
 
+            <script type="text/javascript" src="/javascript/spacewalk-pwstrength.js"></script>
             <div class="form-group">
                 <label class="col-lg-3 control-label" for="desiredpass">
                     <bean:message key="desiredpass" />
                     <span name="password-asterisk" class="required-form-field">*</span>:
                 </label>
                 <div class="col-lg-6">
-                    <html:password property="desiredpassword"
-                                   size="15"
-                                   styleClass="form-control"
-                                   maxlength="32"
-                                   styleId="desiredpass" />
+                    <div id="desiredpassword-input-group" class="input-group">
+                        <html:password property="desiredpassword"
+                                       size="15"
+                                       styleClass="form-control"
+                                       maxlength="32"
+                                       styleId="desiredpass" />
+                        <span class="input-group-addon">
+                            <i class="fa fa-times-circle text-danger fa-1-5x" id="desiredtick"></i>
+                        </span>
+                    </div>
                 </div>
             </div>
 
@@ -67,9 +73,15 @@
                     <span name="password-asterisk" class="required-form-field">*</span>:
                 </label>
                 <div class="col-lg-6">
-                    <html:password property="desiredpasswordConfirm" size="15"
-                                   styleClass="form-control"
-                                   maxlength="32" styleId="confirmpass"/>
+                    <div class="input-group">
+                        <html:password property="desiredpasswordConfirm" size="15"
+                                       styleClass="form-control"
+                                       onkeyup="updateTickIcon()"
+                                       maxlength="32" styleId="confirmpass"/>
+                        <span class="input-group-addon">
+                            <i class="fa fa-times-circle text-danger fa-1-5x" id="confirmtick"></i>
+                        </span>
+                    </div>
                 </div>
             </div>
 
@@ -168,6 +180,7 @@
                 }
             }
         </script>
+
     </body>
 </html>
 
diff --git a/java/code/webapp/WEB-INF/pages/common/fragments/user/edit_user_table_rows.jspf b/java/code/webapp/WEB-INF/pages/common/fragments/user/edit_user_table_rows.jspf
index 60844c6..0a91010 100644
--- a/java/code/webapp/WEB-INF/pages/common/fragments/user/edit_user_table_rows.jspf
+++ b/java/code/webapp/WEB-INF/pages/common/fragments/user/edit_user_table_rows.jspf
@@ -2,7 +2,7 @@
 
 <div class="form-group">
     <label class="col-lg-3 control-label"><bean:message key="username.displayname"/></label>
-    <div class="col-lg-6"><c:out escapeXml="true" value="${user.login}" /></div>
+    <div id="loginname" class="col-lg-6 form-control-static"><c:out escapeXml="true" value="${user.login}" /></div>
 </div>
 
 <div class="form-group">
@@ -44,17 +44,28 @@
     </c:if>
 </rhn:require>
 
+<script type="text/javascript" src="/javascript/spacewalk-pwstrength.js"></script>
 <div class="form-group">
     <label class="col-lg-3 control-label"><bean:message key="password.displayname"/></label>
     <div class="col-lg-6">
-        <html:password property="desiredpassword" styleClass="form-control" maxlength="${passwordLength}"/>
+        <div id="desiredpassword-input-group" class="input-group">
+            <html:password property="desiredpassword" styleClass="form-control" maxlength="${passwordLength}"/>
+            <span class="input-group-addon">
+                <i class="fa fa-check-circle text-success fa-1-5x" id="desiredtick"></i>
+            </span>
+        </div>
     </div>
 </div>
 
 <div class="form-group">
     <label class="col-lg-3 control-label"><bean:message key="confirmpass.displayname"/></label>
     <div class="col-lg-6">
-        <html:password property="desiredpasswordConfirm" styleClass="form-control" maxlength="${passwordLength}"/>
+        <div class="input-group">
+            <html:password property="desiredpasswordConfirm" styleClass="form-control" onkeyup="updateTickIcon()" maxlength="${passwordLength}" styleId="confirmpass"/>
+            <span class="input-group-addon">
+                <i class="fa fa-check-circle text-success fa-1-5x" id="confirmtick"></i>
+            </span>
+        </div>
     </div>
 </div>
 
diff --git a/java/code/webapp/WEB-INF/pages/user/create/usercreate.jsp b/java/code/webapp/WEB-INF/pages/user/create/usercreate.jsp
index c673477..ea27917 100644
--- a/java/code/webapp/WEB-INF/pages/user/create/usercreate.jsp
+++ b/java/code/webapp/WEB-INF/pages/user/create/usercreate.jsp
@@ -43,21 +43,32 @@
               <tr>
                 <td><label for="login"><rhn:required-field key="desiredlogin"/>:</label></td>
                 <td>
-                  <html:text property="login" styleClass="form-control" maxlength="${loginLength}" styleId="login"/>
+                  <html:text property="login" styleClass="form-control" maxlength="${loginLength}" styleId="loginname"/>
                 </td>
               </tr>
               <tr>
                 <td><label for="desiredpass"><bean:message key="desiredpass" /><span name="password-asterisk"
                       class="required-form-field">*</span>:</td></label>
                 <td>
-                  <html:password property="desiredpassword" styleClass="form-control" size="15" maxlength="${passwordLength}"/>
+                  <script type="text/javascript" src="/javascript/spacewalk-pwstrength.js"></script>
+                  <div id="desiredpassword-input-group" class="input-group">
+                      <html:password property="desiredpassword" styleClass="form-control" size="15" maxlength="${passwordLength}"/>
+                      <span class="input-group-addon">
+                          <i class="fa fa-times-circle text-danger fa-1-5x" id="desiredtick"></i>
+                      </span>
+                  </div>
                 </td>
               </tr>
               <tr>
                 <td><label for="confirmpass"><bean:message key="confirmpass" /><span name="password-asterisk"
                       class="required-form-field">*</span>:</label></td>
                 <td>
-                  <html:password styleClass="form-control" property="desiredpasswordConfirm" size="15" maxlength="${passwordLength}" styleId="confirmpass"/>
+                  <div class="input-group">
+                      <html:password styleClass="form-control" property="desiredpasswordConfirm" onkeyup="updateTickIcon()" size="15" maxlength="${passwordLength}" styleId="confirmpass"/>
+                      <span class="input-group-addon">
+                          <i class="fa fa-times-circle text-danger fa-1-5x" id="confirmtick"></i>
+                      </span>
+                  </div>
                 </td>
               </tr>
               <c:if test="${displaypam == 'true' && account_type != 'create_sat'}">
--
1.8.4

_______________________________________________
Spacewalk-devel mailing list
Spacewalk-devel@redhat.com
https://www.redhat.com/mailman/listinfo/spacewalk-devel

Reply via email to