Viewing File: /usr/local/cpanel/base/cjt/password.js

/* jshint -W108 */
// check to be sure the CPANEL global object already exists
if (typeof CPANEL == "undefined" || !CPANEL) {
    alert("You must include the CPANEL global object before including password.js!");
} else {
    if (!window.LOCALE) {
        window.LOCALE = new CPANEL.Locale();
    }

    /**
        The password module contains methods used for random password generation, strength validation, etc
        @module password
*/

    /**
        The password class contains methods used for random password generation, strength validation, etc
        @class password
        @namespace CPANEL
        @extends CPANEL
*/
    CPANEL.password = {

        // this function
        setup: function(password1_el, password2_el, strength_bar_el, password_strength, create_strong_el, why_strong_link_el, why_strong_text_el, min_length) {

            // check that we have received enough arguments
            if (YAHOO.util.Dom.inDocument(password1_el) == false) {
                alert("CPANEL.password.setup error: password1_el argument does not exist in the DOM!");
            }
            if (YAHOO.util.Dom.inDocument(password2_el) == false) {
                alert("CPANEL.password.setup error: password2_el argument does not exist in the DOM!");
            }
            if (YAHOO.util.Dom.inDocument(strength_bar_el) == false) {
                alert("CPANEL.password.setup error: strength_bar_el argument does not exist in the DOM!");
            }
            if (CPANEL.validate.positive_integer(password_strength) == false && password_strength != -1) {
                alert("CPANEL.password.setup error: password strength is not a positive integer!");
            }

            // handle optional arguments and set default value
            if (typeof min_length == "undefined" || CPANEL.validate.integer(min_length) == false) {
                min_length = 5;
            }

            // create the strength bar
            var password_bar = new CPANEL.password.strength_bar(strength_bar_el);

            // function to verify password strength
            var verify_password_strength = function() {
                if (password_bar.current_strength >= password_strength) {
                    return true;
                }
                return false;
            };

            // update the strength bar when we type in the password field
            password_bar.attach(password1_el, function() {
                strength_validator.verify();
            });

            // create a validator for password strength
            var strength_validator = new CPANEL.validate.validator(LOCALE.maketext("Password Strength"));
            if (password_strength != -1) {
                strength_validator.add(password1_el, "min_length(%input%, 1)", LOCALE.maketext("Password cannot be empty."));
            }
            if (min_length > 0) {
                strength_validator.add(password1_el, "min_length(%input%," + min_length + ")", LOCALE.maketext("Passwords must be at least [quant,_1,character,characters] long.", min_length));
            }
            if (password_strength > 0) {
                strength_validator.add(password1_el, verify_password_strength, LOCALE.maketext("Password strength must be at least [numf,_1].", password_strength));
            }
            strength_validator.attach();

            // create a validator for the two passwords matching
            var matching_validator = new CPANEL.validate.validator(LOCALE.maketext("Passwords Match"));
            matching_validator.add(password2_el, "equals('" + password1_el + "', '" + password2_el + "')", LOCALE.maketext("Passwords do not match."));
            matching_validator.attach();

            // create strong password link
            if (YAHOO.util.Dom.inDocument(create_strong_el) == true) {

                // function that executes when a user clicks the "use" button on the strong password dialog
                var fill_in_strong_password = function(strong_pass) {

                    // fill in the two fields
                    YAHOO.util.Dom.get(password1_el).value = strong_pass;
                    YAHOO.util.Dom.get(password2_el).value = strong_pass;

                    // verify the matching validator
                    matching_validator.verify();

                    // update the strength bar
                    password_bar.check_strength(password1_el, function() {

                        // verify the password strength
                        strength_validator.verify();
                    });
                };

                // add an event handler for the "create strong password" link
                YAHOO.util.Event.on(create_strong_el, "click", function() {
                    CPANEL.password.generate_password(fill_in_strong_password);
                });
            }

            // add an event handler for the "why?" link
            if (YAHOO.util.Dom.inDocument(why_strong_link_el) == true && YAHOO.util.Dom.inDocument(why_strong_text_el) == true) {
                CPANEL.panels.create_help(why_strong_link_el, why_strong_text_el);
            }

            // return the two validator objects we created
            return [strength_validator, matching_validator];
        },

        strength_bar: function(el) {

            // save each request so that we can cancel the last one when we fire off a new one
            this.ajax_request;

            // get the password bar element
            if (YAHOO.util.Dom.inDocument(el) == false) {
                alert("Failed to initialize password strength bar." + "\n" + "Could not find " + el + " in the DOM.");
            }
            this.strength_bar_el = YAHOO.util.Dom.get(el);

            // initialize the password strength at 0
            this.current_strength = 0;
            CPANEL.password.show_strength_bar(this.strength_bar_el, 0);

            // attach the password bar to an input field
            this.attach = function(input_el, callback_function) {

                if (YAHOO.util.Dom.inDocument(input_el) == false) {
                    alert("Failed to attach strength bar object.\n Could not find " + input_el + "in the DOM.");
                } else {

                    // if no callback function was added create one that does nothing
                    if (typeof (callback_function) === "undefined") {
                        callback_function = function() {};
                    }

                    var callback_args = {
                        "input_el": input_el,
                        "func": callback_function
                    };

                    // add event handlers to the input field
                    if (CPANEL.dom.has_oninput) {
                        EVENT.on(input_el, "input", oninput_listener, callback_args);
                    } else {
                        EVENT.on(input_el, "keyup", oninput_listener, callback_args);
                        EVENT.on(input_el, "change", oninput_listener, callback_args);

                        // The delay seems to be necessary.
                        EVENT.on(input_el, "paste", function() {
                            var that = this,
                                args = arguments;
                            setTimeout(function() {
                                oninput_listener.apply(that, args);
                            }, 5);
                        }, callback_args);
                    }
                }
            };

            // reset the bar
            this.destroy = function() {
                this.strength_bar_el.innerHTML = "";
                this.current_strength = 0;
            };

            // public wrapper for the check strength function
            this.check_strength = function(input_el, callback_function) {
                _check_strength(null, {
                    "input_el": input_el,
                    "func": callback_function
                });
            };

            // PRIVATE METHODS
            var that = this;

            var pw_strength_timeout;
            var oninput_listener = function(e, o) {
                clearTimeout(pw_strength_timeout);

                // if a request is currently active cancel it
                if (YAHOO.util.Connect.isCallInProgress(that.ajax_request)) {
                    YAHOO.util.Connect.abort(that.ajax_request);
                }

                var password = DOM.get(o.input_el).value;

                if (password in cached_strengths) {
                    that.current_strength = cached_strengths[password];
                    _update_strength_bar(cached_strengths[password]);
                    o.func();
                } else {
                    pw_strength_timeout = setTimeout(function() {
                        _check_strength(e, o);
                    }, CPANEL.password.keyup_delay_ms);
                }
            };

            var cached_strengths = {
                "": 0
            };

            // check the strength of the password
            var _check_strength = function(e, o) {


                // show a loading indicator
                _update_strength_bar(null);

                // set the value
                var password = YAHOO.util.Dom.get(o.input_el).value;

                // create the callback functions
                var callback = {
                    success: function(o2) {

                        // the responseText should be JSON data
                        try {
                            var response = YAHOO.lang.JSON.parse(o2.responseText);
                        } catch (e) {

                            // TODO: write CPANEL.errors.json();
                            alert("JSON Parse Error: Please refresh the page and try again.");
                            return;
                        }

                        // make sure strength is an integer between 0 and 100
                        var strength = parseInt(response.strength);

                        if (strength < 0) {
                            strength = 0;
                        } else if (strength > 100) {
                            strength = 100;
                        }

                        cached_strengths[password] = strength;

                        that.current_strength = strength;

                        _update_strength_bar(strength);

                        // run the callback function
                        o.func();
                    },

                    failure: function(o2) {
                        var error = '<table style="width: 100%; height: 100%; padding: 0px; margin: 0px"><tr>';
                        error += '<td style="padding: 0px; margin: 0px; text-align: center" valign="middle">AJAX Error: Try Again</td>';
                        error += "</tr></table>";
                        YAHOO.util.Dom.get(that.strength_bar_el).innerHTML = error;
                    }
                };

                // if a request is currently active cancel it
                if (YAHOO.util.Connect.isCallInProgress(that.ajax_request)) {
                    YAHOO.util.Connect.abort(that.ajax_request);
                }

                // send the AJAX request
                var url = CPANEL.urls.password_strength();
                that.ajax_request = YAHOO.util.Connect.asyncRequest("POST", url, callback, "password=" + encodeURIComponent(password));
            };

            var _update_strength_bar = function(strength) {
                CPANEL.password.show_strength_bar(that.strength_bar_el, strength);
            };
        },

        /**
                Shows a password strength bar for a given strength.
                @method show_strength_bar
                @param {DOM element} el The DOM element to put the bar.  Should probably be a div.
                @param {integer, null} strength The strength of the bar, or null for "updating".
        */
        show_strength_bar: function(el, strength) {

            el = YAHOO.util.Dom.get(el);

            // NOTE: it would probably be more appropriate to move these colors into a CSS file, but I want the CJT to be self-contained.  this solution is fine for now
            var phrase, color;
            if (strength === null) {
                phrase = LOCALE.maketext("Loading …");
            } else if (strength >= 80) {
                phrase = LOCALE.maketext("Very Strong");
                color = "#8FFF00"; // lt green
            } else if (strength >= 60) {
                phrase = LOCALE.maketext("Strong");
                color = "#C5FF00"; // chartreuse
            } else if (strength >= 40) {
                phrase = LOCALE.maketext("OK");
                color = "#F1FF4D"; // yellow
            } else if (strength >= 20) {
                phrase = LOCALE.maketext("Weak");
                color = "#FF9837"; // orange
            } else if (strength >= 0) {
                phrase = LOCALE.maketext("Very Weak");
                color = "#FF0000"; // red
            }

            var html;

            // container div with relative positioning, height/width set to 100% to fit the container element
            html = '<div style="position: relative; width: 100%; height: 100%">';

            // phrase div fits the width and height of the container div and has its text vertically and horizontally centered; has a z-index of 1 to put it above the color bar div
            html += '<div style="position: absolute; left: 0px; width: 100%; height: 100%; text-align: center; z-index: 1; padding: 0px; margin: 0px' + (strength === null ? "; font-style:italic; color:graytext" : "") + '">';
            html += '<table style="width: 100%; height: 100%; padding: 0px; margin: 0px"><tr style="padding: 0px; margin: 0px"><td valign="middle" style="padding: 0px; margin: 0px">' + phrase + (strength !== null ? (" (" + strength + "/100)") : "") + "</td></tr></table>"; // use a table to vertically center for greatest compatibility
            html += "</div>";

            if (strength !== null) {

                // color bar div fits the width and height of the container div and width changes depending on the strength of the password
                html += '<div style="position: absolute; left: 0px; width: ' + strength + "%; height: 100%; background-color: " + color + ';"></div>';
            }

            // close the container div
            html += "</div>";

            el.innerHTML = html;
        },

        // Time in ms between last keyup and sending off the password strength CGI
        // AJAX call. Useful to prevent an excess of aborted CGI calls.
        keyup_delay_ms: 500,


        /**
                Creates a strong password
                @method create_password
                @param {object} options (optional) options to specify limitations on the password
                @return {string} a strong password
        */
        create_password: function(options) {

            // set the length
            var length;
            if (CPANEL.validate.positive_integer(options.length) === false) {
                length = 12;
            } else {
                length = options.length;
            }

            // possible password characters
            var uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            var lowercase = "abcdefghijklmnopqrstuvwxyz";
            var numbers = "0123456789";
            var symbols = "!@#$%^&*()-_=+{}[];,.?~";

            var chars = "";
            if (options.uppercase == true) {
                chars += uppercase;
            }
            if (options.lowercase == true) {
                chars += lowercase;
            }
            if (options.numbers == true) {
                chars += numbers;
            }
            if (options.symbols == true) {
                chars += symbols;
            }

            // generate the thing
            var password = "";
            for (var i = 0; i < length; i++) {
                var rnum = Math.floor(Math.random() * chars.length);
                password += chars.substring(rnum, rnum + 1);
            }

            return password;
        },

        /**
                Pops up a modal dialog box containing a strong password.
                @method generate_password
                @param {function} use_password_function A function that gets executed when the user clicks the "Use Password" button on the modal box.  The function will have the password string sent to it as its first argument.
                @param {integer} length (optional) the length of the random password to generate.  defaults to 15
                launch_el - the element you clicked on to launch the password generator (it appears relative to this element)
        */
        generate_password: function(use_password_function, length) {

            // create the password
            var default_options = {
                length: 12,
                uppercase: true,
                lowercase: true,
                numbers: true,
                symbols: true
            };
            var password = this.create_password(default_options);

            // remove the panel if it already exists
            if (YAHOO.util.Dom.inDocument("generate_password_panel") == true) {
                var remove_me = YAHOO.util.Dom.get("generate_password_panel");
                YAHOO.util.Event.purgeElement(remove_me, true);
                remove_me.parentNode.removeChild(remove_me);
            }

            // create the panel
            var panel_options = {
                width: "380px",
                fixedcenter: true,
                close: true,
                draggable: false,
                zindex: 1000,
                visible: true,
                modal: true,
                postmethod: "manual",
                hideaftersubmit: false,
                strings: {
                    "close": LOCALE.maketext("Close")
                },
                buttons: [{
                    text: LOCALE.maketext("Use Password"),
                    handler: function() {
                        this.submit();
                    }
                }, {
                    text: LOCALE.maketext("Cancel"),
                    handler: function() {
                        this.cancel();
                    }
                }],
                effect: {
                    effect: CPANEL.animate.ContainerEffect.FADE_MODAL,
                    duration: 0.25
                }
            };
            var panel = new YAHOO.widget.Dialog("generate_password_panel", panel_options);

            panel.renderEvent.subscribe(function() {
                var buttons = this.getButtons();
                YAHOO.util.Dom.addClass(buttons[0], "input-button disabled");
                YAHOO.util.Dom.addClass(buttons[1], "input-button");
            });


            // header
            var header = '<div class="header"><div class="lt"></div>';
            header += "<span>" + LOCALE.maketext("Password Generator") + "</span>";
            header += '<div class="rt"></div></div>';
            panel.setHeader(header);

            // body
            var body = '<div id="generate_password_body_div">';

            body += '<table id="generate_password_table">';
            body += "<tr>";
            body += '<td><input id="generate_password_input_field" type="text" value="' + password + '" size="27"  /></td>';
            body += "</tr>";
            body += "<tr>";
            body += '<td><input type="button" class="input-button btn btn-primary" value="' + LOCALE.maketext("Generate Password") + '" id="generate_password_reload" /></td>';
            body += "</tr>";
            body += "<tr>";
            body += '<td><span class="action_link" id="generate_password_toggle_advanced_options">' + LOCALE.maketext("Advanced Options") + " &raquo;</span>";

            body += '<div id="generate_password_advanced_options" style="display: none"><table style="width: 100%">';
            body += "<tr>";
            body += '<td colspan="2">' + LOCALE.maketext("Length") + ': <input type="text" id="generate_password_length" size="2" maxlength="2" value="12" /> (10-18)</td>';
            body += "</tr>";
            body += "<tr>";
            body += '<td width="50%">' + LOCALE.maketext("Alpha Characters") + ":</td>";
            body += '<td width="50%">' + LOCALE.maketext("Non Alpha Characters") + ":</td>";
            body += "</tr><tr>";
            body += '<td><input type="radio" name="generate_password_alpha" id="generate_password_mixed_alpha" checked="checked" /> <label for="generate_password_mixed_alpha">' + LOCALE.maketext("Both") + " (aBcD)</label></td>";
            body += '<td><input type="radio" name="generate_password_nonalpha" id="generate_password_mixed_nonalpha" checked="checked" /> <label for="generate_password_mixed_nonalpha">' + LOCALE.maketext("Both") + " (1@3$)</label></td>";
            body += "</tr><tr>";
            body += '<td><input type="radio" name="generate_password_alpha" id="generate_password_lowercase" /> <label for="generate_password_lowercase">' + LOCALE.maketext("Lowercase") + " (abc)</label></td>";
            body += '<td><input type="radio" name="generate_password_nonalpha" id="generate_password_numbers" /> <label for="generate_password_numbers">' + LOCALE.maketext("Numbers") + " (123)</label></td>";
            body += "</tr><tr>";
            body += '<td><input type="radio" name="generate_password_alpha" id="generate_password_uppercase" /> <label for="generate_password_uppercase">' + LOCALE.maketext("Uppercase") + " (ABC)</label></td>";
            body += '<td><input type="radio" name="generate_password_nonalpha" id="generate_password_symbols" /> <label for="generate_password_symbols">' + LOCALE.maketext("Symbols") + " (@#$)</label></td>";
            body += "</tr>";
            body += "</table></div>";

            body += "</td></tr></table>";

            body += '<p><input type="checkbox" id="generate_password_confirm" /> <label for="generate_password_confirm">' + LOCALE.maketext("I have copied this password in a safe place.") + "</label></p>";

            body += "</div>";
            panel.setBody(body);

            // render the panel
            panel.render(document.body);

            if (CPANEL.password.fade_from) {
                CPANEL.password.fade_from.fade_to(panel);
            }

            // make sure the input button is not checked (defeat browser caching)
            YAHOO.util.Dom.get("generate_password_confirm").checked = false;


            panel.validate = function() {
                return DOM.get("generate_password_confirm").checked;
            };
            panel.submitEvent.subscribe(function() {
                use_password_function(YAHOO.util.Dom.get("generate_password_input_field").value);
                this.cancel();
            });
            panel.cancelEvent.subscribe(function() {
                if (CPANEL.password.fade_from) {
                    panel.fade_to(CPANEL.password.fade_from);
                }
            });
            panel.hideEvent.subscribe(panel.destroy, panel, true);


            YAHOO.util.Event.on("generate_password_confirm", "click", function() {
                this.checked ? YAHOO.util.Dom.removeClass(panel.getButtons()[0], "disabled") : YAHOO.util.Dom.addClass(panel.getButtons()[0], "disabled");
            });

            // select the input field when the user clicks on it
            YAHOO.util.Event.on("generate_password_input_field", "click", function() {
                YAHOO.util.Dom.get("generate_password_input_field").select();
            });

            YAHOO.util.Event.on("generate_password_toggle_advanced_options", "click", function() {
                CPANEL.animate.slide_toggle("generate_password_advanced_options");
            });

            // get the password options from the interface
            var get_password_options = function() {
                var options = {};

                var length_el = YAHOO.util.Dom.get("generate_password_length");
                var length = length_el.value;
                if (CPANEL.validate.positive_integer(length) == false) {
                    length = 12;
                } else if (length < 10) {
                    length = 10;
                } else if (length > 18) {
                    length = 18;
                }
                length_el.value = length;
                options.length = length;

                if (YAHOO.util.Dom.get("generate_password_mixed_alpha").checked == true) {
                    options.uppercase = true;
                    options.lowercase = true;
                } else {
                    options.uppercase = YAHOO.util.Dom.get("generate_password_uppercase").checked;
                    options.lowercase = YAHOO.util.Dom.get("generate_password_lowercase").checked;
                }

                if (YAHOO.util.Dom.get("generate_password_mixed_nonalpha").checked == true) {
                    options.numbers = true;
                    options.symbols = true;
                } else {
                    options.numbers = YAHOO.util.Dom.get("generate_password_numbers").checked;
                    options.symbols = YAHOO.util.Dom.get("generate_password_symbols").checked;
                }

                return options;
            };

            // generate a new password and select the input field text when the user clicks the refresh text
            var generate_new_password = function() {
                YAHOO.util.Dom.get("generate_password_input_field").value = CPANEL.password.create_password(get_password_options());
            };
            if (this.beforeExtendPanel) {
                this.beforeExtendPanel();
                YAHOO.util.Dom.get("generate_password_input_field").value = CPANEL.password.create_password(get_password_options());
            }

            YAHOO.util.Event.on("generate_password_reload", "click", generate_new_password);

            // watch the advanced options inputs
            var password_options = [
                "generate_password_mixed_alpha", "generate_password_uppercase", "generate_password_lowercase",
                "generate_password_mixed_nonalpha", "generate_password_numbers", "generate_password_symbols"
            ];
            YAHOO.util.Event.on(password_options, "change", generate_new_password);
        }

    }; // end password object
} // end else statement
Back to Directory File Manager