import users from getpersonas.com (bug 726814)

This commit is contained in:
Andy McKay 2012-03-06 16:41:07 -08:00
Родитель 3771eab0e2
Коммит 503ac1440f
3 изменённых файлов: 173 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,138 @@
from base64 import decodestring
from optparse import make_option
from time import time
from django.core.management.base import BaseCommand
from addons.models import Persona
from bandwagon.models import CollectionAddon
from users.models import UserProfile
import MySQLdb as mysql
from django.db import transaction
class Command(BaseCommand):
"""
Import from the personas database:
`task`: the table to import from, eg users
`host`: the host of the personas database
`database`: the personas database, eg: personas
`commit`: if yes, actually commit the transaction, for any other value, it
aborts the transaction at the end.
"""
option_list = BaseCommand.option_list + (
make_option('--task', action='store',
dest='task', help='The task to import'),
make_option('--host', action='store',
dest='host', help='The host of MySQL'),
make_option('--db', action='store',
dest='db', help='The database in MySQL'),
make_option('--user', action='store',
dest='user', help='The database user'),
make_option('--password', action='store',
dest='password', help='The database user password'),
make_option('--commit', action='store',
dest='commit', help='If: yes, then commits the run'),
)
def log(self, msg):
print msg
def connect(self, **options):
options = dict([(k, v) for k, v in options.items() if k in
['host', 'db', 'user', 'password'] and v])
self.connection = mysql.connect(**options)
self.cursor = self.connection.cursor()
def handle_users(self):
self.log('Importing users.')
self.cursor.execute('SELECT count(username) from users')
k, step = 0, 10
count = self.cursor.fetchone()[0]
self.log('Found %s users.' % count)
for x in range(0, count, step):
k += step
self.log('Doing %s to %s' % (x, k))
self.cursor.execute('SELECT * FROM users ORDER BY username '
'LIMIT %s OFFSET %s' % (step, k))
for user in self.cursor.fetchall():
self._handle_user(user)
def handle_designers(self):
# TODO: bug 726186
pass
def handle_images(self):
# TODO: bug 726190
pass
def _handle_user(self, user):
user = dict(zip(['username', 'display_username', 'md5', 'email',
'privs', 'change_code', 'news', 'description'], user))
for k in ['username', 'display_username', 'email']:
user[k] = user[k].decode('latin1').encode('utf-8')
profile = UserProfile.objects.filter(email=user['email'])
if UserProfile.objects.filter(username=user['username']).exists():
user['username'] = user['username'] + '-' + time()
self.log('Username already taken, so making username: %s'
% user['username'])
if profile:
self.log('Ignoring %s' % user['email'])
else:
self.log('Creating user for %s' % user['email'])
note = 'Imported from personas, username: %s' % user['username']
algo, salt, password = user['md5'].split('$')
# The salt is a bytes string. In get personas it is base64
# encoded. I'd like to decode here so we don't have to do any
# more work, but that means MySQL doesn't like the bytes that
# get written to the column. So we'll have to persist that
# base64 encoding. Let's add +base64 on to it so we know this in
# zamboni.
password = '$'.join([algo + '+base64', salt, password])
profile = UserProfile.objects.create(username=user['username'],
email=user['email'],
password=password,
notes=note)
profile.create_django_user()
# Now sort out any favourites.
self.cursor.execute('SELECT id FROM favorites WHERE '
'username = %s', user['username'])
favs = self.cursor.fetchall()
fav = profile.favorites_collection()
for fav in favs:
try:
addon = Persona.objects.get(persona_id=fav[0]).addon
except Persona.DoesNotExist:
self.log('Not found fav. %s for user %s' %
(fav[0], user['username']))
continue
CollectionAddon.objects.create(collection=fav, addon=addon)
self.log('Adding fav. %s for user %s' % (addon, user['username']))
@transaction.commit_manually
def handle(self, *args, **options):
task = options.get('task')
task = getattr(self, 'handle_%s' % task, None)
if not task:
raise ValueError('Unknown task: %s' % task)
self.connect(**options)
try:
task()
except:
self.log('Error, not committing changes.')
transaction.rollback()
raise
finally:
if options.get('commit'):
self.log('Committing changes.')
transaction.commit()
else:
self.log('Not committing changes, this is a dry run.')
transaction.rollback()

Просмотреть файл

@ -1,3 +1,4 @@
from base64 import decodestring
from datetime import datetime
import hashlib
import os
@ -30,6 +31,18 @@ log = commonware.log.getLogger('z.users')
def get_hexdigest(algorithm, salt, raw_password):
if 'base64' in algorithm:
# These are getpersonas passwords with base64 encoded salts.
salt = decodestring(salt)
algorithm = algorithm.replace('+base64', '')
if algorithm.startswith('sha512+MD5'):
# These are persona specific passwords when we imported
# users from getpersonas.com. The password is md5 hashed
# and then sha512'd.
md5 = hashlib.new('md5', raw_password).hexdigest()
return hashlib.new('sha512', smart_str(salt + md5)).hexdigest()
return hashlib.new(algorithm, smart_str(salt + raw_password)).hexdigest()

Просмотреть файл

@ -1,3 +1,4 @@
from base64 import encodestring
import datetime
import hashlib
from urlparse import urlparse
@ -201,6 +202,7 @@ class TestUserProfile(amo.tests.TestCase):
class TestPasswords(amo.tests.TestCase):
utf = u'\u0627\u0644\u062a\u0637\u0628'
bytes_ = '\xb1\x98og\x88\x87\x08q'
def test_invalid_old_password(self):
u = UserProfile(password=self.utf)
@ -225,6 +227,26 @@ class TestPasswords(amo.tests.TestCase):
u.set_password(self.utf)
assert u.check_password(self.utf) is True
def test_persona_sha512_md5(self):
md5 = hashlib.md5('password').hexdigest()
hsh = hashlib.sha512(self.bytes_ + md5).hexdigest()
u = UserProfile(password='sha512+MD5$%s$%s' %
(self.bytes_, hsh))
assert u.check_password('password') is True
def test_persona_sha512_base64(self):
hsh = hashlib.sha512(self.bytes_ + 'password').hexdigest()
u = UserProfile(password='sha512+base64$%s$%s' %
(encodestring(self.bytes_), hsh))
assert u.check_password('password') is True
def test_persona_sha512_md5_base64(self):
md5 = hashlib.md5('password').hexdigest()
hsh = hashlib.sha512(self.bytes_ + md5).hexdigest()
u = UserProfile(password='sha512+MD5+base64$%s$%s' %
(encodestring(self.bytes_), hsh))
assert u.check_password('password') is True
class TestBlacklistedUsername(amo.tests.TestCase):
fixtures = ['users/test_backends']