Projet

Général

Profil

0002-manager-prevent-changing-ldap-sychronised-role-affec.patch

Valentin Deniaud, 20 avril 2020 17:33

Télécharger (8,29 ko)

Voir les différences:

Subject: [PATCH 2/2] manager: prevent changing ldap-sychronised role
 affectations (#37187)

 src/authentic2/manager/tables.py              |  4 ++-
 .../authentic2/manager/user_roles_table.html  |  6 +++-
 src/authentic2/manager/user_views.py          | 15 +++++++--
 src/authentic2/manager/views.py               | 17 +++++++++-
 tests/test_ldap.py                            | 32 +++++++++++++++++++
 5 files changed, 69 insertions(+), 5 deletions(-)
src/authentic2/manager/tables.py
122 122
        'indeterminate{%% endif %%}"'
123 123
        ' name="role-{{ record.pk }}" type="checkbox" {%% if record.member %%}checked{%% endif %%} '
124 124
        '{%% if not record.has_perm %%}disabled '
125
        'title="{%% trans "%s" %%}"{%% endif %%}/>' % ugettext_noop('You are not authorized to manage this role'),
125
        'title="{%% trans "%s" %%}"{%% endif %%} '
126
        '{%% if record.from_ldap %%}disabled '
127
        'title="{%% trans "%s" %%}"{%% endif %%}/>' % (ugettext_noop('You are not authorized to manage this role'), ugettext_noop('This role is synchronised from LDAP, changing members is not allowed.')),
126 128
        verbose_name=_('Member'),
127 129
        order_by=('member', 'via', 'name'))
128 130

  
src/authentic2/manager/templates/authentic2/manager/user_roles_table.html
7 7
    <th></th>
8 8
  {% endblock %}
9 9
  {% block table.tbody.last.column %}
10
  <td>{% if table.context.view.can_change and row.record.member %}<a class="icon-remove-sign js-remove-object" data-confirm="{% blocktrans with name=row.record.name username=table.context.object.get_full_name %}Do you really want to remove role &quot;{{ name }}&quot; from user &quot;{{ username }}&quot;&nbsp;?{% endblocktrans %}" href="#" data-pk-arg="role"></a>{% endif %}</td>
10
  <td>
11
    {% if table.context.view.can_change and row.record.member %}
12
      <a class="icon-remove-sign {% if row.record.from_ldap %} disabled {% else %} js-remove-object {% endif %}" data-confirm="{% blocktrans with name=row.record.name username=table.context.object.get_full_name %}Do you really want to remove role &quot;{{ name }}&quot; from user &quot;{{ username }}&quot;&nbsp;?{% endblocktrans %}" href="#" data-pk-arg="role" title="{% if row.record.from_ldap %}{% trans "This role is synchronised from LDAP, changing members is not allowed." %}{% endif %}"></a>
13
    {% endif %}
14
  </td>
11 15
  {% endblock %}
12 16
{% endif %}
src/authentic2/manager/user_views.py
19 19
import collections
20 20
import operator
21 21

  
22
from django.conf import settings
22 23
from django.db import models
23 24
from django.utils.translation import ugettext_lazy as _, ugettext
24 25
from django.utils.html import format_html
......
558 559
            manageable_ids = map(str, qs2.values_list('pk', flat=True))
559 560
            qs = qs.extra(select={'has_perm': 'a2_rbac_role.id in (%s)' % ', '.join(manageable_ids)})
560 561
            qs = qs.exclude(slug__startswith='_a2-managers-of-role')
561
            return qs
562 562
        else:
563
            return self.object.roles_and_parents()
563
            qs = self.object.roles_and_parents()
564
        qs = self.flag_ldap_roles(qs)
565
        return qs
566

  
567
    def flag_ldap_roles(self, qs):
568
        mappings = getattr(settings, 'LDAP_AUTH_SETTINGS', [{}])[0].get('group_to_role_mapping', [])
569
        if mappings:
570
            role_names = [role for mapping in mappings for role in mapping[1]]
571
            roles = qs.filter(name__in=role_names)
572
            role_ids = map(str, roles.values_list('pk', flat=True))
573
            qs = qs.extra(select={'from_ldap': 'a2_rbac_role.id in (%s)' % ', '.join(role_ids)})
574
        return qs
564 575

  
565 576
    def get_table_data(self):
566 577
        qs = super(UserRolesView, self).get_table_data()
src/authentic2/manager/views.py
17 17
import json
18 18
import inspect
19 19

  
20
from django.conf import settings
21
from django.contrib import messages
20 22
from django.core.exceptions import PermissionDenied, ValidationError
21 23
from django.db import transaction
22 24
from django.views.generic.base import ContextMixin
......
38 40

  
39 41
from gadjo.templatetags.gadjo import xstatic
40 42

  
41
from django_rbac.utils import get_ou_model
43
from django_rbac.utils import get_ou_model, get_role_model
42 44

  
43 45
from authentic2.data_transfer import export_site, import_site, ImportContext
44 46
from authentic2.forms.profile import modelform_factory
......
120 122
                    perm = '%s.%s_%s' % (app_label, permission, model_name)
121 123
                    setattr(self, 'can_' + permission,
122 124
                            request.user.has_perm(perm, self.object))
125
                if self.can_manage_members:
126
                    self.check_for_ldap_sync()
123 127
                if self.permissions \
124 128
                        and not request.user.has_perms(
125 129
                            self.permissions, self.object):
......
134 138
                if not self.permissions_global and not request.user.has_perm_any(self.permissions):
135 139
                    raise PermissionDenied
136 140

  
141
    def check_for_ldap_sync(self):
142
        if not isinstance(self.object, get_role_model()):
143
            return
144
        mappings = getattr(settings, 'LDAP_AUTH_SETTINGS', [{}])[0].get('group_to_role_mapping', [])
145
        if mappings and any(role for mapping in mappings for role in mapping[1]
146
                            if role == self.object.name):
147
            messages.info(self.request,
148
                          'This role is synchronised from LDAP, changing members is not allowed.')
149
            self.can_manage_members = False
150

  
151

  
137 152
    def dispatch(self, request, *args, **kwargs):
138 153
        response = self.authorize(request, *args, **kwargs)
139 154
        if response is not None:
tests/test_ldap.py
348 348
    assert response.context['user'].roles.count() == 1
349 349

  
350 350

  
351
def test_group_to_role_mapping_modify_disabled(slapd, settings, db, app, admin, simple_user):
352
    from authentic2.a2_rbac.models import Role
353

  
354
    role = Role.objects.get_or_create(name='Role2', ou=simple_user.ou)[0]
355
    simple_user.roles.add(role)
356
    settings.LDAP_AUTH_SETTINGS = [{
357
        'url': [slapd.ldap_url],
358
        'basedn': u'o=ôrga',
359
        'use_tls': False,
360
        'group_to_role_mapping': [
361
            ['cn=group2,o=ôrga', ['Role2']],
362
        ],
363
    }]
364
    utils.login(app, admin, '/manage/')
365

  
366
    response = app.get('/manage/users/%s/roles/?search-ou=all' % simple_user.pk)
367
    q = response.pyquery.remove_namespaces()
368
    assert q('table tbody td.name').text() == 'Role2'
369
    assert q('table tbody td .disabled')
370

  
371
    response = app.get('/manage/users/%s/roles/?search-ou=%s' % (simple_user.pk, simple_user.ou.pk))
372
    q = response.pyquery.remove_namespaces()
373
    assert q('table tbody td.name').text() == 'Role2'
374
    assert q('table tbody td.member input').attr('disabled')
375

  
376
    response = app.get('/manage/roles/%s/' % (role.pk))
377
    q = response.pyquery.remove_namespaces()
378
    assert not q('form.manager-m2m-add-form')
379
    assert q('div.role-inheritance .role-add.disabled')
380
    assert not q('table tbody td a.icon-remove-sign js-remove-object')
381

  
382

  
351 383
def test_group_su(slapd, settings, client, db):
352 384
    from django.contrib.auth.models import Group
353 385

  
354
-