Projet

Général

Profil

0008-apply-soft-deletion-logic-when-retrieving-roleparent.patch

Paul Marillonnet, 15 octobre 2021 10:42

Télécharger (11,5 ko)

Voir les différences:

Subject: [PATCH 8/8] apply soft-deletion logic when retrieving roleparenting
 objects (#57500)

 src/authentic2/custom_user/models.py          |  7 +-
 .../management/commands/check-and-repair.py   |  4 +-
 src/authentic2/manager/resources.py           |  3 +-
 src/authentic2/manager/role_views.py          |  4 +-
 src/authentic2/manager/tables.py              |  4 +-
 src/authentic2/manager/user_views.py          |  2 +-
 src/django_rbac/managers.py                   | 18 ++++-
 src/django_rbac/models.py                     |  6 +-
 tests_rbac/test_rbac.py                       | 66 ++++++++++++++++++-
 9 files changed, 98 insertions(+), 16 deletions(-)
src/authentic2/custom_user/models.py
219 219

  
220 220
    def roles_and_parents(self):
221 221
        qs1 = self.roles.all()
222
        qs2 = qs1.model.objects.filter(child_relation__child__in=qs1)
222
        qs2 = qs1.model.objects.filter(
223
            child_relation__deleted__isnull=True,
224
            child_relation__child__in=qs1,
225
        )
223 226
        qs = (qs1 | qs2).order_by('name').distinct()
224 227
        RoleParenting = get_role_parenting_model()
225
        rp_qs = RoleParenting.objects.filter(child__in=qs1)
228
        rp_qs = RoleParenting.active_objects.filter(child__in=qs1)
226 229
        qs = qs.prefetch_related(models.Prefetch('child_relation', queryset=rp_qs), 'child_relation__parent')
227 230
        qs = qs.prefetch_related(
228 231
            models.Prefetch('members', queryset=self.__class__.objects.filter(pk=self.pk), to_attr='member')
src/authentic2/management/commands/check-and-repair.py
388 388
                    direct_members = manager_role.members.all()
389 389
                    direct_members_count = direct_members.count()
390 390
                    direct_children = Role.objects.filter(
391
                        parent_relation__parent=manager_role, parent_relation__direct=True
391
                        parent_relation__deleted__isnull=True,
392
                        parent_relation__parent=manager_role,
393
                        parent_relation__direct=True,
392 394
                    )
393 395
                    direct_children_count = direct_children.count()
394 396
                    show = members_count or self.verbosity > 1
src/authentic2/manager/resources.py
40 40
        for role in instance.roles.all():
41 41
            result.add(role)
42 42
            for pr in role.parent_relation.all():
43
                result.add(pr.parent)
43
                if pr.deleted is None:
44
                    result.add(pr.parent)
44 45
        return ', '.join(str(x) for x in result)
45 46

  
46 47
    class Meta:
src/authentic2/manager/role_views.py
396 396
            )
397 397
        )
398 398
        RoleParenting = get_role_parenting_model()
399
        rp_qs = RoleParenting.objects.filter(parent__in=children).annotate(name=F('parent__name'))
399
        rp_qs = RoleParenting.active_objects.filter(parent__in=children).annotate(name=F('parent__name'))
400 400
        qs = qs.prefetch_related(Prefetch('parent_relation', queryset=rp_qs, to_attr='via'))
401 401
        return qs
402 402

  
......
461 461
            )
462 462
        )
463 463
        RoleParenting = get_role_parenting_model()
464
        rp_qs = RoleParenting.objects.filter(child__in=parents).annotate(name=F('child__name'))
464
        rp_qs = RoleParenting.active_objects.filter(child__in=parents).annotate(name=F('child__name'))
465 465
        qs = qs.prefetch_related(Prefetch('child_relation', queryset=rp_qs, to_attr='via'))
466 466
        return qs
467 467

  
src/authentic2/manager/tables.py
182 182
    )
183 183
    ou = tables.Column()
184 184
    via = tables.TemplateColumn(
185
        '{% if not record.member %}{% for rel in record.child_relation.all %}{{ rel.child }} {% if not'
186
        ' forloop.last %}, {% endif %}{% endfor %}{% endif %}',
185
        '{% if not record.member %}{% for rel in record.child_relation.all %}{% if not rel.deleted %}'
186
        '{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endif %}{% endfor %}{% endif %}',
187 187
        verbose_name=_('Inherited from'),
188 188
        orderable=False,
189 189
    )
src/authentic2/manager/user_views.py
643 643
            User = get_user_model()
644 644
            Role = get_role_model()
645 645
            RoleParenting = get_role_parenting_model()
646
            rp_qs = RoleParenting.objects.filter(child__in=roles)
646
            rp_qs = RoleParenting.active_objects.filter(child__in=roles)
647 647
            qs = Role.objects.all()
648 648
            qs = qs.prefetch_related(models.Prefetch('child_relation', queryset=rp_qs, to_attr='via'))
649 649
            qs = qs.prefetch_related(
src/django_rbac/managers.py
109 109
        return self.filter(members=user).parents().distinct()
110 110

  
111 111
    def parents(self, include_self=True, annotate=False):
112
        qs = self.model.objects.filter(child_relation__child__in=self)
112
        qs = self.model.objects.filter(
113
            child_relation__deleted__isnull=True,
114
            child_relation__child__in=self,
115
        )
113 116
        if include_self:
114 117
            qs = self | qs
115 118
        qs = qs.distinct()
......
118 121
        return qs
119 122

  
120 123
    def children(self, include_self=True, annotate=False):
121
        qs = self.model.objects.filter(parent_relation__parent__in=self)
124
        qs = self.model.objects.filter(
125
            parent_relation__deleted__isnull=True,
126
            parent_relation__parent__in=self,
127
        )
122 128
        if include_self:
123 129
            qs = self | qs
124 130
        qs = qs.distinct()
......
130 136
        User = get_user_model()
131 137
        prefetch = Prefetch('roles', queryset=self, to_attr='direct')
132 138
        return (
133
            User.objects.filter(Q(roles__in=self) | Q(roles__parent_relation__parent__in=self))
139
            User.objects.filter(
140
                Q(roles__in=self)
141
                | (
142
                    Q(roles__parent_relation__parent__in=self)
143
                    & Q(roles__parent_relation__deleted__isnull=True)
144
                )
145
            )
134 146
            .distinct()
135 147
            .prefetch_related(prefetch)
136 148
        )
src/django_rbac/models.py
217 217
    def all_members(self):
218 218
        User = get_user_model()
219 219
        prefetch = Prefetch('roles', queryset=self.__class__.objects.filter(pk=self.pk), to_attr='direct')
220

  
220 221
        return (
221
            User.objects.filter(Q(roles=self) | Q(roles__parent_relation__parent=self))
222
            User.objects.filter(
223
                Q(roles=self)
224
                | Q(roles__parent_relation__parent=self) & Q(roles__parent_relation__deleted__isnull=True)
225
            )
222 226
            .distinct()
223 227
            .prefetch_related(prefetch)
224 228
        )
tests_rbac/test_rbac.py
66 66
        assert role.parents().count() == i + 1
67 67
        assert role.children(False).count() == 6 - i - 1
68 68
        assert role.parents(False).count() == i
69
    RoleParenting.objects.filter(parent=roles[2], child=roles[3], direct=True).delete()
70
    assert RoleParenting.objects.filter(direct=True).count() == 4
71
    assert RoleParenting.objects.filter(direct=False).count() == 2
69
    RoleParenting.objects.soft_delete(roles[2], roles[3], direct=True)
70
    assert (
71
        RoleParenting.active_objects.filter(
72
            direct=True,
73
        ).count()
74
        == 4
75
    )
76
    assert (
77
        RoleParenting.active_objects.filter(
78
            direct=False,
79
        ).count()
80
        == 2
81
    )
72 82
    # test that it works with cycles
73 83
    RoleParenting.objects.create(parent=roles[2], child=roles[3])
74 84
    RoleParenting.objects.create(parent=roles[5], child=roles[0])
......
77 87
        assert role.parents().count() == 6
78 88

  
79 89

  
90
def test_role_parenting_soft_delete_children(db):
91
    OrganizationalUnit = utils.get_ou_model()
92
    Role = utils.get_role_model()
93
    RoleParenting = utils.get_role_parenting_model()
94

  
95
    ou = OrganizationalUnit.objects.create(name='ou')
96
    roles = []
97
    for i in range(10):
98
        roles.append(Role.objects.create(name='r%d' % i, ou=ou))
99
    assert not len(RoleParenting.objects.all())
100

  
101
    rps = []
102
    for i in range(5):
103
        rps.append(RoleParenting.objects.create(parent=roles[9 - i], child=roles[i]))
104
    assert len(RoleParenting.objects.all()) == 5
105
    for i in range(5):
106
        roles[9 - i].remove_child(roles[i])
107
        assert len(RoleParenting.objects.all()) == 5
108
        assert len(RoleParenting.active_objects.all()) == 4 - i
109
    for i in range(5):
110
        roles[9 - i].add_child(roles[i])
111
        assert len(RoleParenting.objects.all()) == 5
112
        assert len(RoleParenting.active_objects.all()) == i + 1
113

  
114

  
115
def test_role_parenting_soft_delete_parents(db):
116
    OrganizationalUnit = utils.get_ou_model()
117
    Role = utils.get_role_model()
118
    RoleParenting = utils.get_role_parenting_model()
119

  
120
    ou = OrganizationalUnit.objects.create(name='ou')
121
    roles = []
122
    for i in range(10):
123
        roles.append(Role.objects.create(name='r%d' % i, ou=ou))
124
    assert not len(RoleParenting.objects.all())
125

  
126
    rps = []
127
    for i in range(5):
128
        rps.append(RoleParenting.objects.create(child=roles[9 - i], parent=roles[i]))
129
    assert len(RoleParenting.objects.all()) == 5
130
    for i in range(5):
131
        roles[9 - i].remove_parent(roles[i])
132
        assert len(RoleParenting.objects.all()) == 5
133
        assert len(RoleParenting.active_objects.all()) == 4 - i
134
    for i in range(5):
135
        roles[9 - i].add_parent(roles[i])
136
        assert len(RoleParenting.objects.all()) == 5
137
        assert len(RoleParenting.active_objects.all()) == i + 1
138

  
139

  
80 140
SIZE = 1000
81 141
SPAN = 50
82 142

  
83
-