pwcheck returns the strength of a password in an interval of 0 to 7, 0 meaning weak, and 7 strong. It also has a mapping of string representations of these strength values, so we can show this information to the user. --- firstboot/pwcheck.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 123 insertions(+), 0 deletions(-) create mode 100644 firstboot/pwcheck.py diff --git a/firstboot/pwcheck.py b/firstboot/pwcheck.py new file mode 100644 index 0000000..6aae573 --- /dev/null +++ b/firstboot/pwcheck.py @@ -0,0 +1,123 @@ +import re +import cracklib + +import logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("pwcheck") + +import gettext +_ = lambda x: gettext.ldgettext("firstboot", x) + + +class PwError(Exception): + pass + + +class PwRule(object): + + def __init__(self, rule, weight=1, required=False, desc=""): + if callable(rule): + # is a func + self.rule = rule + else: + # is a regex + pattern = re.compile(rule) + self.rule = lambda x: bool(pattern.search(x)) + + if not weight: + raise PwError("weight must be a non-zero value") + + self.weight = weight + self.required = required + + self.desc = desc + + @property + def include(self): + return self.weight > 0 + + @property + def exclude(self): + return self.weight < 0 + + def check(self, password): + passed = self.rule(password) + + if ((self.include and self.required and not passed) or + (self.exclude and self.required and passed)): + logger.debug("%s: %d", self.desc, 0) + raise PwError("password does not meet required criteria") + + if ((self.include and passed) or + (self.exclude and not passed)): + logger.debug("%s: %d", self.desc, self.weight) + return self.weight + else: + logger.debug("%s: %d", self.desc, 0) + return 0 + + +class Password(object): + + def cracklib_check(password): + try: + cracklib.FascistCheck(password) + except ValueError: + return False + else: + return True + + RULES = [ PwRule(rule=lambda x: len(x) >= 4, weight=1, required=True, + desc="4 characters or more"), + PwRule(rule=lambda x: len(x) >= 8, weight=1, required=False, + desc="8 characters or more"), + PwRule(rule=lambda x: len(x) >= 12, weight=1, required=False, + desc="12 characters or more"), + PwRule(rule=r"[a-z]+", weight=1, required=False, + desc="at least one lowercase character"), + PwRule(rule=r"[A-Z]+", weight=1, required=False, + desc="at least one uppercase character"), + PwRule(rule=r"[0-9]+", weight=1, required=False, + desc="at least one digit"), + PwRule(rule=r"[^a-zA-Z0-9]+", weight=1, required=False, + desc="at least one special character"), + PwRule(rule=cracklib_check, weight=-1, required=False, + desc="cracklib") ] + + STRENGTH_STRINGS = [ _("Very weak"), + _("Very weak"), + _("Weak"), + _("Weak"), + _("Fairly strong"), + _("Strong"), + _("Very strong"), + _("Very strong") ] + + def __init__(self, password): + self.password = password + + @property + def strength(self): + strength = 0 + for rule in self.RULES: + try: + strength += rule.check(self.password) + except PwError: + return 0 + + return strength + + @property + def strength_string(self): + strength = self.strength + + if strength < 0: + return self.STRENGTH_STRINGS[0] + + try: + return self.STRENGTH_STRINGS[strength] + except IndexError: + return _("Undefined") + + def __str__(self): + return "%s" % self.password -- 1.7.1.1 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list