mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Various changes to try to improve the semantic of everything @.@
This commit is contained in:
parent
2b00e072d8
commit
3c5ce491c5
1 changed files with 74 additions and 43 deletions
|
@ -21,60 +21,80 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
import cracklib
|
import cracklib
|
||||||
|
|
||||||
import string
|
import string
|
||||||
ASCII_UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
ASCII_LOWERCASE = "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin",
|
SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin",
|
||||||
"root", "test", "rpi"]
|
"root", "test", "rpi"]
|
||||||
|
|
||||||
PWDDICT_FOLDER = '/usr/local/share/dict/cracklib/'
|
MOST_USED_PASSWORDS = '/usr/local/share/dict/cracklib/100000-most-used'
|
||||||
PWDDICT_LIST = '100000-most-used'
|
|
||||||
|
# Length, digits, lowers, uppers, others
|
||||||
|
STRENGTH_LEVELS = [
|
||||||
|
(6, 0, 0, 0, 0),
|
||||||
|
(8, 1, 1, 1, 0),
|
||||||
|
(8, 1, 1, 1, 1),
|
||||||
|
(12, 1, 1, 1, 1),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class PasswordValidator(object):
|
class PasswordValidator(object):
|
||||||
"""
|
|
||||||
PasswordValidator class validate password
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Length, digits, lowers, uppers, others
|
|
||||||
strength_lvl = [
|
|
||||||
[6, 0, 0, 0, 0],
|
|
||||||
[8, 1, 1, 1, 0],
|
|
||||||
[8, 1, 1, 1, 1],
|
|
||||||
[12, 1, 1, 1, 1],
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, profile):
|
def __init__(self, profile):
|
||||||
|
"""
|
||||||
|
Initialize a password validator.
|
||||||
|
|
||||||
|
The profile shall be either "user" or "admin"
|
||||||
|
and will correspond to a validation strength
|
||||||
|
defined via the setting "security.password.<profile>.strength"
|
||||||
|
"""
|
||||||
|
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
import json
|
|
||||||
try:
|
try:
|
||||||
|
# We do this "manually" instead of using settings_get()
|
||||||
|
# from settings.py because this file is also meant to be
|
||||||
|
# use as a script by ssowat.
|
||||||
|
# (or at least that's my understanding -- Alex)
|
||||||
settings = json.load(open('/etc/yunohost/settings.json', "r"))
|
settings = json.load(open('/etc/yunohost/settings.json', "r"))
|
||||||
setting_key = "security.password." + profile + ".strength"
|
setting_key = "security.password." + profile + ".strength"
|
||||||
self.validation_strength = int(settings[setting_key])
|
self.validation_strength = int(settings[setting_key])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Fallback to default value if we can't fetch settings for some reason
|
||||||
self.validation_strength = 2 if profile == 'admin' else 1
|
self.validation_strength = 2 if profile == 'admin' else 1
|
||||||
|
|
||||||
def validate(self, password):
|
def validation_summary(self, password):
|
||||||
"""
|
"""
|
||||||
Validate a password and raise error or display a warning
|
Check if a password is listed in the list of most used password
|
||||||
|
and if the overall strength is good enough compared to the
|
||||||
|
validation_strength defined in the constructor.
|
||||||
|
|
||||||
|
Produces a summary-tuple comprised of a level (success, error, warning)
|
||||||
|
and a message key describing the issues found.
|
||||||
"""
|
"""
|
||||||
if self.validation_strength <= 0:
|
if self.validation_strength <= 0:
|
||||||
return ("success", "")
|
return ("success", "")
|
||||||
|
|
||||||
self.listed = password in SMALL_PWD_LIST or self.is_in_cracklib_list(password)
|
listed = password in SMALL_PWD_LIST or self.is_in_cracklib_list(password)
|
||||||
self.strength = self.compute(password)
|
strength_level = self.strength_level(password)
|
||||||
if self.strength < self.validation_strength:
|
if strength_level < self.validation_strength:
|
||||||
if self.listed:
|
if listed:
|
||||||
return ("error", "password_listed_" + str(self.validation_strength))
|
return ("error", "password_listed_" + str(self.validation_strength))
|
||||||
else:
|
else:
|
||||||
return ("error", "password_too_simple_" + str(self.validation_strength))
|
return ("error", "password_too_simple_" + str(self.validation_strength))
|
||||||
|
|
||||||
if self.strength < 3:
|
if strength_level < 3:
|
||||||
return ("warning", 'password_advice')
|
return ("warning", 'password_advice')
|
||||||
return ("success", "")
|
return ("success", "")
|
||||||
|
|
||||||
def compute(self, password):
|
def strength(self, password):
|
||||||
|
"""
|
||||||
|
Returns the strength of a password, defined as a tuple
|
||||||
|
containing the length of the password, the number of digits,
|
||||||
|
lowercase letters, uppercase letters, and other characters.
|
||||||
|
|
||||||
|
For instance, "PikachuDu67" is (11, 2, 7, 2, 0)
|
||||||
|
"""
|
||||||
# Indicators
|
# Indicators
|
||||||
length = len(password)
|
length = len(password)
|
||||||
digits = 0
|
digits = 0
|
||||||
|
@ -85,41 +105,52 @@ class PasswordValidator(object):
|
||||||
for character in password:
|
for character in password:
|
||||||
if character in string.digits:
|
if character in string.digits:
|
||||||
digits = digits + 1
|
digits = digits + 1
|
||||||
elif character in ASCII_UPPERCASE:
|
elif character in string.ascii_uppercase:
|
||||||
uppers = uppers + 1
|
uppers = uppers + 1
|
||||||
elif character in ASCII_LOWERCASE:
|
elif character in string.ascii_lowercase:
|
||||||
lowers = lowers + 1
|
lowers = lowers + 1
|
||||||
else:
|
else:
|
||||||
others = others + 1
|
others = others + 1
|
||||||
|
|
||||||
return self.compare(length, digits, lowers, uppers, others)
|
return (length, digits, lowers, uppers, others)
|
||||||
|
|
||||||
def compare(self, length, digits, lowers, uppers, others):
|
def strength_level(self, password):
|
||||||
strength = 0
|
"""
|
||||||
|
Computes the strength of a password and compares
|
||||||
|
it to the STRENGTH_LEVELS.
|
||||||
|
|
||||||
for i, config in enumerate(self.strength_lvl):
|
Returns an int corresponding to the highest STRENGTH_LEVEL
|
||||||
if length < config[0] or digits < config[1] \
|
satisfied by the password.
|
||||||
or lowers < config[3] or uppers < config[4] \
|
"""
|
||||||
or others < config[5]:
|
|
||||||
|
strength = self.strength(password)
|
||||||
|
|
||||||
|
strength_level = 0
|
||||||
|
# Iterate over each level and its criterias
|
||||||
|
for level, level_criterias in enumerate(STRENGTH_LEVELS):
|
||||||
|
# Iterate simulatenously over the level criterias (e.g. [8, 1, 1, 1, 0])
|
||||||
|
# and the strength of the password (e.g. [11, 2, 7, 2, 0])
|
||||||
|
# and compare the values 1-by-1.
|
||||||
|
# If one False is found, the password does not satisfy the level
|
||||||
|
if False in [s>=c for s, c in zip(strength, level_criterias)]:
|
||||||
break
|
break
|
||||||
strength = i + 1
|
# Otherwise, the strength of the password is at least of the current level.
|
||||||
return strength
|
strength_level = level + 1
|
||||||
|
|
||||||
|
return strength_level
|
||||||
|
|
||||||
def is_in_cracklib_list(self, password):
|
def is_in_cracklib_list(self, password):
|
||||||
try:
|
try:
|
||||||
cracklib.VeryFascistCheck(password, None,
|
cracklib.VeryFascistCheck(password, None, MOST_USED_PASSWORDS)
|
||||||
os.path.join(PWDDICT_FOLDER, PWDDICT_LIST))
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
# We only want the dictionnary check of cracklib, not the is_simple
|
# We only want the dictionnary check of cracklib, not the is_simple
|
||||||
# test.
|
# test.
|
||||||
if str(e) not in ["is too simple", "is a palindrome"]:
|
if str(e) not in ["is too simple", "is a palindrome"]:
|
||||||
return True
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class LoggerPasswordValidator(PasswordValidator):
|
class LoggerPasswordValidator(PasswordValidator):
|
||||||
"""
|
|
||||||
PasswordValidator class validate password
|
|
||||||
"""
|
|
||||||
|
|
||||||
def validate(self, password):
|
def validate(self, password):
|
||||||
"""
|
"""
|
||||||
|
@ -135,7 +166,7 @@ class LoggerPasswordValidator(PasswordValidator):
|
||||||
|
|
||||||
logger = logging.getLogger('yunohost.utils.password')
|
logger = logging.getLogger('yunohost.utils.password')
|
||||||
|
|
||||||
status, msg = super(LoggerPasswordValidator, self).validate(password)
|
status, msg = super(LoggerPasswordValidator, self).validation_summary(password)
|
||||||
if status == "error":
|
if status == "error":
|
||||||
raise MoulinetteError(1, m18n.n(msg))
|
raise MoulinetteError(1, m18n.n(msg))
|
||||||
elif status == "warning":
|
elif status == "warning":
|
||||||
|
@ -148,7 +179,7 @@ if __name__ == '__main__':
|
||||||
#print("usage: password.py PASSWORD")
|
#print("usage: password.py PASSWORD")
|
||||||
else:
|
else:
|
||||||
pwd = sys.argv[1]
|
pwd = sys.argv[1]
|
||||||
status, msg = PasswordValidator('user').validate(pwd)
|
status, msg = PasswordValidator('user').validation_summary(pwd)
|
||||||
print(msg)
|
print(msg)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue