Open rozza opened 11 years ago
class PasswordField(StringField):
"""A password field - generate password using specific algorithm (md5,sha1,sha512 etc) and regex validator
Default regex validator: r[A-Za-z0-9] <- Match any of the above: leters and digits
Example:
class User(Document):
username = StringField(required=True,unique=True)
password = PasswordField(algorithm="md5")
ip = IPAddressField()
# save user:
user = User(username=username,password="mongoengine789",ip="192.167.12.255")
user.save()
# search user
user = User.objects(username=username).first()
if user is None:
print "Not found!"
return
user_password = user.password
print str(upassword) -> {'hash': 'c2e920e469d14f240d4de02883489750a1a63e68', 'salt': 'QBX6FZD', 'algorithm': 'sha1'}
... check password ...
"""
ALGORITHM_MD5 = "md5"
ALGORITHM_SHA1 = "sha1"
ALGORITHM_SHA256 = "sha256"
ALGORITHM_SHA512 = "sha512"
ALGORITHM_CRYPT = "crypt"
DEFAULT_VALIDATOR = r'[A-Za-z0-9]' # letters and digits
DOLLAR = "$"
def __init__(self, min_length=6, max_length=None, salt=None,
algorithm=ALGORITHM_SHA1, regex=DEFAULT_VALIDATOR, **kwargs):
self.max_length = max_length
self.min_length = min_length
self.algorithm = algorithm.lower()
self.salt = salt or self.random_password()
super(PasswordField, self).__init__(kwargs)
def random_password(self, nchars=6):
chars = string.printable
hash = ''
for char in xrange(nchars):
rand_char = random.randrange(0, len(chars))
hash += chars[rand_char]
return hash
def hexdigest(self, password):
if self.algorithm == PasswordField.ALGORITHM_CRYPT:
try:
import crypt
except ImportError:
self.error("crypt module not found in this system. Please use md5 or sha* algorithm")
return crypt.crypt(password, self.salt)
# use sha1 algoritm
if self.algorithm == PasswordField.ALGORITHM_SHA1:
return hashlib.sha1(self.salt + password).hexdigest()
elif self.algorithm == PasswordField.ALGORITHM_MD5:
return hashlib.md5(self.salt + password).hexdigest()
elif self.algorithm == PasswordField.ALGORITHM_SHA256:
return hashlib.sha256(self.salt + password).hexdigest()
elif self.algorithm == PasswordField.ALGORITHM_SHA512:
return hashlib.sha512(self.salt + password).hexdigest()
raise ValueError('Unsupported hash type %s' % self.algorithm)
def set_password(self, password):
"""
Sets the user's password using format [encryption algorithm]$[salt]$[password]
Example: sha1$SgwcbaH$20f16a1fa9af6fa40d59f78fd2c247f426950e46
"""
password = self.hexdigest(password)
return '%s$%s$%s' % (self.algorithm, self.salt, password)
def to_mongo(self, value):
return self.set_password(value)
def to_python(self, value):
"""
Return password like sha1$DEnDMSj$ef5cd35779bba65528c900d248f3e939fb495c65
"""
return value
def to_dict(self, value):
"""
Return password split into components
"""
(algorithm, salt, hash) = value.split(PasswordField.DOLLAR)
return {"algorithm" : algorithm,
"salt" : salt,
"hash" : hash}
def test_password_validation(self):
"""Ensure that PasswordField is valid.
"""
class Users(Document):
username = StringField()
password = PasswordField(algorithm="sha1",validator='[a-z]+')
import random
user = Users()
user.username=str(random.random())
user.password="abracadabra"
user.validate()
user.save()
class Users(Document):
username = StringField()
password = PasswordField(algorithm="md5")
user = Users()
user.username=str(random.random())
user.password="pymongoengine2012"
user.validate()
user.save()
for applying minimum and maximum lengths to password field, we should pass the same to the super class's constructor.. super(PasswordField, self).init(kwargs,min_length=min_length,max_length=max_length)
string.printable contains "$" on the possibilities so when do a xrange you can end up with a hash with a $ on it and then you will have problems to check the hash, the algorithm and the salt. I think you should try to not use the delimit char on the xrange algorithm to avoid this problem.
So why wasn't this added to the repo?
what do you think about https://github.com/juancho088/SecureMongoEngine ?
AS per : https://github.com/noQ/mongoengine