csv_import: allow settings password hash (#50156)
This commit is contained in:
parent
7aa260def8
commit
0f4439afe1
|
@ -23,7 +23,8 @@ from chardet.universaldetector import UniversalDetector
|
|||
import attr
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.contrib.auth.hashers import identify_hasher
|
||||
from django.core.exceptions import FieldDoesNotExist, ValidationError
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db import IntegrityError, models
|
||||
from django.db.transaction import atomic
|
||||
|
@ -249,9 +250,10 @@ SOURCE_ID = '_source_id'
|
|||
SOURCE_COLUMNS = set([SOURCE_NAME, SOURCE_ID])
|
||||
ROLE_NAME = '_role_name'
|
||||
ROLE_SLUG = '_role_slug'
|
||||
PASSWORD_HASH = 'password_hash'
|
||||
REGISTRATION = '@registration'
|
||||
REGISTRATION_RESET_EMAIL = 'send-email'
|
||||
SPECIAL_COLUMNS = SOURCE_COLUMNS | {ROLE_NAME, ROLE_SLUG, REGISTRATION}
|
||||
SPECIAL_COLUMNS = SOURCE_COLUMNS | {ROLE_NAME, ROLE_SLUG, REGISTRATION, PASSWORD_HASH}
|
||||
|
||||
|
||||
class ImportUserForm(BaseUserForm):
|
||||
|
@ -268,11 +270,23 @@ class ImportUserForm(BaseUserForm):
|
|||
choices=choices,
|
||||
label=_('Registration option'),
|
||||
required=False)
|
||||
locals()[PASSWORD_HASH] = forms.CharField(
|
||||
label=_('Password hash'),
|
||||
required=False)
|
||||
|
||||
|
||||
def clean(self):
|
||||
super(BaseUserForm, self).clean()
|
||||
self._validate_unique = False
|
||||
|
||||
def clean_password_hash(self):
|
||||
password_hash = self.cleaned_data['password_hash']
|
||||
try:
|
||||
hasher = identify_hasher(password_hash)
|
||||
except ValueError:
|
||||
raise ValidationError(_('Invalid password format or unknown hashing algorithm.'))
|
||||
return password_hash
|
||||
|
||||
|
||||
class ImportUserFormWithExternalId(ImportUserForm):
|
||||
locals()[SOURCE_NAME] = forms.CharField(
|
||||
|
@ -731,6 +745,9 @@ class UserCsvImporter(object):
|
|||
success &= self.add_role(cell, user, do_clear=True)
|
||||
elif cell.header.name == REGISTRATION and row.action == 'create':
|
||||
success &= self.registration_option(cell, user)
|
||||
elif cell.header.name == PASSWORD_HASH:
|
||||
user.password = cell.value
|
||||
user.save()
|
||||
|
||||
setattr(self, row.action + 'd', getattr(self, row.action + 'd') + 1)
|
||||
return success
|
||||
|
|
|
@ -250,6 +250,13 @@ john.doe@example.com,John,Doe,Role2
|
|||
<pre>email key,first_name,last_name,@registration
|
||||
john.doe@example.com,John,Doe,send-email
|
||||
jane.doe@example.com,Jane,Doe,
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>{% blocktrans trimmed %}Importing email, first and last name of users
|
||||
and setting a password using hash in standard Django format.{% endblocktrans %}</p>
|
||||
<blockquote>
|
||||
<pre>email key,first_name,last_name,password_hash
|
||||
john.doe@example.com,John,Doe,pbkdf2_sha256$36000$oTHdVaoMjnCp$uTkpF7Ne6KV/L5gAerS7mngXM96DOEaLsLMZ251HJ/M=
|
||||
</pre>
|
||||
</blockquote>
|
||||
</div>
|
||||
|
|
|
@ -770,6 +770,11 @@ class UserImportsView(MediaMixin, PermissionMixin, FormView):
|
|||
'name': attribute.name,
|
||||
'key': attribute.name == key,
|
||||
})
|
||||
help_columns.append({
|
||||
'label': _('Password hash'),
|
||||
'name': 'password_hash',
|
||||
'key': False,
|
||||
})
|
||||
ctx['help_columns'] = help_columns
|
||||
example_data = u','.join(column['name'] + (' key' if column['key'] else '') for column in help_columns) + '\n'
|
||||
example_url = 'data:text/csv;base64,%s' % base64.b64encode(example_data.encode('utf-8')).decode('ascii')
|
||||
|
|
|
@ -22,6 +22,7 @@ import pytest
|
|||
import io
|
||||
import codecs
|
||||
|
||||
from django.contrib.auth.hashers import make_password, check_password
|
||||
from django.core import mail
|
||||
|
||||
from django_rbac.utils import get_role_model
|
||||
|
@ -597,3 +598,30 @@ remote,8,john.doe,,,
|
|||
for row in importer.rows if any(cell.errors for cell in row.cells)
|
||||
}
|
||||
assert cell_errors == {}
|
||||
|
||||
|
||||
def test_csv_password_hash(profile, user_csv_importer_factory):
|
||||
content = '''email key,first_name,last_name,password_hash
|
||||
tnoel@entrouvert.com,Thomas,Noël,%s'''
|
||||
password_hash = make_password('hop')
|
||||
|
||||
importer = user_csv_importer_factory(content % password_hash)
|
||||
assert importer.run()
|
||||
thomas = User.objects.get(email='tnoel@entrouvert.com')
|
||||
assert check_password('hop', thomas.password)
|
||||
|
||||
password_hash = make_password('test', hasher='sha256')
|
||||
importer = user_csv_importer_factory(content % password_hash)
|
||||
assert importer.run()
|
||||
thomas.refresh_from_db()
|
||||
assert check_password('test', thomas.password)
|
||||
|
||||
importer = user_csv_importer_factory(content % 'wrong-format')
|
||||
assert importer.run()
|
||||
assert importer.has_errors
|
||||
assert 'unknown hashing algorithm' in importer.rows[0].cells[-1].errors[0].description
|
||||
|
||||
importer = user_csv_importer_factory(content)
|
||||
assert importer.run()
|
||||
assert importer.has_errors
|
||||
assert 'unknown hashing algorithm' in importer.rows[0].cells[-1].errors[0].description
|
||||
|
|
Loading…
Reference in New Issue