0001-manager-forbid-changing-role-members-when-synced-fro.patch
src/authentic2/a2_rbac/migrations/0023_role_manage_members_allowed.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2020-05-27 13:22 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('a2_rbac', '0022_auto_20200402_1101'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.AddField( |
|
16 |
model_name='role', |
|
17 |
name='manage_members_allowed', |
|
18 |
field=models.BooleanField(default=True, verbose_name='Allow adding or deleting role members'), |
|
19 |
), |
|
20 |
] |
src/authentic2/a2_rbac/models.py | ||
---|---|---|
227 | 227 |
content_type_field='target_ct', |
228 | 228 |
object_id_field='target_id') |
229 | 229 | |
230 |
manage_members_allowed = models.BooleanField( |
|
231 |
default=True, |
|
232 |
verbose_name=_('Allow adding or deleting role members')) |
|
233 | ||
230 | 234 |
def get_admin_role(self, create=True): |
231 | 235 |
from . import utils |
232 | 236 |
src/authentic2/backends/ldap_backend.py | ||
---|---|---|
783 | 783 |
# Remove extra roles |
784 | 784 |
elif dn not in role_dns and role in roles: |
785 | 785 |
user.roles.remove(role) |
786 |
if role.manage_members_allowed: |
|
787 |
log.info('role %s is now only manageable through LDAP', role) |
|
788 |
role.manage_members_allowed = False |
|
789 |
role.save() |
|
786 | 790 | |
787 | 791 |
def get_ldap_group_dns(self, user, dn, conn, block, attributes): |
788 | 792 |
'''Retrieve group DNs from the LDAP by attributes (memberOf) or by |
src/authentic2/manager/role_views.py | ||
---|---|---|
149 | 149 |
def title(self): |
150 | 150 |
return self.get_instance_name() |
151 | 151 | |
152 |
@property |
|
153 |
def can_manage_members(self): |
|
154 |
return self.object.manage_members_allowed and getattr(self, '_can_manage_members', False) |
|
155 | ||
156 |
@can_manage_members.setter |
|
157 |
def can_manage_members(self, value): |
|
158 |
self._can_manage_members = value |
|
159 | ||
152 | 160 |
def get_table_queryset(self): |
153 | 161 |
return self.object.all_members() |
154 | 162 | |
... | ... | |
189 | 197 |
ctx['admin_roles'] = views.filter_view(self.request, |
190 | 198 |
self.object.get_admin_role().children(include_self=False, |
191 | 199 |
annotate=True)) |
200 |
ctx['from_ldap'] = self._can_manage_members and not self.can_manage_members |
|
192 | 201 |
return ctx |
193 | 202 | |
194 | 203 |
members = RoleMembersView.as_view() |
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 not record.manage_members_allowed %%}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/role_members.html | ||
---|---|---|
46 | 46 |
{% endblock %} |
47 | 47 | |
48 | 48 |
{% block main %} |
49 |
{% if from_ldap %} |
|
50 |
<div class="infonotice"> |
|
51 |
{% trans "This role is synchronised from LDAP, changing members is not allowed." %} |
|
52 |
</div> |
|
53 |
{% endif %} |
|
49 | 54 |
{% with row_link=1 url_name="a2-manager-user-detail" %} |
50 | 55 |
{% render_table table "authentic2/manager/role_members_table.html" %} |
51 | 56 |
{% endwith %} |
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 "{{ name }}" from user "{{ username }}" ?{% 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 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 "{{ name }}" from user "{{ username }}" ?{% 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> |
|
13 |
{% endif %} |
|
14 |
</td> |
|
11 | 15 |
{% endblock %} |
12 | 16 |
{% endif %} |
tests/test_ldap.py | ||
---|---|---|
34 | 34 |
from django.utils.six.moves.urllib import parse as urlparse |
35 | 35 | |
36 | 36 |
from authentic2.models import Service |
37 |
from authentic2.a2_rbac.models import Role |
|
37 | 38 |
from authentic2.a2_rbac.utils import get_default_ou |
38 | 39 |
from django_rbac.utils import get_ou_model |
39 | 40 |
from authentic2.backends import ldap_backend |
... | ... | |
310 | 311 | |
311 | 312 | |
312 | 313 |
def test_group_to_role_mapping(slapd, settings, client, db): |
313 |
from authentic2.a2_rbac.models import Role |
|
314 | ||
315 | 314 |
Role.objects.get_or_create(name='Role1') |
316 | 315 |
settings.LDAP_AUTH_SETTINGS = [{ |
317 | 316 |
'url': [slapd.ldap_url], |
... | ... | |
329 | 328 | |
330 | 329 | |
331 | 330 |
def test_posix_group_to_role_mapping(slapd, settings, client, db): |
332 |
from authentic2.a2_rbac.models import Role |
|
333 | ||
334 | 331 |
Role.objects.get_or_create(name='Role2') |
335 | 332 |
settings.LDAP_AUTH_SETTINGS = [{ |
336 | 333 |
'url': [slapd.ldap_url], |
... | ... | |
348 | 345 |
assert response.context['user'].roles.count() == 1 |
349 | 346 | |
350 | 347 | |
348 |
def test_group_to_role_mapping_modify_disabled(slapd, settings, db, app, admin, client): |
|
349 |
role = Role.objects.create(name='Role3') |
|
350 |
settings.LDAP_AUTH_SETTINGS = [{ |
|
351 |
'url': [slapd.ldap_url], |
|
352 |
'basedn': u'o=ôrga', |
|
353 |
'use_tls': False, |
|
354 |
'group_to_role_mapping': [ |
|
355 |
['cn=group1,o=ôrga', ['Role3']], |
|
356 |
], |
|
357 |
}] |
|
358 |
response = client.post('/login/', {'login-password-submit': '1', |
|
359 |
'username': USERNAME, |
|
360 |
'password': PASS}, follow=True) |
|
361 |
user = response.context['user'] |
|
362 |
assert user.roles.count() == 1 |
|
363 | ||
364 |
utils.login(app, admin, '/manage/') |
|
365 | ||
366 |
response = app.get('/manage/users/%s/roles/?search-ou=%s' % (user.pk, user.ou.pk)) |
|
367 |
q = response.pyquery.remove_namespaces() |
|
368 |
assert q('table tbody td.name').text() == 'Role3' |
|
369 |
assert q('table tbody td.member input').attr('disabled') |
|
370 | ||
371 |
response = app.get('/manage/users/%s/roles/?search-ou=all' % user.pk) |
|
372 |
q = response.pyquery.remove_namespaces() |
|
373 |
assert q('table tbody td.name').text() == 'Role3' |
|
374 |
assert q('table tbody td.member input').attr('disabled') |
|
375 | ||
376 |
response = app.get('/manage/roles/%s/' % (role.pk)) |
|
377 |
assert 'synchronised from LDAP' in response.text |
|
378 |
q = response.pyquery.remove_namespaces() |
|
379 |
assert not q('form.manager-m2m-add-form') |
|
380 |
assert q('div.role-inheritance .role-add.disabled') |
|
381 |
assert not q('table tbody td a.icon-remove-sign js-remove-object') |
|
382 | ||
383 | ||
351 | 384 |
def test_group_su(slapd, settings, client, db): |
352 | 385 |
from django.contrib.auth.models import Group |
353 | 386 | |
354 |
- |