Projet

Général

Profil

0001-api-add-a-hashed_password-attribute-for-user-api-354.patch

Nicolas Roche, 05 septembre 2019 14:42

Télécharger (6,89 ko)

Voir les différences:

Subject: [PATCH] api: add a hashed_password attribute for user api (#35482)

 src/authentic2/api_views.py | 29 +++++++++++++++++++++++---
 tests/test_api.py           | 41 +++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 3 deletions(-)
src/authentic2/api_views.py
20 20
import django
21 21
from django.db import models
22 22
from django.contrib.auth import get_user_model
23
from django.contrib.auth.hashers import identify_hasher
23 24
from django.core.exceptions import MultipleObjectsReturned
24 25
from django.utils.translation import ugettext as _
25 26
from django.utils.encoding import force_text
......
340 341
    send_registration_email_next_url = serializers.URLField(write_only=True, required=False)
341 342
    password = serializers.CharField(max_length=128, required=False)
342 343
    force_password_reset = serializers.BooleanField(write_only=True, required=False, default=False)
344
    hashed_password = serializers.CharField(max_length=128, required=False)
343 345

  
344 346
    def __init__(self, *args, **kwargs):
345 347
        super(BaseUserSerializer, self).__init__(*args, **kwargs)
......
392 394
        attributes = validated_data.pop('attributes', {})
393 395
        is_verified = validated_data.pop('is_verified', {})
394 396
        password = validated_data.pop('password', None)
397
        hashed_password = validated_data.pop('hashed_password', None)
395 398
        self.check_perm('custom_user.add_user', validated_data.get('ou'))
396 399
        instance = super(BaseUserSerializer, self).create(validated_data)
397 400
        # prevent update on a get_or_create
......
413 416
        instance.save()
414 417
        if force_password_reset:
415 418
            PasswordReset.objects.get_or_create(user=instance)
419
        if hashed_password is not None:
420
            instance.password = hashed_password
421
            instance.save()
416 422
        if send_registration_email and validated_data.get('email'):
417 423
            try:
418 424
                utils.send_password_reset_mail(
......
437 443
        attributes = validated_data.pop('attributes', {})
438 444
        is_verified = validated_data.pop('is_verified', {})
439 445
        password = validated_data.pop('password', None)
446
        hashed_password = validated_data.pop('hashed_password', None)
440 447
        # Double check: to move an user from one ou into another you must be administrator of both
441 448
        self.check_perm('custom_user.change_user', instance.ou)
442 449
        if 'ou' in validated_data:
......
465 472
            instance.save()
466 473
        if force_password_reset:
467 474
            PasswordReset.objects.get_or_create(user=instance)
475
        if hashed_password is not None:
476
            instance.password = hashed_password
477
            instance.save()
468 478
        return instance
469 479

  
470 480
    def validate(self, data):
......
491 501
            if ou and ou.email_is_unique and qs.filter(ou=ou, email=data['email']).exists():
492 502
                already_used = True
493 503

  
504
        errors = {}
494 505
        if already_used:
495
            raise serializers.ValidationError({
496
                'email': 'email already used',
497
            })
506
            errors['email'] = 'email already used'
507
        if data.get('password') and data.get('hashed_password'):
508
            errors['password'] = 'conflict with provided hashed_password'
509
        if data.get('hashed_password'):
510
            try:
511
                hasher = identify_hasher(data.get('hashed_password'))
512
            except ValueError:
513
                errors['hashed_password'] = "unknown hash format"
514
            else:
515
                try:
516
                    hasher.safe_summary(data.get('hashed_password'))
517
                except Exception:
518
                    errors['hashed_password'] = "hash format error"
519
        if errors:
520
            raise serializers.ValidationError(errors)
498 521
        return data
499 522

  
500 523
    class Meta:
tests/test_api.py
22 22
import uuid
23 23

  
24 24

  
25
import django
25 26
from django.contrib.auth.hashers import check_password
26 27
from django.contrib.auth import get_user_model
27 28
from django.contrib.contenttypes.models import ContentType
......
1268 1269
    assert uuid == resp.json['uuid']
1269 1270
    assert Role.objects.get(uuid=uuid).name == 'Role 2'
1270 1271
    assert Role.objects.get(uuid=uuid).slug == 'role-1'
1272

  
1273

  
1274
def test_api_users_hashed_password(settings, app, admin):
1275
    app.authorization = ('Basic', (admin.username, admin.username))
1276
    payload = {
1277
        'email': 'john.doe@example.net',
1278
        'first_name': 'John',
1279
        'last_name': 'Doe',
1280
        'hashed_password': 'pbkdf2_sha256$36000$re9zaUj1ize0$bX1cqB91ni4aMOtRh8//TLaJkX+xnD2w84MCQx9AJcE=',
1281
    }
1282
    resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=201)
1283
    id = resp.json['id']
1284
    assert User.objects.get(id=id).first_name == 'John'
1285
    assert User.objects.get(id=id).last_name == 'Doe'
1286
    assert User.objects.get(id=id).check_password('admin')
1287
    password = User.objects.get(id=id).password
1288

  
1289
    payload['hashed_password'] = 'sha-oldap$$e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4'
1290
    resp = app.post_json('/api/users/?update_or_create=email', params=payload, status=200)
1291
    assert User.objects.get(id=id).password != password
1292
    assert User.objects.get(id=id).check_password('secret')
1293

  
1294
    payload['password'] = 'secret'
1295
    resp = app.post_json('/api/users/?update_or_create=email', params=payload, status=400)
1296
    assert resp.json['result'] == 0
1297
    assert resp.json['errors']['password'] == ['conflict with provided hashed_password']
1298

  
1299
    del payload['password']
1300
    payload['hashed_password'] = 'unknown_format'
1301
    resp = app.post_json('/api/users/?update_or_create=email', params=payload, status=400)
1302
    assert resp.json['result'] == 0
1303
    assert resp.json['errors']['hashed_password'] == ['unknown hash format']
1304

  
1305
    payload['hashed_password'] = 'argon2$wrong_format'
1306
    resp = app.post_json('/api/users/?update_or_create=email', params=payload, status=400)
1307
    assert resp.json['result'] == 0
1308
    if django.VERSION >= (1, 10, 0):
1309
        assert resp.json['errors']['hashed_password'] == ['hash format error']
1310
    else:  #  before Django 1.10 Argon2 is not recognized
1311
        assert resp.json['errors']['hashed_password'] == ['unknown hash format']
1271
-