1. #1
    Sencha User walldorff's Avatar
    Join Date
    Mar 2008
    Location
    Antwerp, Belgium
    Posts
    164
    Vote Rating
    2
    walldorff is on a distinguished road

      0  

    Lightbulb Ext.ux.PasswordGenerator

    Ext.ux.PasswordGenerator


    I've created a small function to generate passwords. This can paricularly be used in signup forms, in which the user is required to fill in the password 2 times, with a generate-button next to the first password field. Please feel free to use, comment and/or improve.

    Source and demo here.

    FormPanel
    PHP Code:
    var form = new Ext.form.FormPanel({
        
    renderTo      'form-ct'
        
    ,monitorValid true
        
    ,title        'Sign Up Form'
        
    ,width        340
        
    ,labelWidth   100
        
    ,labelAlign   'right'
        
    ,buttonAlign  'center'
        
    ,bodyStyle    'padding:5px;'
        
    ,frame        true
        
    ,url          'save-form.php'
        
    ,defaultType  'textfield'
        
    ,defaults     : {
            
    labelSeparator ''
            
    ,allowBlank    false
            
    ,selectOnFocus true
        
    }
        ,
    items: [{
                
    id          'len'
                
    ,fieldLabel 'Password Length'
                
    ,width      50
                
    ,minValue   10
                
    ,maxValue   15
                
    ,value      10
            
    },{
                
    name        'name'
                
    ,fieldLabel 'Name'
            
    },{
                
    id           'p1'
                
    ,name        'pass1'
                
    ,fieldLabel  'Password'
                
    ,minLength   10 // at least 8 for the safest results
                
    ,maxLength   20
            
    },{
                
    id           'p2'
                
    ,name        'pass2'
                
    ,fieldLabel  'Confirm password'
            
    }
            ,new 
    Ext.ux.PasswordGenerator.Button({
                
    handler:function(btn) {
                    var 
    length Ext.getCmp('len').getValue();
                    
    btn.generate('p1''p2'length);
                }
            })
        ]
        ,
    buttons      : [{
            
    text      'Sign up'
            
    ,formBind true
        
    }]
    }); 
    Ext.ux.PasswordGenerator.Button class:
    PHP Code:
    /**
     * Password generator button, useful in signup/registration forms.
     *
     * @author Roland van Wanrooy (walldorff)
     * @date   March 8, 2010
     */


    Ext.namespace('Ext.ux.PasswordGenerator');

    /**
     * @class Ext.ux.PasswordGenerator.Button
     * @extends Ext.Button
     *
     * A specific {@link Ext.Button} that can be used to populate 2 password fields with a
     * very strong password, which meets the highest security standards as advertises by
     * The Password Meter (http://www.passwordmeter.com/)).
     *
     * Instantiate this class anywhere in the form and apply the class method generate()
     * in the handler of the button.
     *
     * @param {Object} config The config object
     */

    Ext.ux.PasswordGenerator.Button Ext.extend(Ext.Button, {

        
    /**
         * @cfg {String} text The button text. Defaults to "Generate".
         */
        
    text             'Generate'

        
    /**
         * @cfg {String} tooltip The tooltip of this component.
         * Defaults to "Generate password".
         */
        
    ,tooltip         'Generate password'


        
    ,initComponent     : function() {
            
    Ext.apply(this, {
                
    /**
                 * @cfg {Array} stores The 4 stores contain the 4 different strings, from
                 * which the password shall be generated. Each store contains a stock (the
                 * specific string), an id for readability purpose and 2 boolean flags to
                 * indicate whether the store is closed for the moment, or completely out
                 * of stock (hence closed permanently).
                 *
                 * When a character is fetched from the store, the following actions will
                 * be taken:
                 * - the store will be temporarily closed to prevent consecutive characters
                 *   from the same store AND to ensure that all stores are "visited";
                 * - the character will be removed from the stock to prevent multiple
                 *   occurrences of the same character;
                 * - if the character is from the "lower" or "upper" store, then the "lower"
                 *   or "upper" version of the fetched one will be further ignored, because
                 *   this standard is case-insensitive;
                 * - if there are no more characters left in the stock, the store will be
                 *   closed permanently by assigning TRUE to the flag <em>soldOut</em>.
                 */
                
    stores         : [{
                        
    id         'lower'
                        
    ,open    true
                        
    // not included: "l"(lower L) to prevent mixing up with "1"(one)
                        
    ,stock    'abcdefghijkmnopqrstuvwxyz'
                        
    ,soldOutfalse
                    
    },{
                        
    id         'upper'
                        
    ,open    true
                        
    // not included: "I"(upper i) and "O"(upper o) to prevent mixing
                        // up with "1"(one) and "0"(zero)
                        
    ,stock    'ABCDEFGHJKLMNPQRSTUVWXYZ'
                        
    ,soldOutfalse
                    
    },{
                        
    id         'number'
                        
    ,open    true
                        
    // not included: "1"(one) and "0"(zero) to prevent mixing up
                        // with "I"(upper i)/"l"(lower L) and "O"(upper o)
                        
    ,stock    '23456789'
                        
    ,soldOutfalse
                    
    },{
                        
    id         'token'
                        
    ,open    true
                        
    ,stock    '/!@#$%^&*()_+[]~<>-:{}.,;:'
                        
    ,soldOutfalse
                
    }]
            
                
    /**
                 * @cfg {Object} store Current store, that provides the current character.
                 */
                
    ,store             null
            
                
    /**
                 * @cfg {Number} stockSize The lenth of the character string in the store.
                 * Short notation of <em>this.stores[n].stock.length</em>.
                 */
                
    ,stockSize        0
            
                
    /**
                 * @cfg {Number} pickPos Position of the current fetch character in the stock.
                 */
                
    ,pickPos         0
            
                
    /**
                 * @cfg {String} pickChar The fetched character.
                 */
                
    ,pickChar         ''
            
                
    /**
                 * @cfg {String} password The placeholder for the newbuild password.
                 */
                
    ,password        ''
            
    });
            
    Ext.ux.PasswordGenerator.Button.superclass.initComponent.call(this);
          }


        
    /**
         * Called by the {@link Ext.Button} handler. 
         * Main method, which generates the password.
         *
          * @param {String} initialPassFieldID The id of the main password field
          * @param {String} confirmPassFieldID The id of the field, from which the user
         * input in the initial password field will be checked.
          * Both password fields should have the same value.
         */
        
    ,generate: function(initialPassFieldIDconfirmPassFieldIDlength) {
            var 
    0;
            
    // array of field components
            
    var fields = new Array(Ext.getCmp(initialPassFieldID), Ext.getCmp(confirmPassFieldID));
            
    // the length of the password. Defaults to 10.
            
    var passwordSize length || initialPassFieldID.minLength || 10;

            
    // start with brand new stores in the original state
            
    this.reset();

            
    // generate
            
    while (passwordSize) {
                
    // see if all the stores are closed; if so, open them.
                
    this.openStores();
                
    // pick a store
                
    this.pickStore();
                
    // fetch a character and add it to the new password string
                
    this.fetchChar();
                
    // delete the char from the stock, so this won't be picked again
                
    this.deleteChar();
                
    // add the char to the new password string
                
    this.password += this.pickChar;

                
    i++;
            }

            
    // populate and validate the 2 password fields
              
    Ext.each(fields, function(itemindexallItems) {
                 var 
    field fields[index];
                
    field.setValue(this.password);

                if (
    field.validate()) {
                    
    // change background to indicate the valid state of the field
                    
    field.addClass('x-field-valid');
                } else {
                    
    // academical; it is very unlikely that the script reaches this point,
                    // because the password will meet the highest standards.
                    
    field.removeClass('x-field-valid');
                }
              }, 
    this);
        }

        
    /**
         * Called by {@link #generate()}. Resets all stores to the
         * original state. This method is called every time the user clicks the button.
         */
        
    ,reset        : function() {
            
    this.store         null;
            
    this.pickPos     0;
            
    this.pickChar     '';
            
    this.stockSize    0;
            
    this.password    '';
            var 
    i;

            for (
    0this.stores.lengthi++) {
                
    // short
                
    var this.stores[i];
                
    s.open true;
                
    s.soldOut false;
                switch(
    s.id) {
                    case 
    'lower' s.stock 'abcdefghijkmnopqrstuvwxyz'; break;
                    case 
    'upper' s.stock 'ABCDEFGHJKLMNPQRSTUVWXYZ'; break;
                    case 
    'number's.stock '23456789'; break;
                    case 
    'token' s.stock '/!@#$%^&*()_+[]~<>-:{}.,;:'; break;
                }
            }
        }

        
    /**
         * Gets called by {@link #generate()}. Checks if all the stores
         * are closed. Reopens the stores which have a stock. Keeps the current store
         * closed in orderto prevent consecutive chars from the same store.
         */
        
    ,openStores    : function() {
            var 
    iclosed 0;
            
    // count the closed stores
            
    for (0this.stores.lengthi++) {
                if (!
    this.stores[i].open) {
                    
    closed++;
                }
            }
            
    // if all the stores are closed, open them all up again if they're not sold out...
            
    if (closed == 4) {
                
    Ext.each(this.stores, function(itemindexallItems) {
                        
    this.stores[index].open = !this.stores[index].soldOut;
                }, 
    this);
                
    // ...except for the last one. 
                // Close the last store again; we don't want consecutives
                
    if (Ext.isObject(this.store)) {
                    
    this.store.open false;
                }
            }
        }

        
    /**
         * Called by {@link #generate()}. Determines which store will be
         * the current store. Will be limited to one of the currently open stores.
         */
        
    ,pickStore        : function() {
            
    // pick a store (index of stores)
            
    var index Math.floor(Math.random()*4);
            
    // see if it's open
            
    while ( (this.store this.storesindex ]).open == false ) {
                
    index Math.floor(Math.random()*4);
            }
            
    // short notation in class var
            
    this.stockSize this.store.stock.length;
        }

        
    /**
         * Called by {@link #generate()}. Takes one character from the
         * current store. Picks another store and checks if the password string contains an 
         * upper/lowercase equivalent of the current character.
         * For comparison of the char against the password, both have to be formatted to 
         * upper string.
         * If the comparison is successful, then this means that the char came from the upper
         * or lower stock; therefore it is inevitable to choose another stock entirely.
         */
        
    ,fetchChar        : function() {
            
    // pick a positon from which the character is pulled out of the stock
            
    this.pickPos   Math.floor(Math.random()*this.stockSize);
            
    // pick that character
            
    this.pickChar  this.store.stock.charAt(this.pickPos);

            
    // compare the char against the password, in order to determine the occurrence of other-case equivalent.
            
    while ( (this.password.toUpperCase().lastIndexOf(this.pickChar.toUpperCase())) > -) {
                
    // comparison succesful; pick a new store
                
    this.pickStore();
                
    // pick a positon from which the character is pulled out of the stock
                
    this.pickPos   Math.floor(Math.random()*this.stockSize);
                
    // pick that character
                
    this.pickChar  this.store.stock.charAt(this.pickPos);
            }

        }

        
    /**
         * Called by {@link #generate()}. Deletes the charachter from the stock and
         * checks the store for an <em>out of stock</em> status.
         * If this is the case, then this store will remain closed permanently.
         */
        
    ,deleteChar     : function() {
            
    // see if we're sold out (i.e. if the stock is empty, once this character has been removed)
            
    if (this.stockSize == 1) {
                
    this.store.soldOut true;
            } else{
                
    // we're not going to be out of stock, so remove the char.
                
    if (this.pickPos == 0) {
                    
    this.store.stock this.store.stock.substr(1);
                } else if (
    this.pickPos == this.stockSize) {
                    
    this.store.stock this.store.stock.substr(0this.stockSize-1);
                } else {
                    
    this.store.stock this.store.stock.substr(0this.pickPos) + this.store.stock.substr(this.pickPos+1);
                }
            }
            
    // close the store for now, because next the character has to be chosen from another store
            // (or because it is out of stock)
            
    this.store.open false;
        }

    }); 
    Last edited by walldorff; 4 Dec 2010 at 9:07 AM. Reason: edit url
    ExtJS 3.4, WAMP Apache 2.2.17, PHP 5.3.5, MySQL 5.5.8

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    37,330
    Vote Rating
    847
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    I think it'd be better that you can specify a length and whatever as a config. If it's not set, then open a window.
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha User walldorff's Avatar
    Join Date
    Mar 2008
    Location
    Antwerp, Belgium
    Posts
    164
    Vote Rating
    2
    walldorff is on a distinguished road

      0  

    Thumbs up


    Quote Originally Posted by mitchellsimoens View Post
    I think it'd be better that you can specify a length and whatever as a config. If it's not set, then open a window.
    I see what you mean. But what if I set the minLength to 10 and the user wants a 15 character password?

    At the moment I'm writing the validation, so this will be updated.

    Thanks for your feedback!
    ExtJS 3.4, WAMP Apache 2.2.17, PHP 5.3.5, MySQL 5.5.8

  4. #4
    Sencha User wemerson.januario's Avatar
    Join Date
    Nov 2008
    Location
    Brazil, Goias, GoiĆ¢nia
    Posts
    441
    Vote Rating
    7
    wemerson.januario will become famous soon enough

      0  

    Default Nice

    Nice


    Nice work. Congratulation.

  5. #5
    Sencha User walldorff's Avatar
    Join Date
    Mar 2008
    Location
    Antwerp, Belgium
    Posts
    164
    Vote Rating
    2
    walldorff is on a distinguished road

      0  

    Thumbs up


    Thank you, januario! There's an update on the way.
    ExtJS 3.4, WAMP Apache 2.2.17, PHP 5.3.5, MySQL 5.5.8

  6. #6
    Sencha User walldorff's Avatar
    Join Date
    Mar 2008
    Location
    Antwerp, Belgium
    Posts
    164
    Vote Rating
    2
    walldorff is on a distinguished road

      0  

    Default new version

    new version


    I finally decided to follow the suggestion of Mitchell Simoens.
    The popup window was a goner to begin with. I think nobody wants the user to decide whether the password should contain which type of characters.
    That leaves only the password length's, which can be initiated in the form config. Also the user could be offered a spinner to dynamically set the desired length.

    I've made completely new code; it is now a Ext.Button extended class. The code in post #1, as well as in the zip, are changed accordingly. See the demo.
    Last edited by walldorff; 10 Mar 2010 at 12:40 AM. Reason: typos
    ExtJS 3.4, WAMP Apache 2.2.17, PHP 5.3.5, MySQL 5.5.8