Projet

Général

Profil

0001-csv_import-allow-settings-password-hash-50156.patch

Valentin Deniaud, 23 février 2021 15:31

Télécharger (6,12 ko)

Voir les différences:

Subject: [PATCH] csv_import: allow settings password hash (#50156)

 src/authentic2/csv_import.py                  | 21 ++++++++++++--
 .../authentic2/manager/user_imports.html      |  7 +++++
 src/authentic2/manager/user_views.py          |  5 ++++
 tests/test_csv_import.py                      | 28 +++++++++++++++++++
 4 files changed, 59 insertions(+), 2 deletions(-)
src/authentic2/csv_import.py
23 23
import attr
24 24

  
25 25
from django import forms
26
from django.core.exceptions import FieldDoesNotExist
26
from django.contrib.auth.hashers import identify_hasher
27
from django.core.exceptions import FieldDoesNotExist, ValidationError
27 28
from django.core.validators import RegexValidator
28 29
from django.db import IntegrityError, models
29 30
from django.db.transaction import atomic
......
249 250
SOURCE_COLUMNS = set([SOURCE_NAME, SOURCE_ID])
250 251
ROLE_NAME = '_role_name'
251 252
ROLE_SLUG = '_role_slug'
253
PASSWORD_HASH = 'password_hash'
252 254
REGISTRATION = '@registration'
253 255
REGISTRATION_RESET_EMAIL = 'send-email'
254
SPECIAL_COLUMNS = SOURCE_COLUMNS | {ROLE_NAME, ROLE_SLUG, REGISTRATION}
256
SPECIAL_COLUMNS = SOURCE_COLUMNS | {ROLE_NAME, ROLE_SLUG, REGISTRATION, PASSWORD_HASH}
255 257

  
256 258

  
257 259
class ImportUserForm(BaseUserForm):
......
268 270
        choices=choices,
269 271
        label=_('Registration option'),
270 272
        required=False)
273
    locals()[PASSWORD_HASH] = forms.CharField(
274
        label=_('Password hash'),
275
        required=False)
276

  
271 277

  
272 278
    def clean(self):
273 279
        super(BaseUserForm, self).clean()
274 280
        self._validate_unique = False
275 281

  
282
    def clean_password_hash(self):
283
        password_hash = self.cleaned_data['password_hash']
284
        try:
285
            hasher = identify_hasher(password_hash)
286
        except ValueError:
287
            raise ValidationError(_('Invalid password format or unknown hashing algorithm.'))
288
        return password_hash
289

  
276 290

  
277 291
class ImportUserFormWithExternalId(ImportUserForm):
278 292
    locals()[SOURCE_NAME] = forms.CharField(
......
722 736
                success &= self.add_role(cell, user, do_clear=True)
723 737
            elif cell.header.name == REGISTRATION and row.action == 'create':
724 738
                success &= self.registration_option(cell, user)
739
            elif cell.header.name == PASSWORD_HASH:
740
                user.password = cell.value
741
                user.save()
725 742

  
726 743
        setattr(self, row.action + 'd', getattr(self, row.action + 'd') + 1)
727 744
        return success
src/authentic2/manager/templates/authentic2/manager/user_imports.html
250 250
      <pre>email key,first_name,last_name,@registration
251 251
john.doe@example.com,John,Doe,send-email
252 252
jane.doe@example.com,Jane,Doe,
253
</pre>
254
    </blockquote>
255
    <p>{% blocktrans trimmed %}Importing email, first and last name of users
256
    and setting a password using hash in standard Django format.{% endblocktrans %}</p>
257
    <blockquote>
258
      <pre>email key,first_name,last_name,password_hash
259
john.doe@example.com,John,Doe,pbkdf2_sha256$36000$oTHdVaoMjnCp$uTkpF7Ne6KV/L5gAerS7mngXM96DOEaLsLMZ251HJ/M=
253 260
</pre>
254 261
    </blockquote>
255 262
  </div>
src/authentic2/manager/user_views.py
777 777
                'name': attribute.name,
778 778
                'key': attribute.name == key,
779 779
            })
780
        help_columns.append({
781
            'label': _('Password hash'),
782
            'name': 'password_hash',
783
            'key': False,
784
        })
780 785
        ctx['help_columns'] = help_columns
781 786
        example_data = u','.join(column['name'] + (' key' if column['key'] else '') for column in help_columns) + '\n'
782 787
        example_url = 'data:text/csv;base64,%s' % base64.b64encode(example_data.encode('utf-8')).decode('ascii')
tests/test_csv_import.py
22 22
import io
23 23
import codecs
24 24

  
25
from django.contrib.auth.hashers import make_password, check_password
25 26
from django.core import mail
26 27

  
27 28
from django_rbac.utils import get_role_model
......
564 565
        for row in importer.rows if any(cell.errors for cell in row.cells)
565 566
    }
566 567
    assert cell_errors == {}
568

  
569

  
570
def test_csv_password_hash(profile, user_csv_importer_factory):
571
    content = '''email key,first_name,last_name,password_hash
572
tnoel@entrouvert.com,Thomas,Noël,%s'''
573
    password_hash = make_password('hop')
574

  
575
    importer = user_csv_importer_factory(content % password_hash)
576
    assert importer.run()
577
    thomas = User.objects.get(email='tnoel@entrouvert.com')
578
    assert check_password('hop', thomas.password)
579

  
580
    password_hash = make_password('test', hasher='sha256')
581
    importer = user_csv_importer_factory(content % password_hash)
582
    assert importer.run()
583
    thomas.refresh_from_db()
584
    assert check_password('test', thomas.password)
585

  
586
    importer = user_csv_importer_factory(content % 'wrong-format')
587
    assert importer.run()
588
    assert importer.has_errors
589
    assert 'unknown hashing algorithm' in importer.rows[0].cells[-1].errors[0].description
590

  
591
    importer = user_csv_importer_factory(content)
592
    assert importer.run()
593
    assert importer.has_errors
594
    assert 'unknown hashing algorithm' in importer.rows[0].cells[-1].errors[0].description
567
-