Projet

Général

Profil

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

Valentin Deniaud, 10 décembre 2019 16:13

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
......
557 558
            manageable_ids = map(str, qs2.values_list('pk', flat=True))
558 559
            qs = qs.extra(select={'has_perm': 'a2_rbac_role.id in (%s)' % ', '.join(manageable_ids)})
559 560
            qs = qs.exclude(slug__startswith='_a2-managers-of-role')
560
            return qs
561 561
        else:
562
            return self.object.roles_and_parents()
562
            qs = self.object.roles_and_parents()
563
        qs = self.flag_ldap_roles(qs)
564
        return qs
565

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

  
564 575
    def get_table_data(self):
565 576
        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
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, DataImportError, 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
345 345
    assert response.context['user'].roles.count() == 1
346 346

  
347 347

  
348
def test_group_to_role_mapping_modify_disabled(slapd, settings, db, app, admin, simple_user):
349
    from authentic2.a2_rbac.models import Role
350

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

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

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

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

  
379

  
348 380
def test_group_su(slapd, settings, client, db):
349 381
    from django.contrib.auth.models import Group
350 382

  
351
-