Login

Instructions and code to use drupal 7 passwords

Author:
grillermo
Posted:
June 1, 2013
Language:
Python
Version:
1.4
Tags:
django password drupal
Score:
0 (after 0 ratings)

This is another fork of http://djangosnippets.org/snippets/2729/ that fixes the issue.

Unlike those other versions i give you instructions so it works for you, this is modified a little.

Instructions:

If you want to import the passwords from drupal you need to prepend to each of them the word drupal so it looks like this:

drupal$S$DQjyXl0F7gupCqleCuraCkQJTzC3qAourXB7LvpOHKx0YAfihiPC

Then add this snippet to someapp/hashers/DrupalPasswordHasher.py

And then in your settings add this:

PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'someapp.hashers.DrupalPasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.CryptPasswordHasher', )

So, what did i modify

First i added the attribute algorithm to the class, django uses this word to identify wich hasher to use, so the passwords beggining with drupal should be verified with the hasher.algorithm == 'drupal'

Ok so know django knows to use our class, but now our passwords won't validate because we changed them by adding the word drupal, so what do we do? we modify the verify method to remove the word drupal before verification :P

Hope it helps

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
import hashlib

from django.contrib.auth.hashers import BasePasswordHasher

class DrupalPasswordHasher(BasePasswordHasher):
    """
    >>> h = DrupalPasswordHasher()
    >>> h.verify("password1234", "$S$DeIZ1KTE.VzRvudZ5.xgOakipuMFrVyPmRdWTjAdYieWj27NMglI")
    True
    """
    
    DRUPAL_HASH_COUNT = 15
    DRUPAL_MIN_HASH_COUNT = 7
    DRUPAL_MAX_HASH_COUNT = 30
    DRUPAL_HASH_LENGTH = 55
    _ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
    algorithm = 'drupal'
    
    def verify(self, password, hashed_password):
        hashed_password = hashed_password.replace('drupal','')

        setting = hashed_password[0:12]
        
        if setting[0] != '$' or setting[2] != '$':
            return False
            
        count_log2 = self._ITOA64.index(setting[3])
        
        if count_log2 < self.DRUPAL_MIN_HASH_COUNT or count_log2 > self.DRUPAL_MAX_HASH_COUNT:
            return False
  
        salt = setting[4:4+8]
        
        if len(salt) != 8:
            return False
        
        count = 2 ** count_log2
           
        pass_hash = hashlib.sha512(salt + password).digest()

        for _ in range(count):
            pass_hash = hashlib.sha512(pass_hash + password).digest()

        hash_length = len(pass_hash)

        output = setting + self._password_base64_encode(pass_hash, hash_length)
        
        if len(output) != 98:
            return False
        
        return output[:self.DRUPAL_HASH_LENGTH] == hashed_password      

    def safe_summary(self, encoded):
        algorithm, iterations, salt = encoded.split('$', 3)
        return SortedDict([
            ('algorithm', self.algorithm),
            ('iterations', iterations),
            ('salt', 'salt'),
            ('hash', 'hash'),
        ])


    def _password_base64_encode(self, to_encode, count):
        output = ''
        i = 0
        while True:
            value = ord(to_encode[i])
            
            i += 1
            
            output = output + self._ITOA64[value & 0x3f]
            if i < count:
                value |= ord(to_encode[i]) << 8
            output = output + self._ITOA64[(value >> 6) & 0x3f]
    
            if i >= count:
                break

            i += 1
    
            if i < count:
                value |= ord(to_encode[i]) << 16
            
            output = output + self._ITOA64[(value >> 12) & 0x3f]
    
            if i >= count:
                break
            
            i += 1
            
            output = output + self._ITOA64[(value >> 18) & 0x3f]
            
            if i >= count:
                break
        
        return output
  
if __name__ == "__main__":
    import doctest
    doctest.testmod()

More like this

  1. Plaintext password by yetty 1 year, 9 months ago
  2. Complex Formsets, Redux by smagala 5 years, 4 months ago
  3. Log all interaction with user to the DB by inuwashi 4 years, 6 months ago
  4. Custom managers with chainable filters by itavor 7 years, 6 months ago
  5. Allow filtering and ordering by counts of related query results by exogen 8 years, 3 months ago

Comments

tlockhart (on September 20, 2013):

Seems to have a (small) problem with (at least) Django-1.5 and python-2.7. The password is passed in to the verify() method as a unicode string and the call to hashlib.sha512 blows chunks at some point when iterating when it ends up with a non-ascii character in the hash. This can be fixed with the following extra bit of code at the top of the method:

if (isinstance(password, unicode)): password = password.encode('ascii')

#

Please login first before commenting.