Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PasswordField #8

Open
rozza opened this issue Jun 3, 2013 · 5 comments
Open

PasswordField #8

rozza opened this issue Jun 3, 2013 · 5 comments

Comments

@rozza
Copy link
Contributor

rozza commented Jun 3, 2013

AS per : https://github.com/noQ/mongoengine

@rozza
Copy link
Contributor Author

rozza commented Jun 3, 2013

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()

@sjose1x
Copy link

sjose1x commented Mar 13, 2015

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)

@minrock
Copy link

minrock commented Dec 28, 2015

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.

@Greyvend
Copy link

So why wasn't this added to the repo?

@overquota
Copy link

what do you think about https://github.com/juancho088/SecureMongoEngine ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants