manager: forbid changing role members when synced from ldap (#37187)

This commit is contained in:
Valentin Deniaud 2020-05-26 17:53:35 +02:00
parent ffb04c1ab3
commit 0d8ea42ad2
8 changed files with 87 additions and 6 deletions

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-05-27 13:22
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0022_auto_20200402_1101'),
]
operations = [
migrations.AddField(
model_name='role',
name='can_manage_members',
field=models.BooleanField(default=True, verbose_name='Allow adding or deleting role members'),
),
]

View File

@ -227,6 +227,10 @@ class Role(RoleAbstractBase):
content_type_field='target_ct',
object_id_field='target_id')
can_manage_members = models.BooleanField(
default=True,
verbose_name=_('Allow adding or deleting role members'))
def get_admin_role(self, create=True):
from . import utils

View File

@ -783,6 +783,10 @@ class LDAPBackend(object):
# Remove extra roles
elif dn not in role_dns and role in roles:
user.roles.remove(role)
if role.can_manage_members:
log.info('role %s is now only manageable through LDAP', role)
role.can_manage_members = False
role.save()
def get_ldap_group_dns(self, user, dn, conn, block, attributes):
'''Retrieve group DNs from the LDAP by attributes (memberOf) or by

View File

@ -149,6 +149,14 @@ class RoleMembersView(views.HideOUColumnMixin, RoleViewMixin, views.BaseSubTable
def title(self):
return self.get_instance_name()
@property
def can_manage_members(self):
return self.object.can_manage_members and getattr(self, '_can_manage_members', False)
@can_manage_members.setter
def can_manage_members(self, value):
self._can_manage_members = value
def get_table_queryset(self):
return self.object.all_members()
@ -189,6 +197,7 @@ class RoleMembersView(views.HideOUColumnMixin, RoleViewMixin, views.BaseSubTable
ctx['admin_roles'] = views.filter_view(self.request,
self.object.get_admin_role().children(include_self=False,
annotate=True))
ctx['from_ldap'] = self._can_manage_members and not self.can_manage_members
return ctx
members = RoleMembersView.as_view()

View File

@ -122,7 +122,9 @@ class OuUserRolesTable(tables.Table):
'indeterminate{%% endif %%}"'
' name="role-{{ record.pk }}" type="checkbox" {%% if record.member %%}checked{%% endif %%} '
'{%% if not record.has_perm %%}disabled '
'title="{%% trans "%s" %%}"{%% endif %%}/>' % ugettext_noop('You are not authorized to manage this role'),
'title="{%% trans "%s" %%}"{%% endif %%} '
'{%% if not record.can_manage_members %%}disabled '
'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.')),
verbose_name=_('Member'),
order_by=('member', 'via', 'name'))

View File

@ -46,6 +46,11 @@
{% endblock %}
{% block main %}
{% if from_ldap %}
<div class="infonotice">
{% trans "This role is synchronised from LDAP, changing members is not allowed." %}
</div>
{% endif %}
{% with row_link=1 url_name="a2-manager-user-detail" %}
{% render_table table "authentic2/manager/role_members_table.html" %}
{% endwith %}

View File

@ -7,6 +7,10 @@
<th></th>
{% endblock %}
{% block table.tbody.last.column %}
<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>
<td>
{% if table.context.view.can_change and row.record.member %}
<a class="icon-remove-sign {% if not row.record.allow_manage_members %} 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 not row.record.allow_manage_members %}{% trans "This role is synchronised from LDAP, changing members is not allowed." %}{% endif %}"></a>
{% endif %}
</td>
{% endblock %}
{% endif %}

View File

@ -34,6 +34,7 @@ from django.utils import timezone
from django.utils.six.moves.urllib import parse as urlparse
from authentic2.models import Service
from authentic2.a2_rbac.models import Role
from authentic2.a2_rbac.utils import get_default_ou
from django_rbac.utils import get_ou_model
from authentic2.backends import ldap_backend
@ -310,8 +311,6 @@ def test_posix_group_mapping(slapd, settings, client, db):
def test_group_to_role_mapping(slapd, settings, client, db):
from authentic2.a2_rbac.models import Role
Role.objects.get_or_create(name='Role1')
settings.LDAP_AUTH_SETTINGS = [{
'url': [slapd.ldap_url],
@ -329,8 +328,6 @@ def test_group_to_role_mapping(slapd, settings, client, db):
def test_posix_group_to_role_mapping(slapd, settings, client, db):
from authentic2.a2_rbac.models import Role
Role.objects.get_or_create(name='Role2')
settings.LDAP_AUTH_SETTINGS = [{
'url': [slapd.ldap_url],
@ -348,6 +345,42 @@ def test_posix_group_to_role_mapping(slapd, settings, client, db):
assert response.context['user'].roles.count() == 1
def test_group_to_role_mapping_modify_disabled(slapd, settings, db, app, admin, client):
role = Role.objects.create(name='Role3')
settings.LDAP_AUTH_SETTINGS = [{
'url': [slapd.ldap_url],
'basedn': u'o=ôrga',
'use_tls': False,
'group_to_role_mapping': [
['cn=group1,o=ôrga', ['Role3']],
],
}]
response = client.post('/login/', {'login-password-submit': '1',
'username': USERNAME,
'password': PASS}, follow=True)
user = response.context['user']
assert user.roles.count() == 1
utils.login(app, admin, '/manage/')
response = app.get('/manage/users/%s/roles/?search-ou=%s' % (user.pk, user.ou.pk))
q = response.pyquery.remove_namespaces()
assert q('table tbody td.name').text() == 'Role3'
assert q('table tbody td.member input').attr('disabled')
response = app.get('/manage/users/%s/roles/?search-ou=all' % user.pk)
q = response.pyquery.remove_namespaces()
assert q('table tbody td.name').text() == 'Role3'
assert q('table tbody td.member input').attr('disabled')
response = app.get('/manage/roles/%s/' % (role.pk))
assert 'synchronised from LDAP' in response.text
q = response.pyquery.remove_namespaces()
assert not q('form.manager-m2m-add-form')
assert q('div.role-inheritance .role-add.disabled')
assert not q('table tbody td a.icon-remove-sign js-remove-object')
def test_group_su(slapd, settings, client, db):
from django.contrib.auth.models import Group