Projet

Général

Profil

0001-a2_rbac-do-not-break-unicity-when-get-or-creating-ad.patch

Paul Marillonnet, 28 juillet 2020 11:59

Télécharger (8,31 ko)

Voir les différences:

Subject: [PATCH] a2_rbac: do not break unicity when get-or-creating admin role
 (#42178)

 src/authentic2/a2_rbac/management.py | 32 +++++++++++++++++---------
 src/authentic2/a2_rbac/managers.py   | 34 +++++++++++++---------------
 src/authentic2/a2_rbac/models.py     |  2 +-
 tests/test_a2_rbac.py                |  6 ++---
 4 files changed, 41 insertions(+), 33 deletions(-)
src/authentic2/a2_rbac/management.py
20 20
from django.contrib.contenttypes.models import ContentType
21 21

  
22 22
from django_rbac.models import ADMIN_OP
23
from django_rbac.utils import get_role_model, get_ou_model
23
from django_rbac.utils import get_ou_model
24
from django_rbac.utils import get_permission_model
25
from django_rbac.utils import get_role_model
24 26

  
25 27
from ..utils import get_fk_model
26 28
from . import utils, app_settings
......
138 140
            continue
139 141
        ct_admin_role = Role.objects.get_admin_role(instance=ct, name=name,
140 142
                                                    slug=slug,
141
                                                    update_name=True)
143
                                                    update_name=True,
144
                                                    update_slug=True,
145
                                                    create=True)
142 146
        if MANAGED_CT[ct_tuple].get('must_view_user'):
143 147
            ct_admin_role.permissions.add(view_user_perm)
144 148
        ct_admin_role.permissions.add(search_ou_perm)
......
146 150

  
147 151

  
148 152
def update_user_admin_roles_permission():
149
    roles = get_role_model().objects.filter(slug__startswith='_a2-managers-of-role',
150
                                            permissions__operation__slug=ADMIN_OP.slug)
151
    for role in roles:
152
        old_perm = role.permissions.get(operation__slug=ADMIN_OP.slug)
153
        administered_role = old_perm.target
153
    role_ct = ContentType.objects.get_for_model(get_role_model())
154
    permissions = get_permission_model().objects.filter(target_ct=role_ct,
155
            operation__slug=ADMIN_OP.slug)
156
    for perm in permissions:
157
        administered_role = perm.target
154 158
        admin_role = administered_role.get_admin_role()
155
        new_perm = admin_role.permissions.get(operation__slug=MANAGE_MEMBERS_OP.slug)
159
        assert admin_role.slug.startswith('_a2-managers-of-role')
160
        new_perm = admin_role.permissions.get(
161
                operation__slug=MANAGE_MEMBERS_OP.slug)
162
        assert new_perm.ou is None
163

  
156 164
        admin_role.delete()
165
        assert len(perm.roles.all()) == 1
166
        role = perm.roles.first()
157 167
        role.admin_scope_id = new_perm.pk
158
        role.save()
159
        role.permissions.remove(old_perm)
168
        role.save(update_fields=['admin_scope_id'])
169
        role.permissions.remove(perm)
160 170
        role.permissions.add(new_perm)
161
        assert role.pk == administered_role.get_admin_role().pk
171
        assert role.pk == administered_role.get_admin_role(create=False).pk
src/authentic2/a2_rbac/managers.py
41 41
        op = get_operation(operation)
42 42
        Permission = rbac_utils.get_permission_model()
43 43
        if create:
44
            perm, created = Permission.objects.get_or_create(
44
            perm, _ = Permission.objects.get_or_create(
45 45
                operation=op,
46 46
                target_ct=ContentType.objects.get_for_model(instance),
47 47
                target_id=instance.pk,
......
55 55
                    **kwargs)
56 56
            except Permission.DoesNotExist:
57 57
                return None
58
            created = False
59 58

  
60 59
        admin_role = self.get_mirror_role(perm, name, slug, ou=ou,
61 60
                                          update_name=update_name,
......
76 75

  
77 76
    def get_mirror_role(self, instance, name, slug, ou=None,
78 77
                        update_name=False, update_slug=False, create=True):
79
        '''Get or create a role which mirror another model, for example a
78
        '''Get or create a role which mirrors another model, for example a
80 79
           permission.
81 80
        '''
82 81
        ct = ContentType.objects.get_for_model(instance)
82
        update_fields = {}
83 83
        kwargs = {}
84
        if ou or getattr(instance, 'ou', None):
85
            kwargs['ou'] = ou or instance.ou
84
        ou = ou or getattr(instance, 'ou', None)
85
        if ou:
86
            update_fields['ou'] = ou
86 87
        else:
87 88
            kwargs['ou__isnull'] = True
89
        if update_name:
90
            update_fields['name'] = name
91
        if update_slug:
92
            update_fields['slug'] = slug
93

  
88 94
        if create:
89
            role, created = self.prefetch_related('permissions').get_or_create(
95
            role, _ = self.prefetch_related('permissions').update_or_create(
90 96
                admin_scope_ct=ct,
91 97
                admin_scope_id=instance.pk,
92
                defaults={
93
                    'name': name,
94
                    'slug': slug,
95
                },
98
                defaults=update_fields,
96 99
                **kwargs)
97 100
        else:
98 101
            try:
......
102 105
                    **kwargs)
103 106
            except self.model.DoesNotExist:
104 107
                return None
105
            created = False
106

  
107
        if update_name and not created and role.name != name:
108
            role.name = name
109
            role.save()
110
        if update_slug and not created and role.slug != slug:
111
            role.slug = slug
112
            role.save()
108
            for field, value in update_fields.items():
109
                setattr(role, field, value)
110
            role.save(update_fields=update_fields)
113 111
        return role
114 112

  
115 113
    def get_by_natural_key(self, slug, ou_natural_key, service_natural_key):
src/authentic2/a2_rbac/models.py
151 151
        slug = '_a2-managers-of-{ou.slug}'.format(ou=self)
152 152
        return Role.objects.get_admin_role(
153 153
            instance=self, name=name, slug=slug, operation=VIEW_OP,
154
            update_name=True, update_slug=True)
154
            update_name=True, update_slug=True, create=True)
155 155

  
156 156
    def delete(self, *args, **kwargs):
157 157
        Permission.objects.filter(ou=self).delete()
tests/test_a2_rbac.py
108 108
    user = User.objects.create(username='john.doe')
109 109
    name1 = 'Can manage john.doe'
110 110
    slug1 = 'can-manage-john-doe'
111
    admin_role1 = Role.objects.get_admin_role(user, name1, slug1)
111
    admin_role1 = Role.objects.get_admin_role(user, name1, slug1, update_name=True, update_slug=True)
112 112
    assert admin_role1.name == name1
113 113
    assert admin_role1.slug == slug1
114 114
    name2 = 'Should manage john.doe'
......
403 403
    assert ar1.slug == '_a2-managers-of-role-r1ter'
404 404

  
405 405

  
406
def test_admin_role_user_view(settings, app, admin, simple_user, ou1, user_ou1, role_ou1):
406
def test_admin_role_user_view(db, settings, app, admin, simple_user, ou1, user_ou1, role_ou1):
407 407
    role_ou1.get_admin_role().members.add(simple_user)
408 408

  
409 409
    # Default: all users are visible
......
493 493
@pytest.mark.parametrize(
494 494
    'alert,deletion', [(-1, 31), (31, -1), (0, 31), (31, 0), (None, 31), (31, None), (32, 31)]
495 495
)
496
def test_unused_account_settings_validation(ou1, alert, deletion):
496
def test_unused_account_settings_validation(db, ou1, alert, deletion):
497 497
    ou1.clean_unused_accounts_alert = alert
498 498
    ou1.clean_unused_accounts_deletion = deletion
499 499
    with pytest.raises(ValidationError):
500
-