csv_import: allow settings password hash (#50156)

This commit is contained in:
Valentin Deniaud 2021-02-23 15:28:56 +01:00
parent 7aa260def8
commit 0f4439afe1
4 changed files with 59 additions and 2 deletions

View File

@ -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

View File

@ -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>

View File

@ -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')

View File

@ -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