Login

Instructions and code to use drupal 7 passwords

Author:
grillermo
Posted:
June 1, 2013
Language:
Python
Version:
1.4
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. Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 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.