1. #1
    Ext JS Premium Member vincentc's Avatar
    Join Date
    Jul 2007
    Location
    Lyon, France
    Posts
    33
    Vote Rating
    0
    vincentc is on a distinguished road

      0  

    Question [Beta] Ext.ux.SecurePass

    [Beta] Ext.ux.SecurePass


    Hello,

    First, thanks to the team and specifically Jack S to this very great Extjs.

    I'm currently writing my first extension, that add a meter to evaluate the strength of an input password. It is based on the one of hotmail. Following the code needed:

    scripts/secure-pass.js:
    Code:
    Ext.namespace('Ext.ux');
    
    Ext.ux.SecurePass = function(config) {
        Ext.ux.SecurePass.superclass.constructor.call(this, config);
    }
     
    Ext.extend(Ext.ux.SecurePass, Ext.form.TextField, {
        /**
         * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
         * {
         *  PwdEmpty: "Please type a password, and then retype it to confirm.",
         *  PwdDifRPwd: "The new password and the confirmation password don't match. Please type the same password in both boxes.",
         *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
         *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
         *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
         *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
         *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
         *  LNInPwd: "Your password can't contain your last name. Please type a different password."
         * })
         */
        // private
        errors : {
            PwdEmpty: "Please type a password, and then retype it to confirm.",
            PwdDifRPwd: "The new password and the confirmation password don't match. Please type the same password in both boxes.",
            PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
            PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
            PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
            IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
            FNInPwd: "Your password can't contain your first name. Please type a different password.",
            LNInPwd: "Your password can't contain your last name. Please type a different password."
        },
     
        /**
         * @cfg {String/Object} Label for the strength meter (defaults to
         * 'Password strength:')
         */
        // private
        meterLabel : 'Password strength:',
     
        /**
         * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
         * ['Weak', 'Medium', 'Strong'])
         */
        // private
        pwdStrengths : ['Weak', 'Medium', 'Strong'],
     
        // private
        strength : 0,
     
        // private
        _lastPwd : null,
     
        // private
        kCapitalLetter : 0,
        kSmallLetter : 1,
        kDigit : 2,
        kPunctuation : 3,
     
        // private
        initEvents : function(){
            Ext.ux.SecurePass.superclass.initEvents.call(this);
            this.el.on('keyup', this.checkStrength,  this, {buffer:50});
        },
     
        // private
        onRender : function(ct, position){
            Ext.ux.SecurePass.superclass.onRender.call(this, ct, position);
            this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
            this.trigger = this.wrap.createChild({tag: "div", cls: "StrengthMeter "+this.triggerClass});
            if(this.meterLabel != ''){
                this.trigger.createChild({tag: "label", html: this.meterLabel});
            }
            this.trigger.createChild({tag: "div", cls: "PwdMeterBase", html: '<div class="PwdBack"><div class="PwdMeter" id="PwdMeter"></div></div>'});
            if(this.hideTrigger){
                this.trigger.setDisplayed(false);
            }
            this.setSize(this.width||'', this.height||'');
        },
     
        // private
        onDestroy : function(){
            if(this.trigger){
                this.trigger.removeAllListeners();
                this.trigger.remove();
            }
            if(this.wrap){
                this.wrap.remove();
            }
            Ext.form.TriggerField.superclass.onDestroy.call(this);
        },
     
        // private
        checkStrength : function(){
            var pwd = this.el.getValue();
            if (pwd == this._lastPwd) {
                return;
            }
     
            var strength;
            if (this.ClientSideStrongPassword(pwd)) {
                strength = 3;
            } else if(this.ClientSideMediumPassword(pwd)) {
                strength = 2;
            } else if(this.ClientSideWeakPassword(pwd)) {
                strength = 1;
            } else {
                strength = 0;
            }
     
            document.getElementById('PwdMeter').style.width = 82 * strength +'px';
            if(this.pwdStrengths != null && strength > 0) {
                document.getElementById('PwdMeter').innerHTML = '&nbsp;'+ this.pwdStrengths[strength - 1];
            } else {
                document.getElementById('PwdMeter').innerHTML = '';
            }
     
            this._lastPwd = pwd;
        },
     
        // private
        validateValue : function(value){
            if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
                return false;
            }
            if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
                 return true;
            }
            if(value.length == 0) {
                this.markInvalid(this.errors.PwdEmpty);
                return false;
            }
            if("[\x21-\x7e]*".match(value)) {
                this.markInvalid(this.errors.PwdBadChar);
                return false;
            }
            if(value.length < 6) {
                this.markInvalid(this.errors.PwdShort);
                return false;
            }
            if(value.length > 16) {
                this.markInvalid(this.errors.PwdLong);
                return false;
            }
            /*if(value.length > 0 && this.iRPwd != 'undefined' && Ext.get(this.iRPwd) != null && value != Ext.get(this.iRPwd).getValue()) {
                this.markInvalid(this.errors.PwdDifRPwd);
                return false;
            }*/
            return true;
        },
     
        // private
        CharacterSetChecks : function(type){
            this.type = type;
            this.fResult = false;
        },
     
        // private
        isctype : function(character, type){
            switch (type) { //why needed break after return in js ? very odd bug
                case this.kCapitalLetter: if (character >= 'A' && character <= 'Z') { return true; } break;
                case this.kSmallLetter: if (character >= 'a' && character <= 'z') { return true; } break;
                case this.kDigit: if (character >= '0' && character <= '9') { return true; } break;
                case this.kPunctuation: if ("!@#$%^&*()_+-='\";:[{]}|.>,</?`~".indexOf(character) >= 0) { return true; } break;
                default: return false;
            }
        },
     
        // private
        IsLongEnough : function(pwd, size){
            return !(pwd == null || isNaN(size) || pwd.length < size);
        },
     
        // private
        SpansEnoughCharacterSets : function(word, nb){
            if (!this.IsLongEnough(word, nb))
            {
                return false;
            }
     
            var characterSetChecks = new Array(
                new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
                new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation));
            for (var index = 0; index < word.length; ++index) {
                for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
                    if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
                        characterSetChecks[nCharSet].fResult = true;
                        break;
                    }
                }
            }
     
            var nCharSets = 0;
            for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
                if (characterSetChecks[nCharSet].fResult) {
                    ++nCharSets;
                }
            }
     
            if (nCharSets < nb) {
                return false;
            }
            return true;
        },
     
        // private
        ClientSideStrongPassword : function(pwd){
            return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
        },
     
        // private
        ClientSideMediumPassword : function(pwd){
            return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
        },
     
        // private
        ClientSideWeakPassword : function(pwd){
            return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
        }
    })
    styles/secure-pass.css:
    Code:
    @charset "utf-8";
     
    .PwdMeterBase{
        margin-bottom:10px;
        width:248px;
    }
     
    .PwdBack{
        height:9px;
        background-image:url(../images/password_meter_grey.gif);
        background-position:center center;
        background-repeat: no-repeat;
    }
     
    .PwdMeter{
        width:0;
        height:9px;
        background-image:url(../images/password_meter.gif);
        background-position:center center;
        background-repeat: no-repeat;
        font-size:9px;
    }
    and a sample html:
    [html]<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Secure Pass</title>
    <script type="text/javascript" src="library/yui-utilities.js"></script>
    <script type="text/javascript" src="library/ext-yui-adapter.js"></script>
    <script type="text/javascript" src="library/ext-all.js"></script>
    <script type="text/javascript" src="library/ext-lang-fr_CA.js"></script>
    <script type="text/javascript" src="scripts/secure-pass.js"></script>
    <script type="text/javascript">
    Ext.onReady(function() {
    sPwd = new Ext.ux.SecurePass({
    iRPwd : 'iRetypePwd',
    pwdStrengths : ['Faible', 'Moyen', 'Fort'],
    errors : {
    PwdEmpty: 'Tapez un mot de passe, puis retapez-le pour confirmation.',
    PwdDifRPwd: 'Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas. Tapez le m

  2. #2
    Sencha User
    Join Date
    Apr 2012
    Location
    Austin, Texas
    Posts
    2
    Vote Rating
    0
    brian.moeskau is an unknown quantity at this point

      0  

    Default


    Cool! Care to make it official?

    http://extjs.com/learn/Ext_Extensions

  3. #3
    Sencha User trbs's Avatar
    Join Date
    Mar 2007
    Posts
    310
    Vote Rating
    0
    trbs is on a distinguished road

      0  

    Default


    Yeah looks great, cannot wait to try this out
    I'm part of the Ext Community
    Maintaining: Translations and some Examples
    Developing on: ExtJS Python Builder / Gozerbot
    Places: Ido.nl.eu.org / My ExtSamples / Trbs on Wiki / IRC

  4. #4
    Ext JS Premium Member vincentc's Avatar
    Join Date
    Jul 2007
    Location
    Lyon, France
    Posts
    33
    Vote Rating
    0
    vincentc is on a distinguished road

      0  

    Thumbs up [1.0] Ext.ux.form.SecurePass

    [1.0] Ext.ux.form.SecurePass


    Hello,

    Please find here the final version:

    Strong passwords contain 8-16 characters, do not include other fields, and combine three of these character types: uppercase letters, lowercase letters, numbers, or symbols.
    Medium passwords contain 7 characters, do not other fields, and combine two of the character types.
    Weak passwords contain 6 characters minimum, do not include other fields.

    scripts/secure-pass.js
    Code:
    Ext.namespace('Ext.ux.form');
    
    Ext.ux.form.SecurePass = function(config) {
    	Ext.ux.form.SecurePass.superclass.constructor.call(this, config);
    }
    
    Ext.extend(Ext.ux.form.SecurePass, Ext.form.TextField, {
    	/**
    	 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
    	 * {
    	 *  PwdEmpty: "Please type a password, and then retype it to confirm.",
    	 *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
    	 *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
    	 *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
    	 *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
    	 *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
    	 *  LNInPwd: "Your password can't contain your last name. Please type a different password."
    	 * })
    	 */
    	// private
    	errors : {
    		PwdEmpty: "Please type a password, and then retype it to confirm.",
    		PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
    		PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
    		PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
    		IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
    		FNInPwd: "Your password can't contain your first name. Please type a different password.",
    		LNInPwd: "Your password can't contain your last name. Please type a different password."
    	},
    	
    	/**
    	 * @cfg {String/Object} Label for the strength meter (defaults to
    	 * 'Password strength:')
    	 */
    	// private
    	meterLabel : 'Password strength:',
    
    	/**
    	 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
    	 * ['Weak', 'Medium', 'Strong'])
    	 */
    	// private
    	pwdStrengths : ['Weak', 'Medium', 'Strong'],
    
    	/**
    	 * @cfg {String/Object} fieldsFilter A fieldsFilter spec, as [['field_name', 'error_id'], ...]
    	 */
    	// private
    	fieldsFilter : null,
    
    	// private
    	strength : 0,
    
    	// private
    	_lastPwd : null,
    
    	// private
    	kCapitalLetter : 0,
    	kSmallLetter : 1,
    	kDigit : 2,
    	kPunctuation : 3,
    
        // private
        initEvents : function(){
            Ext.ux.form.SecurePass.superclass.initEvents.call(this);
    		this.el.on('keyup', this.checkStrength, this, {buffer:50});
    	},
    
    	// private
    	onRender : function(ct, position){
    		Ext.ux.form.SecurePass.superclass.onRender.call(this, ct, position);
    		this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
    		this.trigger = this.wrap.createChild({tag: "div", cls: "StrengthMeter "+this.triggerClass});
    		if(this.meterLabel != ''){
    			this.trigger.createChild({tag: "label", html: this.meterLabel});
    		}
    		this.trigger.createChild({tag: "div", cls: "PwdMeterBase", html: '<div class="PwdBack"><div class="PwdMeter" id="PwdMeter"></div></div>'});
    		if(this.hideTrigger){
    			this.trigger.setDisplayed(false);
    		}
    		this.setSize(this.width||'', this.height||'');
    	},
    
    	// private
    	onDestroy : function(){
    		if(this.trigger){
    			this.trigger.removeAllListeners();
    			this.trigger.remove();
    		}
    		if(this.wrap){
    			this.wrap.remove();
    		}
    		Ext.form.TriggerField.superclass.onDestroy.call(this);
    	},
    
    	// private
    	checkStrength : function(){
    		var pwd = this.el.getValue();
    		if (pwd == this._lastPwd) {
    			return;
    		}
    
    		var strength;
    		if (this.ClientSideStrongPassword(pwd)) {
    			strength = 3;
    		} else if(this.ClientSideMediumPassword(pwd)) {
    			strength = 2;
    		} else if(this.ClientSideWeakPassword(pwd)) {
    			strength = 1;
    		} else {
    			strength = 0;
    		}
    
    		document.getElementById('PwdMeter').style.width = 82 * strength +'px';
    		if(this.pwdStrengths != null && strength > 0) {
    			document.getElementById('PwdMeter').innerHTML = '&nbsp;'+ this.pwdStrengths[strength - 1];
    		} else {
    			document.getElementById('PwdMeter').innerHTML = '';
    		}
    
    		this._lastPwd = pwd;
    	},
    
        // private
    	validateValue : function(value){
    		if (!Ext.form.TextField.superclass.validateValue.call(this, value)){
                return false;
            }
    		if (value.length == 0) {
    			this.markInvalid(this.errors.PwdEmpty);
                return false;
    		}
    		if ("[\x21-\x7e]*".match(value)) {
    			this.markInvalid(this.errors.PwdBadChar);
                return false;
    		}
    		if (value.length < 6) {
    			this.markInvalid(this.errors.PwdShort);
                return false;
    		}
    		if (value.length > 16) {
    			this.markInvalid(this.errors.PwdLong);
                return false;
    		}
    		for (var index = 0; index < this.fieldsFilter.length; ++index) {
    			filter = document.getElementById(this.fieldsFilter[index][0]).value;
    			if (filter != '')
    			{
    				re = new RegExp(filter);
    				if (re.test(value)) {
    					this.markInvalid(eval('this.errors.'+ this.fieldsFilter[index][1]));
    					return false;
    				}
    			}
    		}
    		return true;
    	},
    
        // private
    	CharacterSetChecks : function(type){
    		this.type = type;
    		this.fResult = false;
    	},
    
        // private
    	isctype : function(character, type){
    		switch (type) { //why needed break after return in js ? very odd bug
    			case this.kCapitalLetter: if (character >= 'A' && character <= 'Z') { return true; } break;
    			case this.kSmallLetter: if (character >= 'a' && character <= 'z') { return true; } break;
    			case this.kDigit: if (character >= '0' && character <= '9') { return true; } break;
    			case this.kPunctuation: if ("!@#$%^&*()_+-='\";:[{]}|.>,</?`~".indexOf(character) >= 0) { return true; } break;
    			default: return false;
    		}
    	},
    
        // private
    	IsLongEnough : function(pwd, size){
    		return !(pwd == null || isNaN(size) || pwd.length < size);
    	},
    
        // private
    	SpansEnoughCharacterSets : function(word, nb){
    		if (!this.IsLongEnough(word, nb))
    		{
    			return false;
    		}
    
    		var characterSetChecks = new Array(
    			new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
    			new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation));
    		for (var index = 0; index < word.length; ++index) {
    			for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
    				if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
    					characterSetChecks[nCharSet].fResult = true;
    					break;
    				}
    			}
    		}
    
    		var nCharSets = 0;
    		for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
    			if (characterSetChecks[nCharSet].fResult) {
    				++nCharSets;
    			}
    		}
    
    		if (nCharSets < nb) {
    			return false;
    		}
    		return true;
    	},
    
        // private
    	ClientSideStrongPassword : function(pwd){
    		return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
    	},
    
        // private
    	ClientSideMediumPassword : function(pwd){
    		return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
    	},
    
        // private
    	ClientSideWeakPassword : function(pwd){
    		return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
    	}
    })
    styles/secure-pass.css
    Code:
    @charset "utf-8";
    
    .PwdMeterBase{
    margin-bottom:10px;
    width:248px;
    }
    .PwdBack{
    height:9px;
    background-image:url(../images/password_meter_grey.gif);
    background-position:center center;
    background-repeat: no-repeat;
    }
    .PwdMeter{
    width:0;
    height:9px;
    background-image:url(../images/password_meter.gif);
    background-position:center center;
    background-repeat: no-repeat;
    font-size:9px;
    }
    and a html sample...
    HTML Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Secure Pass</title>
    <script type="text/javascript" src="library/yui-utilities.js"></script>
    <script type="text/javascript" src="library/ext-yui-adapter.js"></script>
    <script type="text/javascript" src="library/ext-all.js"></script>
    <script type="text/javascript" src="scripts/secure-pass.js"></script>
    <script type="text/javascript">
    Ext.onReady(function() {
        sPwd = new Ext.ux.form.SecurePass({
            fieldsFilter : [['lastname', 'LNInPwd']]
        });
        sPwd.applyTo('iPwd');
    });
    </script>
    <style>
    @import 'styles/ext-all.css';
    @import 'styles/secure-pass.css';
    </style>
    </head>
    <body>
    <div class="x-form-element">
        <label for="lastname">*Last name:</label>
        <input id="lastname" name="lastname" maxlength="32" type="text">
    </div>
    <div class="x-form-element">
        <label for="iPwd">*Type password:</label>
        <input id="iPwd" name="iPwd" maxlength="16" type="password">
    </div>
    </body>
    </html>
    Possible evolutions :
    • manage retype password
    • dictionnary of common words
    • custom strength functions
    • custom minimum / maximum password length

    Again, thanks to Ext team for their work.

    Hope you find this usefull.
    Enjoy !
    Attached Images

  5. #5
    Sencha - Community Support Team JeffHowden's Avatar
    Join Date
    Mar 2007
    Location
    Forest Grove, OR
    Posts
    1,038
    Vote Rating
    1
    JeffHowden is on a distinguished road

      0  

    Default Resources on password strength

    Resources on password strength


    I think this is a great start. However, like pretty much everything Ext, develop this to the nth degree and combine all the right decisions from the various players in the field, the whole time, doing it overall better. With that, here's some of what's currently in the market:

    http://www.certainkey.com/demos/password/
    Notice that it calculates entropy and detects dictionary words.

    http://www.codeandcoffee.com/2007/06...r-like-google/
    An example based off Google's approach

    Additionally, the checker should "canonicalize" (normalize or convert to all-letter form) the password prior to checks against the dictionary. This will catch common combinations of using 5 for s, 1 for l or i, 2 for z, 8 for b or B, etc.

    Here's some good advice regarding passwords:

    http://geodsoft.com/howto/password/password_advice.htm
    Jeff Howden
    Ext JS - Support Team Volunteer
    jeff@extjs.com

    Any and all code samples that are authored by me and posted on the Ext forums or website are hereby released into the public domain and I release anyone or entity of liability by using said code samples unless explicitly stated otherwise.

    Opinions are mine and not necessarily endorsed by Ext, LLC. Please do not contact me directly for assistance unless requested by me.

  6. #6
    Ext User
    Join Date
    Mar 2007
    Posts
    90
    Vote Rating
    0
    Herm is on a distinguished road

      0  

    Default


    Or you could do password strength checking 'in the cloud' and provide an Ext UI to the Google checker: https://www.google.com/accounts/Rate...wd=amI2Strong? which has been written about here http://www.codeproject.com/useritems...rdStrength.asp .

  7. #7
    Ext User DigitalSkyline's Avatar
    Join Date
    Apr 2007
    Location
    Rochester, MI
    Posts
    461
    Vote Rating
    1
    DigitalSkyline is on a distinguished road

      0  

    Default


    No offence but using a third party to determine the strength of your password? Why not just send them the keys to your house while your at it?

  8. #8
    Sencha - Community Support Team JeffHowden's Avatar
    Join Date
    Mar 2007
    Location
    Forest Grove, OR
    Posts
    1,038
    Vote Rating
    1
    JeffHowden is on a distinguished road

      0  

    Default


    I briefly considered that too, but I highly dislike the notion of a) sending the password over the wire, b) using an external service that has no documentation on their practice of traffic logging on that address, and c) there being literally no documentation as to how the strength checking works (ie, is it just length, is it a count of different character types, does it do dictionary checks, does it include any sort of "other field" checking to ensure things like username, first name, last name, email, etc. aren't in the password).

    Just a guess, but from the following scoring a 4, I'd say their strength checking really isn't very full-featured:

    https://www.google.com/accounts/Rate...strongpassword
    https://www.google.com/accounts/Rate...swd=mypassword
    https://www.google.com/accounts/Rate...?Passwd=iphone
    https://www.google.com/accounts/Rate...d?Passwd=extjs
    https://www.google.com/accounts/Rate...Passwd=jscript
    Jeff Howden
    Ext JS - Support Team Volunteer
    jeff@extjs.com

    Any and all code samples that are authored by me and posted on the Ext forums or website are hereby released into the public domain and I release anyone or entity of liability by using said code samples unless explicitly stated otherwise.

    Opinions are mine and not necessarily endorsed by Ext, LLC. Please do not contact me directly for assistance unless requested by me.

Turkiyenin en sevilen filmlerinin yer aldigi xnxx internet sitemiz olan ve porn sex tarzi bir site olan mobil porno izle sitemiz gercekten dillere destan bir durumda herkesin sevdigi bir site olarak tarihe gececege benziyor. Sitenin en belirgin ozelliklerinden birisi de Turkiyede gercekten kaliteli ve muntazam, duzenli porno izle siteleri olmamasidir. Bu yuzden iste. Ayrica en net goruntu kalitesine sahip adresinde yayinlanmaktadir. Mesela diğer sitelerimizden bahsedecek olursak, en iyi hd porno video arşivine sahip bir siteyiz. "The Best anal porn videos and slut anus, big asses movies set..." hd porno faketaxi