Follow-up to r70520: forgot to add new files
authorMax Semenik <maxsem@users.mediawiki.org>
Thu, 5 Aug 2010 19:18:20 +0000 (19:18 +0000)
committerMax Semenik <maxsem@users.mediawiki.org>
Thu, 5 Aug 2010 19:18:20 +0000 (19:18 +0000)
skins/common/password.css [new file with mode: 0644]
skins/common/password.js [new file with mode: 0644]

diff --git a/skins/common/password.css b/skins/common/password.css
new file mode 100644 (file)
index 0000000..7983e91
--- /dev/null
@@ -0,0 +1,17 @@
+span.mw-password-bad {
+       background: red;
+       color: yellow;
+       font-weight: bold;
+}
+
+.mw-password-mediocre {
+       background: yellow;
+}
+
+.mw-password-acceptable {
+       background: silver;
+}
+
+.mw-password-good {
+       background: green;
+}
\ No newline at end of file
diff --git a/skins/common/password.js b/skins/common/password.js
new file mode 100644 (file)
index 0000000..990b270
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * Password strength checker
+ * @license WTFPL 2.0
+ * All scores are ranged approximately 0 (total disaster) - 100 (_looks_ great)
+ * @todo Check for popular passwords and keyboard sequences (QWERTY, etc)
+ */
+
+function bruteForceComplexity( pwd ) {
+       var score = 0;
+
+       if ( pwd.length < 16 ) {
+               score = pwd.length * 5;
+       } else {
+               score = 80;
+       }
+
+       var regexes = [
+               /[a-z]/,
+               /[A-Z]/,
+               /[0-9]/,
+               /[-_;:\.,'"`~!@#$%\^&\*\(\)\[\]\{\} ]/ ];
+
+       var charClasses = 0;
+       for ( var i in regexes ) {
+               if ( pwd.match( regexes[i] ) ) {
+                       charClasses++;
+               }
+       }
+
+       var matches = pwd.match( /[\x80-\uFFFF]/g );
+       if ( matches ) {
+               charClasses++;
+               
+               // poor man's isUpper() and isLower()
+               var i, lower = false, upper = false;
+               for ( i in matches ) {
+                       var ch = matches[i];
+                       upper |= ch != ch.toLowerCase();
+                       lower |= ch != ch.toUpperCase();
+                       if ( upper && lower ) break;
+               }
+               if ( upper && lower ) {
+                       charClasses++;
+               }
+       }
+       score += ( charClasses - 1 ) * 10;
+
+       return score;
+}
+
+function repetitionScore( pwd ) {
+       var unique = '';
+       for ( var i in pwd ) {
+               if ( unique.indexOf( pwd[i] ) < 0 ) {
+                       unique += pwd[i];
+               }
+       }
+       var ratio = pwd.length / unique.length - 0.4; // allow up to 40% repetition, reward for less, penalize for more
+       
+       return 100 / ratio;
+}
+
+function sequenceScore( pwd ) {
+       pwd = pwd.concat( '\0' );
+       var score = 100, sequence = 1;
+       for ( var i = 1; i < pwd.length; i++ ) {
+               if ( pwd.charCodeAt( i ) == pwd.charCodeAt(i - 1) + 1 ) {
+                       sequence++;
+               } else {
+                       if ( sequence > 2 ) {
+                               score -= Math.sqrt( sequence ) * 15;
+                       }
+                       sequence = 1;
+               }
+       }
+       for ( var i = 1; i < pwd.length; i++ ) {
+               if ( pwd.charCodeAt( i ) == pwd.charCodeAt(i - 1) - 1 ) {
+                       sequence++;
+               } else {
+                       if ( sequence > 2 ) {
+                               score -= Math.sqrt( sequence ) * 15;
+                       }
+                       sequence = 1;
+               }
+       }
+       return score;
+}
+
+(function( $ ) {
+       function passwordChanged() {
+               retypeChanged();
+               var pwd = $( passwordSecurity.password ).val();
+               if ( pwd == '' ) {
+                       $( '#password-strength' ).html( '' );
+                       return;
+               }
+               if ( pwd.length > 100 ) pwd = pwd.slice( 0, 100 );
+               var score = Math.min(
+                       bruteForceComplexity( pwd ),
+                       repetitionScore( pwd ),
+                       sequenceScore( pwd )
+               );
+               var result = 'good';
+               if ( score < 40 ) {
+                       result = 'bad';
+               } else if ( score < 60 ) {
+                       result = 'mediocre';
+               } else if ( score < 85 ) {
+                       result = 'acceptable';
+               }
+               var message = '<span class="mw-password-' + result + '">' + passwordSecurity.messages['password-strength-' + result]
+                       + '</span>';
+               $( '#password-strength' ).html(
+                       passwordSecurity.messages['password-strength'].replace( '$1', message )
+               );
+       }
+
+       function retypeChanged() {
+               var pwd = $( passwordSecurity.password ).val();
+               var retype = $( passwordSecurity.retype ).val();
+               var message;
+               if ( pwd == '' || pwd == retype ) {
+                       message = '';
+               } else if ( retype == '' ) {
+                       message = passwordSecurity.messages['password-retype'];
+               } else {
+                       message = passwordSecurity.messages['password-retype-mismatch'];
+               }
+               $( '#password-retype' ).html( message );
+       }
+
+       $( document ).ready( function() {
+               $( passwordSecurity.password ).bind( 'keyup change', passwordChanged );
+               $( passwordSecurity.retype ).bind( 'keyup change', retypeChanged );
+       })
+})(jQuery);