0004-manager-display-inheritance-info-excerpt-in-table-on.patch
src/authentic2/manager/role_views.py | ||
---|---|---|
242 | 242 | |
243 | 243 |
def get_context_data(self, **kwargs): |
244 | 244 |
ctx = super().get_context_data(**kwargs) |
245 |
ctx['children'] = views.filter_view( |
|
246 |
self.request, self.object.children(include_self=False, annotate=True) |
|
245 |
ctx['children'] = list( |
|
246 |
views.filter_view( |
|
247 |
self.request, |
|
248 |
self.object.children(include_self=False, annotate=True).order_by( |
|
249 |
F('ou').asc(nulls_first=True), 'name' |
|
250 |
), |
|
251 |
)[:11] |
|
247 | 252 |
) |
248 |
ctx['parents'] = views.filter_view( |
|
249 |
self.request, |
|
250 |
self.object.parents(include_self=False, annotate=True).order_by( |
|
251 |
F('ou').asc(nulls_first=True), 'name' |
|
252 |
), |
|
253 |
ctx['parents'] = list( |
|
254 |
views.filter_view( |
|
255 |
self.request, |
|
256 |
self.object.parents(include_self=False, annotate=True).order_by( |
|
257 |
F('ou').asc(nulls_first=True), 'name' |
|
258 |
), |
|
259 |
)[:11] |
|
253 | 260 |
) |
254 | 261 |
ctx['has_multiple_ou'] = OU.objects.count() > 1 |
255 | 262 |
ctx['admin_roles'] = views.filter_view( |
src/authentic2/manager/static/authentic2/manager/css/style.css | ||
---|---|---|
273 | 273 |
table.main.plaintable tr:hover td { |
274 | 274 |
cursor: default; |
275 | 275 |
} |
276 | ||
277 |
a.role-inheritance-view-all { |
|
278 |
font-style: italic; |
|
279 |
} |
src/authentic2/manager/templates/authentic2/manager/role_members.html | ||
---|---|---|
71 | 71 |
<button>{% trans "Add" %}</button> |
72 | 72 |
</form> |
73 | 73 |
{% endif %} |
74 | ||
75 |
<div class="section"> |
|
76 |
<h3>{% trans "Child roles" %} |
|
77 |
{% if view.can_manage_members %} |
|
78 |
<a href="{% url "a2-manager-role-children" pk=object.pk %}" class="button">{% trans "Edit" %}</a> |
|
79 |
{% else %} |
|
80 |
<a title="{% trans "Permission denied" %}" class="button disabled">{% trans "Edit" %}</a> |
|
81 |
{% endif %} |
|
82 |
</h3> |
|
83 |
<div> |
|
84 |
{% if children %} |
|
85 |
<ul class="objects-list single-links"> |
|
86 |
{% for child in children|slice:":10" %} |
|
87 |
<li> |
|
88 |
<a class="role-inheritance-child" href="{% url "a2-manager-role-members" pk=child.pk %}">{% if child.ou and has_multiple_ou %}{{ child.ou }} - {% endif %}{{ child }}</a> |
|
89 |
{% if not child.direct %} |
|
90 |
<span class="badge">{% trans "Indirect" %}</span> |
|
91 |
{% endif %} |
|
92 |
</li> |
|
93 |
{% endfor %} |
|
94 |
{% if children|length > 10 %} |
|
95 |
<li><a class="role-inheritance-view-all" href="{% url "a2-manager-role-children" pk=object.pk %}">({% trans "view all roles" %})</a></li> |
|
96 |
{% endif %} |
|
97 |
</ul> |
|
98 |
{% else %} |
|
99 |
<p>{% trans "This role has no children." %}</p> |
|
100 |
{% endif %} |
|
101 |
</div> |
|
102 |
</div> |
|
103 |
<div class="section"> |
|
104 |
<h3>{% trans "Parent roles" %} |
|
105 |
{% if not object.is_internal %} |
|
106 |
<a href="{% url "a2-manager-role-parents" pk=object.pk %}" class="button">{% trans "Edit" %}</a> |
|
107 |
{% else %} |
|
108 |
<a title="{% trans "This role is technical, you cannot modify its permissions." %}" class="button disabled">{% trans "Edit" %}</a> |
|
109 |
{% endif %} |
|
110 |
</h3> |
|
111 |
<div> |
|
112 |
{% if parents %} |
|
113 |
<ul class="objects-list single-links"> |
|
114 |
{% for parent in parents|slice:":10" %} |
|
115 |
<li> |
|
116 |
<a class="role-inheritance-parent" href="{% url "a2-manager-role-members" pk=parent.pk %}">{% if parent.ou and has_multiple_ou %}{{ parent.ou }} - {% endif %}{{ parent }}</a> |
|
117 |
{% if not parent.direct %} |
|
118 |
<span class="badge">{% trans "Indirect" %}</span> |
|
119 |
{% endif %} |
|
120 |
</li> |
|
121 |
{% endfor %} |
|
122 |
{% if parents|length > 10 %} |
|
123 |
<li><a class="role-inheritance-view-all" href="{% url "a2-manager-role-parents" pk=object.pk %}">({% trans "view all roles" %})</a></li> |
|
124 |
{% endif %} |
|
125 |
</ul> |
|
126 |
{% else %} |
|
127 |
<p>{% trans "This role has no parents." %}</p> |
|
128 |
{% endif %} |
|
129 |
</div> |
|
130 |
</div> |
|
131 | ||
74 | 132 |
<fieldset class="gadjo-foldable gadjo-folded" id="other-properties"> |
75 | 133 |
<legend class="gadjo-foldable-widget">{% trans "Advanced parameters" %}</legend> |
76 | 134 |
<div class="role-inheritance gadjo-folding"> |
... | ... | |
105 | 163 |
<a title="{% trans "Permission denied" %}" class="disabled role-add icon-add-sign"></a> |
106 | 164 |
{% endif %} |
107 | 165 |
</div> |
108 |
<div class="role-inheritance gadjo-folding"> |
|
109 |
{% trans "Child roles:" %} |
|
110 |
{% for child in children %} |
|
111 |
<a href="{% url "a2-manager-role-members" pk=child.pk %}">{{ child }}</a> |
|
112 |
{% if not forloop.last %} − {% endif %} |
|
113 |
{% endfor %} |
|
114 |
{% if view.can_manage_members %} |
|
115 |
<a href="{% url "a2-manager-role-children" pk=object.pk %}" class="role-add icon-add-sign"></a> |
|
116 |
{% else %} |
|
117 |
<a title="{% trans "Permission denied" %}" class="disabled role-add icon-add-sign"></a> |
|
118 |
{% endif %} |
|
119 |
</div> |
|
120 |
<div class="role-inheritance gadjo-folding"> |
|
121 |
{% trans "Parent roles:" %} |
|
122 |
{% for parent in parents %} |
|
123 |
<a class="role" href="{% url "a2-manager-role-members" pk=parent.pk %}"> |
|
124 |
{% if parent.ou and has_multiple_ou %}{{ parent.ou }} - {% endif %}{{ parent }} |
|
125 |
</a> |
|
126 |
{% if not forloop.last %} − {% endif %} |
|
127 |
{% endfor %} |
|
128 |
{% if not object.is_internal %} |
|
129 |
<a href="{% url "a2-manager-role-parents" pk=object.pk %}" class="role-add icon-add-sign"></a> |
|
130 |
{% else %} |
|
131 |
<a title="{% trans "This role is technical, you cannot modify its permissions." %}" class="disabled role-add icon-add-sign"></a> |
|
132 |
{% endif %} |
|
133 |
</div> |
|
134 | 166 |
</fieldset> |
135 | 167 |
{% endblock %} |
tests/test_ldap.py | ||
---|---|---|
652 | 652 |
assert 'synchronised from LDAP' in response.text |
653 | 653 |
q = response.pyquery.remove_namespaces() |
654 | 654 |
assert not q('form.manager-m2m-add-form') |
655 |
assert q('div.role-inheritance .role-add.disabled')
|
|
655 |
assert q('div.section h3 a.button.disabled')
|
|
656 | 656 |
assert not q('table tbody td a.icon-remove-sign js-remove-object') |
657 | 657 | |
658 | 658 |
tests/test_manager.py | ||
---|---|---|
1161 | 1161 |
assert {e.text_content() for e in q('table tbody td.name')} == {simple_role.name} |
1162 | 1162 | |
1163 | 1163 | |
1164 |
def test_display_parent_roles_on_role_page(app, superuser, settings): |
|
1165 |
ou1 = get_default_ou() |
|
1166 |
ou1.name = 'ou1' |
|
1167 |
ou1.save() |
|
1168 | ||
1169 |
child = Role.objects.create(name='child', slug='role', ou=ou1) |
|
1170 |
parent1 = Role.objects.create(name='parent1', slug='role1', ou=None) |
|
1171 |
parent2 = Role.objects.create(name='parent2', slug='role2', ou=ou1) |
|
1172 |
child.add_parent(parent1) |
|
1173 |
child.add_parent(parent2) |
|
1174 |
child.save() |
|
1175 | ||
1176 |
# do not display roles if we have a single OU |
|
1177 |
url = reverse('a2-manager-role-members', kwargs={'pk': child.pk}) |
|
1178 |
login(app, superuser) |
|
1179 |
response = app.get(url, status=200) |
|
1180 |
parent_roles_html = response.html.find_all('div', {'class': 'role-inheritance'})[3] |
|
1181 |
assert 'Parent roles:' in parent_roles_html.text |
|
1182 |
assert [x.text.strip() for x in parent_roles_html.find_all('a', {'class': 'role'})] == [ |
|
1183 |
'parent1', |
|
1184 |
'parent2', |
|
1185 |
] |
|
1186 | ||
1187 |
# display parent roles if we have multiple OUs |
|
1188 |
ou2 = OU.objects.create(name='ou2') |
|
1189 |
response = app.get(url, status=200) |
|
1190 |
parent_roles_html = response.html.find_all('div', {'class': 'role-inheritance'})[3] |
|
1191 |
assert 'Parent roles:' in parent_roles_html.text |
|
1192 |
assert [x.text.strip() for x in parent_roles_html.find_all('a', {'class': 'role'})] == [ |
|
1193 |
'parent1', |
|
1194 |
'ou1 - parent2', |
|
1195 |
] |
|
1196 | ||
1197 |
# display parent roles sorted by OU |
|
1198 |
parent3 = Role.objects.create(name='parent3', slug='role3', ou=ou2) |
|
1199 |
child.add_parent(parent3) |
|
1200 |
parent4 = Role.objects.create(name='parent4', slug='role4', ou=ou1) |
|
1201 |
child.add_parent(parent4) |
|
1202 |
child.save() |
|
1203 |
response = app.get(url, status=200) |
|
1204 |
parent_roles_html = response.html.find_all('div', {'class': 'role-inheritance'})[3] |
|
1205 |
assert 'Parent roles:' in parent_roles_html.text |
|
1206 |
assert [x.text.strip() for x in parent_roles_html.find_all('a', {'class': 'role'})] == [ |
|
1207 |
'parent1', |
|
1208 |
'ou1 - parent2', |
|
1209 |
'ou1 - parent4', |
|
1210 |
'ou2 - parent3', |
|
1211 |
] |
|
1212 | ||
1213 | ||
1214 | 1164 |
def test_manager_service_search(app, admin, ou1): |
1215 | 1165 |
Service.objects.create(ou=ou1, slug='test', name='Test Service') |
1216 | 1166 |
Service.objects.create(ou=get_default_ou(), slug='example', name='Example Service') |
tests/test_role_manager.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import json |
18 | 18 | |
19 |
import pytest |
|
20 |
from django.urls import reverse |
|
19 | 21 |
from django.utils.encoding import force_bytes, force_text |
20 | 22 |
from webtest import Upload |
21 | 23 | |
22 | 24 |
from authentic2.a2_rbac.models import OrganizationalUnit, Role |
23 | 25 |
from authentic2.a2_rbac.utils import get_default_ou |
24 | 26 |
from authentic2.custom_user.models import User |
27 |
from django_rbac.utils import get_ou_model, get_role_model |
|
25 | 28 | |
26 | 29 |
from .utils import login, text_content |
27 | 30 | |
31 |
OU = get_ou_model() |
|
32 |
Role = get_role_model() |
|
33 | ||
28 | 34 | |
29 | 35 |
def test_manager_role_export(app, admin, ou1, role_ou1, ou2, role_ou2): |
30 | 36 |
import csv |
... | ... | |
336 | 342 |
resp = app.get('/manage/roles/csv-import/') |
337 | 343 |
resp = resp.click('Download sample') |
338 | 344 |
assert 'name,slug,ou' in resp.text |
345 | ||
346 | ||
347 |
@pytest.mark.parametrize('relation', ['child', 'parent']) |
|
348 |
def test_role_members_display_inheritance_info(app, superuser, settings, simple_role, relation): |
|
349 |
url = reverse('a2-manager-role-members', kwargs={'pk': simple_role.pk}) |
|
350 | ||
351 |
resp = login(app, superuser, url) |
|
352 |
assert 'This role has no children.' in resp.text |
|
353 |
assert 'This role has no parents.' in resp.text |
|
354 | ||
355 |
for i in range(10): |
|
356 |
role = Role.objects.create(name=f'Role {i}', ou=get_default_ou()) |
|
357 |
getattr(simple_role, 'add_%s' % relation)(role) |
|
358 | ||
359 |
resp = app.get(url) |
|
360 |
if relation == 'child': |
|
361 |
assert 'This role has no children.' not in resp.text |
|
362 |
assert 'This role has no parents.' in resp.text |
|
363 |
elif relation == 'parent': |
|
364 |
assert 'This role has no children.' in resp.text |
|
365 |
assert 'This role has no parents.' not in resp.text |
|
366 | ||
367 |
for i, el in enumerate(resp.pyquery.find('a.role-inheritance-%s' % relation)): |
|
368 |
assert el.text == f'Role {i}' |
|
369 |
assert '(view all roles)' not in resp.text |
|
370 | ||
371 |
role = Role.objects.create(name=f'Role a', ou=get_default_ou()) |
|
372 |
getattr(simple_role, 'add_%s' % relation)(role) |
|
373 |
resp = app.get(url) |
|
374 |
assert 'Role a' not in resp.text |
|
375 |
assert '(view all roles)' in resp.text |
|
376 | ||
377 |
resp = resp.click('(view all roles)', href=relation) |
|
378 |
assert 'Role a' in resp.text |
|
379 | ||
380 |
# display OU if there are more than one |
|
381 |
ou1 = OU.objects.create(name='ou1') |
|
382 |
resp = app.get(url) |
|
383 |
for i, el in enumerate(resp.pyquery.find('a.role-inheritance-%s' % relation)): |
|
384 |
assert el.text == f'Default organizational unit - Role {i}' |
|
385 | ||
386 |
# sort by OU, then name |
|
387 |
Role.objects.filter(name__in=['Role 2', 'Role 3', 'Role 4']).update(ou=ou1) |
|
388 |
Role.objects.filter(name__in=['Role 5', 'Role 6']).update(ou=None) |
|
389 | ||
390 |
resp = app.get(url) |
|
391 |
assert [el.text for el in resp.pyquery.find('a.role-inheritance-%s' % relation)] == [ |
|
392 |
'Role 5', |
|
393 |
'Role 6', |
|
394 |
'Default organizational unit - Role 0', |
|
395 |
'Default organizational unit - Role 1', |
|
396 |
'Default organizational unit - Role 7', |
|
397 |
'Default organizational unit - Role 8', |
|
398 |
'Default organizational unit - Role 9', |
|
399 |
'Default organizational unit - Role a', |
|
400 |
'ou1 - Role 2', |
|
401 |
'ou1 - Role 3', |
|
402 |
] |
|
339 |
- |