This is based on the JavaScript snippet at the snippler URL listed. It in turn referenced a page that is no longer available. So I have decided to make my pythonification of the JavaScript available for others instead of hoarding it.
-
"""
-
Class for checking the strength of a password.
-
Copyright (C) 2011 Henry Longmore. This version
-
of this file may be copied for personal, educational,
-
or commercial purposes. Use at your own risk.
-
"""
-
-
import os
-
from os import path
-
import math
-
-
# Nota Bene: the configuration module is non-standard. You will
-
# need to format and read your dictionary file yourself.
-
from djangoapps import configuration
-
-
password_dict = path.join(os.sep, ‘path’, ‘to’, ‘your’, ‘password.dictionary’)
-
-
# Based on Password Strength Meter posted at Snipplr
-
# http://snipplr.com/view/8755/password-strength-meter/
-
-
class PasswordStrengthChecker(object):
-
def __init__(self, strength=‘medium’, pwd_dict=”):
-
self.punctuation = list("!@#$%^&* ()_+-=’\";:[{]}\|.>,</?`~")
-
self.similarity_map = {
-
’3′: ‘e’, ‘x’: ‘k’, ’5′: ‘s’, ‘$’: ‘s’, ’6′: ‘g’, ’7′: ‘t’,
-
’8′: ‘b’, ‘|’: ‘l’, ’9′: ‘g’, ‘+’: ‘t’, ‘@’: ‘a’, ’0′: ‘o’,
-
’1′: ‘l’, ’2′: ‘z’, ‘!’: ‘i’, ’1′: ‘i’}
-
password_dictionary = configuration.Configuration(configpath=pwd_dict)
-
self.word_list = dict()
-
for i in xrange(3, 17):
-
self.word_list[i] = password_dictionary.get_option(‘%s’ % i, [])
-
-
self.strengths = [‘medium’, ‘strong’, ‘best’]
-
self.thresholds = {‘medium’: 0.8, ‘strong’: 0.6, ‘best’: 0.6}
-
self.min_length = {‘medium’: 8, ‘strong’: 8, ‘best’: 14}
-
self.min_charsets = {‘medium’: 2, ‘strong’: 3, ‘best’: 3}
-
self.similarity = {‘medium’: False, ‘strong’: True, ‘best’: True}
-
-
if strength not in self.strengths:
-
strength = self.strengths[0]
-
self.strength = strength
-
-
def is_charset_type(self, c, c_class):
-
if c_class == ‘capital’:
-
return c.isalpha() and c == c.upper()
-
if c_class == ‘lower’:
-
return c.isalpha() and c == c.lower()
-
if c_class == ‘digit’:
-
return c.isdigit()
-
if c_class == ‘punctuation’:
-
return c in self.punctuation
-
return False
-
-
def canonicalize_word(self, word, letters_only=False):
-
canonicalized = ”
-
for c in list(word.lower()):
-
if letters_only and not self.is_charset_type(c, ‘lower’):
-
canonicalized += c
-
else:
-
canonicalized += self.similarity_map.get(c, c)
-
return canonicalized
-
-
def charset_span(self, word):
-
checks = {‘capital’: 0, ‘lower’: 0, ‘digit’: 0, ‘punctuation’: 0}
-
for c in list(word):
-
for key in checks:
-
if not checks[key] and self.is_charset_type(c, key):
-
checks[key] = 1
-
break
-
return sum(checks.values())
-
-
def in_dictionary(self, word):
-
similarity_check = self.similarity[self.strength]
-
canonicalized = self.canonicalize_word(word, letters_only=similarity_check)
-
word_length = len(canonicalized)
-
if canonicalized in self.word_list[word_length]:
-
return True
-
if similarity_check:
-
minimum_meaningful_match = int(math.floor((self.thresholds[self.strength]) * word_length))
-
for length in xrange(minimum_meaningful_match, word_length):
-
for start in xrange(0, word_length – minimum_meaningful_match):
-
subword = canonicalized[start:start + length]
-
if subword in self.word_list[len(subword)]:
-
return True
-
return False
-
-
def strong_enough(self, password):
-
if not password:
-
return False
-
if len(password) < self.min_length[self.strength]:
-
return False
-
if self.charset_span(password) < self.min_charsets[self.strength]:
-
return False
-
if self.in_dictionary(password):
-
return False
-
return True
-
-
global password_checker
-
password_checker = PasswordStrengthChecker(strength=‘medium’, pwd_dict=password_dict)