0003-manager-controle-role-inheritance-using-table-53481.patch
src/authentic2/manager/forms.py | ||
---|---|---|
133 | 133 | |
134 | 134 | |
135 | 135 |
class RolesForm(LimitQuerysetFormMixin, CssClass, forms.Form): |
136 | ||
137 | 136 |
roles = fields.ChooseRolesField(label=_('Add some roles')) |
138 | 137 | |
139 | 138 | |
140 |
class RoleParentsForm(LimitQuerysetFormMixin, CssClass, forms.Form): |
|
141 |
roles = fields.ChooseManageableMemberRolesField(label=_('Add some roles')) |
|
139 |
class RoleParentForm(LimitQuerysetFormMixin, CssClass, forms.Form): |
|
140 |
role = fields.ChooseManageableMemberRoleField(label=_('Add some roles')) |
|
141 |
action = forms.CharField(initial='add', widget=forms.HiddenInput) |
|
142 | 142 | |
143 | 143 | |
144 | 144 |
class ChooseUserRoleForm(LimitQuerysetFormMixin, CssClass, forms.Form): |
src/authentic2/manager/role_views.py | ||
---|---|---|
21 | 21 |
from django.contrib.contenttypes.models import ContentType |
22 | 22 |
from django.core.exceptions import PermissionDenied, ValidationError |
23 | 23 |
from django.db import transaction |
24 |
from django.db.models import Count, F |
|
24 |
from django.db.models import BooleanField, Count, ExpressionWrapper, F, Prefetch |
|
25 |
from django.db.models.functions import Cast |
|
25 | 26 |
from django.db.models.query import Prefetch, Q |
26 | 27 |
from django.shortcuts import get_object_or_404 |
27 | 28 |
from django.urls import reverse |
... | ... | |
34 | 35 |
from authentic2.apps.journal.views import JournalViewWithContext |
35 | 36 |
from authentic2.forms.profile import modelform_factory |
36 | 37 |
from authentic2.utils.misc import redirect |
37 |
from django_rbac.utils import get_ou_model, get_permission_model, get_role_model |
|
38 |
from django_rbac.utils import get_ou_model, get_permission_model, get_role_model, get_role_parenting_model
|
|
38 | 39 | |
39 | 40 |
from . import app_settings, forms, resources, tables, views |
40 | 41 |
from .journal_views import BaseJournalView |
... | ... | |
365 | 366 |
members_export = RoleMembersExportView.as_view() |
366 | 367 | |
367 | 368 | |
368 |
class RoleAddChildView( |
|
369 |
views.AjaxFormViewMixin, |
|
370 |
views.TitleMixin, |
|
371 |
views.PermissionMixin, |
|
372 |
views.FormNeedsRequest, |
|
373 |
SingleObjectMixin, |
|
374 |
FormView, |
|
375 |
): |
|
369 |
class RoleAddChildView(RoleViewMixin, views.HideOUColumnMixin, views.BaseSubTableView): |
|
376 | 370 |
title = _('Add child role') |
377 |
model = get_role_model()
|
|
378 |
form_class = forms.RolesForm
|
|
379 |
success_url = '..'
|
|
380 |
template_name = 'authentic2/manager/form.html'
|
|
371 |
form_class = forms.ChooseRoleForm
|
|
372 |
table_class = tables.InheritanceRolesTable
|
|
373 |
search_form_class = forms.RoleSearchForm
|
|
374 |
template_name = 'authentic2/manager/roles_inheritance.html'
|
|
381 | 375 |
permissions = ['a2_rbac.manage_members_role'] |
376 |
success_url = '.' |
|
377 |
slug_field = 'uuid' |
|
382 | 378 | |
383 |
def dispatch(self, request, *args, **kwargs): |
|
384 |
self.object = self.get_object() |
|
385 |
return super().dispatch(request, *args, **kwargs) |
|
379 |
def get_table_queryset(self): |
|
380 |
qs = super().get_table_queryset() |
|
381 |
qs = qs.exclude(pk=self.object.pk) |
|
382 |
children = self.object.children(annotate=True, include_self=False) |
|
383 |
children = children.annotate(is_direct=Cast('direct', output_field=BooleanField())) |
|
384 |
qs = qs.annotate( |
|
385 |
checked=ExpressionWrapper(Q(pk__in=children.filter(is_direct=True)), output_field=BooleanField()) |
|
386 |
) |
|
387 |
qs = qs.annotate( |
|
388 |
indeterminate=ExpressionWrapper( |
|
389 |
Q(pk__in=children.filter(is_direct=False)), output_field=BooleanField() |
|
390 |
) |
|
391 |
) |
|
392 |
RoleParenting = get_role_parenting_model() |
|
393 |
rp_qs = RoleParenting.objects.filter(parent__in=children).annotate(name=F('parent__name')) |
|
394 |
qs = qs.prefetch_related(Prefetch('parent_relation', queryset=rp_qs, to_attr='via')) |
|
395 |
return qs |
|
386 | 396 | |
387 | 397 |
def form_valid(self, form): |
388 |
parent = self.get_object() |
|
389 |
for role in form.cleaned_data['roles']: |
|
390 |
parent.add_child(role) |
|
398 |
role = form.cleaned_data['role'] |
|
399 |
action = form.cleaned_data['action'] |
|
400 |
if action == 'add': |
|
401 |
self.object.add_child(role) |
|
402 |
hooks.call_hooks( |
|
403 |
'event', name='manager-add-child-role', user=self.request.user, parent=self.object, child=role |
|
404 |
) |
|
405 |
self.request.journal.record('manager.role.inheritance.addition', parent=self.object, child=role) |
|
406 |
elif action == 'remove': |
|
407 |
self.object.remove_child(role) |
|
391 | 408 |
hooks.call_hooks( |
392 |
'event', name='manager-add-child-role', user=self.request.user, parent=parent, child=role |
|
409 |
'event', |
|
410 |
name='manager-remove-child-role', |
|
411 |
user=self.request.user, |
|
412 |
parent=self.object, |
|
413 |
child=role, |
|
393 | 414 |
) |
394 |
self.request.journal.record('manager.role.inheritance.addition', parent=parent, child=role)
|
|
415 |
self.request.journal.record('manager.role.inheritance.removal', parent=self.object, child=role)
|
|
395 | 416 |
return super().form_valid(form) |
396 | 417 | |
418 |
def get_search_form_kwargs(self): |
|
419 |
kwargs = super().get_search_form_kwargs() |
|
420 |
kwargs['queryset'] = self.request.user.filter_by_perm( |
|
421 |
'a2_rbac.view_role', get_role_model().objects.all() |
|
422 |
) |
|
423 |
return kwargs |
|
424 | ||
397 | 425 | |
398 | 426 |
add_child = RoleAddChildView.as_view() |
399 | 427 | |
400 | 428 | |
401 |
class RoleAddParentView( |
|
402 |
views.AjaxFormViewMixin, views.TitleMixin, views.FormNeedsRequest, SingleObjectMixin, FormView |
|
403 |
): |
|
429 |
class RoleAddParentView(RoleViewMixin, views.HideOUColumnMixin, views.BaseSubTableView): |
|
404 | 430 |
title = _('Add parent role') |
405 |
model = get_role_model() |
|
406 |
form_class = forms.RoleParentsForm |
|
407 |
success_url = '..' |
|
408 |
template_name = 'authentic2/manager/form.html' |
|
431 |
form_class = forms.RoleParentForm |
|
432 |
table_class = tables.InheritanceRolesTable |
|
433 |
search_form_class = forms.RoleSearchForm |
|
434 |
template_name = 'authentic2/manager/roles_inheritance.html' |
|
435 |
success_url = '.' |
|
436 |
slug_field = 'uuid' |
|
409 | 437 | |
410 | 438 |
def dispatch(self, request, *args, **kwargs): |
411 |
self.object = self.get_object() |
|
412 |
if self.object.is_internal(): |
|
439 |
if self.get_object().is_internal(): |
|
413 | 440 |
raise PermissionDenied |
414 | 441 |
return super().dispatch(request, *args, **kwargs) |
415 | 442 | |
443 |
def get_table_queryset(self): |
|
444 |
qs = super().get_table_queryset() |
|
445 |
qs = self.request.user.filter_by_perm('a2_rbac.manage_members_role', qs) |
|
446 |
qs = qs.exclude(pk=self.object.pk) |
|
447 |
parents = self.object.parents(annotate=True, include_self=False) |
|
448 |
parents = parents.annotate(is_direct=Cast('direct', output_field=BooleanField())) |
|
449 |
qs = qs.annotate( |
|
450 |
checked=ExpressionWrapper(Q(pk__in=parents.filter(is_direct=True)), output_field=BooleanField()) |
|
451 |
) |
|
452 |
qs = qs.annotate( |
|
453 |
indeterminate=ExpressionWrapper( |
|
454 |
Q(pk__in=parents.filter(is_direct=False)), output_field=BooleanField() |
|
455 |
) |
|
456 |
) |
|
457 |
RoleParenting = get_role_parenting_model() |
|
458 |
rp_qs = RoleParenting.objects.filter(child__in=parents).annotate(name=F('child__name')) |
|
459 |
qs = qs.prefetch_related(Prefetch('child_relation', queryset=rp_qs, to_attr='via')) |
|
460 |
return qs |
|
461 | ||
416 | 462 |
def form_valid(self, form): |
417 |
child = self.get_object() |
|
418 |
for role in form.cleaned_data['roles']: |
|
419 |
child.add_parent(role) |
|
463 |
role = form.cleaned_data['role'] |
|
464 |
action = form.cleaned_data['action'] |
|
465 |
if action == 'add': |
|
466 |
self.object.add_parent(role) |
|
420 | 467 |
hooks.call_hooks( |
421 |
'event', name='manager-add-child-role', user=self.request.user, parent=role, child=child |
|
468 |
'event', name='manager-add-child-role', user=self.request.user, parent=role, child=self.object |
|
469 |
) |
|
470 |
self.request.journal.record('manager.role.inheritance.addition', parent=role, child=self.object) |
|
471 |
elif action == 'remove': |
|
472 |
self.object.remove_parent(role) |
|
473 |
hooks.call_hooks( |
|
474 |
'event', |
|
475 |
name='manager-remove-child-role', |
|
476 |
user=self.request.user, |
|
477 |
parent=role, |
|
478 |
child=self.object, |
|
422 | 479 |
) |
423 |
self.request.journal.record('manager.role.inheritance.addition', parent=role, child=child)
|
|
480 |
self.request.journal.record('manager.role.inheritance.removal', parent=role, child=self.object)
|
|
424 | 481 |
return super().form_valid(form) |
425 | 482 | |
426 | ||
427 |
add_parent = RoleAddParentView.as_view() |
|
428 | ||
429 | ||
430 |
class RoleRemoveChildView(views.AjaxFormViewMixin, SingleObjectMixin, views.PermissionMixin, TemplateView): |
|
431 |
title = _('Remove child role') |
|
432 |
model = get_role_model() |
|
433 |
success_url = '../..' |
|
434 |
template_name = 'authentic2/manager/role_remove_child.html' |
|
435 |
permissions = ['a2_rbac.manage_members_role'] |
|
436 | ||
437 |
def dispatch(self, request, *args, **kwargs): |
|
438 |
self.object = self.get_object() |
|
439 |
self.child = self.get_queryset().get(pk=kwargs['child_pk']) |
|
440 |
return super().dispatch(request, *args, **kwargs) |
|
441 | ||
442 |
def get_context_data(self, **kwargs): |
|
443 |
ctx = super().get_context_data(**kwargs) |
|
444 |
ctx['child'] = self.child |
|
445 |
return ctx |
|
446 | ||
447 |
def post(self, request, *args, **kwargs): |
|
448 |
self.object.remove_child(self.child) |
|
449 |
hooks.call_hooks( |
|
450 |
'event', |
|
451 |
name='manager-remove-child-role', |
|
452 |
user=self.request.user, |
|
453 |
parent=self.object, |
|
454 |
child=self.child, |
|
455 |
) |
|
456 |
self.request.journal.record('manager.role.inheritance.removal', parent=self.object, child=self.child) |
|
457 |
return redirect(self.request, self.success_url) |
|
458 | ||
459 | ||
460 |
remove_child = RoleRemoveChildView.as_view() |
|
461 | ||
462 | ||
463 |
class RoleRemoveParentView(views.AjaxFormViewMixin, SingleObjectMixin, TemplateView): |
|
464 |
title = _('Remove parent role') |
|
465 |
model = get_role_model() |
|
466 |
success_url = '../..' |
|
467 |
template_name = 'authentic2/manager/role_remove_parent.html' |
|
468 | ||
469 |
def dispatch(self, request, *args, **kwargs): |
|
470 |
self.object = self.get_object() |
|
471 |
if self.object.is_internal(): |
|
472 |
raise PermissionDenied |
|
473 |
self.parent = self.get_queryset().get(pk=kwargs['parent_pk']) |
|
474 |
return super().dispatch(request, *args, **kwargs) |
|
475 | ||
476 |
def get_context_data(self, **kwargs): |
|
477 |
ctx = super().get_context_data(**kwargs) |
|
478 |
ctx['parent'] = self.parent |
|
479 |
return ctx |
|
480 | ||
481 |
def post(self, request, *args, **kwargs): |
|
482 |
if not self.request.user.has_perm('a2_rbac.manage_members_role', self.parent): |
|
483 |
raise PermissionDenied |
|
484 |
self.object.remove_parent(self.parent) |
|
485 |
hooks.call_hooks( |
|
486 |
'event', |
|
487 |
name='manager-remove-child-role', |
|
488 |
user=self.request.user, |
|
489 |
parent=self.parent, |
|
490 |
child=self.object, |
|
483 |
def get_search_form_kwargs(self): |
|
484 |
kwargs = super().get_search_form_kwargs() |
|
485 |
kwargs['queryset'] = self.request.user.filter_by_perm( |
|
486 |
'a2_rbac.manage_members_role', get_role_model().objects.all() |
|
491 | 487 |
) |
492 |
self.request.journal.record('manager.role.inheritance.removal', parent=self.parent, child=self.object) |
|
493 |
return redirect(self.request, self.success_url) |
|
488 |
return kwargs |
|
494 | 489 | |
495 | 490 | |
496 |
remove_parent = RoleRemoveParentView.as_view()
|
|
491 |
add_parent = RoleAddParentView.as_view()
|
|
497 | 492 | |
498 | 493 | |
499 | 494 |
class RoleAddAdminRoleView( |
src/authentic2/manager/tables.py | ||
---|---|---|
240 | 240 |
attrs = {'class': 'main plaintable', 'id': 'user-authorizations-table'} |
241 | 241 |
fields = ('client', 'created', 'expired') |
242 | 242 |
empty_text = _('This user has not granted profile data access to any service yet.') |
243 | ||
244 | ||
245 |
class InheritanceRolesTable(tables.Table): |
|
246 |
name = tables.LinkColumn( |
|
247 |
viewname='a2-manager-role-members', kwargs={'pk': A('pk')}, accessor='name', verbose_name=_('label') |
|
248 |
) |
|
249 |
via = tables.TemplateColumn( |
|
250 |
'''{% for rel in record.via %}{{ rel.name }}{% if not forloop.last %}, {% endif %}{% endfor %}''', |
|
251 |
verbose_name=_('Inherited from'), |
|
252 |
orderable=False, |
|
253 |
) |
|
254 |
member = tables.TemplateColumn( |
|
255 |
'<input class="role-member{% if record.indeterminate %} indeterminate{% endif %}" name="role-{{ record.pk }}" type="checkbox" {% if record.checked %}checked{% endif %}/>', |
|
256 |
verbose_name='', |
|
257 |
) |
|
258 | ||
259 |
class Meta: |
|
260 |
model = get_role_model() |
|
261 |
attrs = {'class': 'main plaintable', 'id': 'inheritance-role-table'} |
|
262 |
fields = ('name', 'ou') |
|
263 |
empty_text = _('None') |
src/authentic2/manager/templates/authentic2/manager/role_members.html | ||
---|---|---|
109 | 109 |
{% trans "Child roles:" %} |
110 | 110 |
{% for child in children %} |
111 | 111 |
<a href="{% url "a2-manager-role-members" pk=child.pk %}">{{ child }}</a> |
112 |
{% if child.direct %} |
|
113 |
<a rel="popup" href="{% url "a2-manager-role-remove-child" pk=object.pk child_pk=child.pk %}" class="role-remove icon-minus-sign"></a> |
|
114 |
{% else %} |
|
115 |
<a title="{% trans "Indirect child role" %}" class="disabled role-remove icon-minus-sign"></a> |
|
116 |
{% endif %} |
|
112 |
{% if not forloop.last %} − {% endif %} |
|
117 | 113 |
{% endfor %} |
118 | 114 |
{% if view.can_manage_members %} |
119 |
<a rel="popup" href="{% url "a2-manager-role-add-child" pk=object.pk %}" class="role-add icon-add-sign"></a>
|
|
115 |
<a href="{% url "a2-manager-role-add-child" pk=object.pk %}" class="role-add icon-add-sign"></a> |
|
120 | 116 |
{% else %} |
121 | 117 |
<a title="{% trans "Permission denied" %}" class="disabled role-add icon-add-sign"></a> |
122 | 118 |
{% endif %} |
... | ... | |
127 | 123 |
<a class="role" href="{% url "a2-manager-role-members" pk=parent.pk %}"> |
128 | 124 |
{% if parent.ou and has_multiple_ou %}{{ parent.ou }} - {% endif %}{{ parent }} |
129 | 125 |
</a> |
130 |
{% if parent.direct %} |
|
131 |
{% if not object.is_internal %} |
|
132 |
<a rel="popup" href="{% url "a2-manager-role-remove-parent" pk=object.pk parent_pk=parent.pk %}" class="role-remove icon-minus-sign"></a> |
|
133 |
{% else %} |
|
134 |
<a title="{% trans "This role is technical, you cannot modify its permissions." %}" class="disabled role-add icon-minus-sign"></a> |
|
135 |
{% endif %} |
|
136 |
{% else %} |
|
137 |
<a title="{% trans "Indirect parent role" %}" class="disabled role-remove icon-minus-sign"></a> |
|
138 |
{% endif %} |
|
126 |
{% if not forloop.last %} − {% endif %} |
|
139 | 127 |
{% endfor %} |
140 | 128 |
{% if not object.is_internal %} |
141 |
<a rel="popup" href="{% url "a2-manager-role-add-parent" pk=object.pk %}" class="role-add icon-add-sign"></a>
|
|
129 |
<a href="{% url "a2-manager-role-add-parent" pk=object.pk %}" class="role-add icon-add-sign"></a> |
|
142 | 130 |
{% else %} |
143 | 131 |
<a title="{% trans "This role is technical, you cannot modify its permissions." %}" class="disabled role-add icon-add-sign"></a> |
144 | 132 |
{% endif %} |
src/authentic2/manager/templates/authentic2/manager/role_remove_child.html | ||
---|---|---|
1 |
{% extends "authentic2/manager/base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block messages %} |
|
5 |
{% endblock %} |
|
6 | ||
7 |
{% block main %} |
|
8 |
{% if title %} |
|
9 |
<div id="appbar"><h2>{{ title }}</h2></div> |
|
10 |
{% endif %} |
|
11 |
<form method="post"> |
|
12 |
{% csrf_token %} |
|
13 |
<div class="form-inner-container"> |
|
14 |
{% block caption %} |
|
15 |
<p>{% blocktrans %}Do you want to remove child role {{ child }} ?{% endblocktrans %}</p> |
|
16 |
{% endblock %} |
|
17 |
<div class="buttons"> |
|
18 |
<button>{% trans "Remove" %}</button> |
|
19 |
<a class="cancel" href="..">{% trans "Cancel" %}</a> |
|
20 |
</div> |
|
21 |
</div> |
|
22 |
</form> |
|
23 |
{% endblock %} |
src/authentic2/manager/templates/authentic2/manager/role_remove_parent.html | ||
---|---|---|
1 |
{% extends "authentic2/manager/base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block messages %} |
|
5 |
{% endblock %} |
|
6 | ||
7 |
{% block main %} |
|
8 |
{% if title %} |
|
9 |
<div id="appbar"><h2>{{ title }}</h2></div> |
|
10 |
{% endif %} |
|
11 |
<form method="post"> |
|
12 |
{% csrf_token %} |
|
13 |
<div class="form-inner-container"> |
|
14 |
{% block caption %} |
|
15 |
<p>{% blocktrans %}Do you want to remove parent role {{ parent }} ?{% endblocktrans %}</p> |
|
16 |
{% endblock %} |
|
17 |
<div class="buttons"> |
|
18 |
<button>{% trans "Remove" %}</button> |
|
19 |
<a class="cancel" href="..">{% trans "Cancel" %}</a> |
|
20 |
</div> |
|
21 |
</div> |
|
22 |
</form> |
|
23 |
{% endblock %} |
src/authentic2/manager/templates/authentic2/manager/roles_inheritance.html | ||
---|---|---|
1 |
{% extends "authentic2/manager/role_common.html" %} |
|
2 |
{% load i18n static django_tables2 %} |
|
3 | ||
4 |
{% block breadcrumb %} |
|
5 |
{{ block.super }} |
|
6 |
<a href="..">{{ object }}</a> |
|
7 |
<a href="#">{% trans "Role inheritance" %}</a> |
|
8 |
{% endblock %} |
|
9 | ||
10 |
{% block extrascripts %} |
|
11 |
{{ block.super }} |
|
12 |
<script src="{% static "authentic2/manager/js/roles_ajax_checkbox.js" %}"></script> |
|
13 |
{% endblock %} |
|
14 | ||
15 |
{% block main %} |
|
16 |
{% with row_link=0 %} |
|
17 |
{% render_table table "authentic2/manager/table.html" %} |
|
18 |
{% endwith %} |
|
19 |
{% endblock %} |
|
20 | ||
21 |
{% block sidebar %} |
|
22 |
<aside id="sidebar"> |
|
23 |
{% include "authentic2/manager/search_form.html" %} |
|
24 |
</aside> |
|
25 |
{% endblock %} |
src/authentic2/manager/templates/authentic2/manager/user_ou_roles.html | ||
---|---|---|
1 | 1 |
{% extends "authentic2/manager/user_common_roles.html" %} |
2 |
{% load django_tables2 %} |
|
2 |
{% load static django_tables2 %}
|
|
3 | 3 | |
4 | 4 |
{% block extrascripts %} |
5 | 5 |
{{ block.super }} |
src/authentic2/manager/urls.py | ||
---|---|---|
128 | 128 |
url(r'^roles/(?P<pk>\d+)/$', role_views.members, name='a2-manager-role-members'), |
129 | 129 |
url(r'^roles/(?P<pk>\d+)/add-child/$', role_views.add_child, name='a2-manager-role-add-child'), |
130 | 130 |
url(r'^roles/(?P<pk>\d+)/add-parent/$', role_views.add_parent, name='a2-manager-role-add-parent'), |
131 |
url( |
|
132 |
r'^roles/(?P<pk>\d+)/remove-child/(?P<child_pk>\d+)/$', |
|
133 |
role_views.remove_child, |
|
134 |
name='a2-manager-role-remove-child', |
|
135 |
), |
|
136 |
url( |
|
137 |
r'^roles/(?P<pk>\d+)/remove-parent/(?P<parent_pk>\d+)/$', |
|
138 |
role_views.remove_parent, |
|
139 |
name='a2-manager-role-remove-parent', |
|
140 |
), |
|
141 | 131 |
url( |
142 | 132 |
r'^roles/(?P<pk>\d+)/add-admin-user/$', |
143 | 133 |
role_views.add_admin_user, |
tests/test_manager.py | ||
---|---|---|
861 | 861 | |
862 | 862 | |
863 | 863 |
def test_roles_for_change_widget(admin, app, db): |
864 |
from authentic2.manager.forms import RoleParentsForm
|
|
864 |
from authentic2.manager.forms import RoleParentForm |
|
865 | 865 | |
866 | 866 |
login(app, admin, '/manage/') |
867 | 867 |
Role.objects.create(name='admin 1') |
868 | 868 |
Role.objects.create(name='user 1') |
869 | 869 | |
870 |
form = RoleParentsForm(request=None)
|
|
870 |
form = RoleParentForm(request=None) |
|
871 | 871 |
assert form.as_p() |
872 |
field_id = form.fields['roles'].widget.build_attrs({})['data-field_id']
|
|
872 |
field_id = form.fields['role'].widget.build_attrs({})['data-field_id'] |
|
873 | 873 |
url = reverse('django_select2-json') |
874 | 874 |
response = app.get(url, params={'field_id': field_id, 'term': 'admin'}) |
875 | 875 |
assert len(response.json['results']) == 1 |
... | ... | |
940 | 940 |
simple_role.permissions.add(view_role_perm) |
941 | 941 |
simple_user.roles.add(simple_role) |
942 | 942 |
admin.roles.add(role) |
943 | ||
943 | 944 |
response = app.get('/manage/roles/%s/add-child/' % simple_role.pk) |
944 |
form = response.form
|
|
945 |
form['roles'].force_value(role.pk)
|
|
946 |
form.submit().follow()
|
|
945 |
token = str(response.context['csrf_token'])
|
|
946 |
params = {'action': 'add', 'role': role.pk, 'csrfmiddlewaretoken': token}
|
|
947 |
response = app.post('/manage/roles/%s/add-child/' % simple_role.pk, params=params)
|
|
947 | 948 |
assert role in simple_role.children() |
948 | 949 | |
949 |
url = '/manage/roles/%s/remove-child/%s/' % (simple_role.pk, role.pk) |
|
950 |
token = str(response.context['csrf_token']) |
|
951 |
app.post(url, params={'csrfmiddlewaretoken': token}) |
|
952 |
assert not role in simple_role.children() |
|
950 |
params = {'action': 'remove', 'role': role.pk, 'csrfmiddlewaretoken': token} |
|
951 |
response = app.post('/manage/roles/%s/add-child/' % simple_role.pk, params=params) |
|
952 |
assert role not in simple_role.children() |
|
953 | 953 | |
954 | 954 |
response = app.get('/manage/roles/%s/add-parent/' % role.pk) |
955 |
form = response.form
|
|
956 |
form['roles'].force_value(simple_role.pk)
|
|
957 |
form.submit().follow()
|
|
955 |
token = str(response.context['csrf_token'])
|
|
956 |
params = {'action': 'add', 'role': simple_role.pk, 'csrfmiddlewaretoken': token}
|
|
957 |
response = app.post('/manage/roles/%s/add-parent/' % role.pk, params=params)
|
|
958 | 958 |
assert simple_role in role.parents() |
959 | 959 | |
960 |
url = '/manage/roles/%s/remove-parent/%s/' % (role.pk, simple_role.pk) |
|
961 |
token = str(response.context['csrf_token']) |
|
962 |
app.post(url, params={'csrfmiddlewaretoken': token}) |
|
960 |
params = {'action': 'remove', 'role': simple_role.pk, 'csrfmiddlewaretoken': token} |
|
961 |
response = app.post('/manage/roles/%s/add-parent/' % role.pk, params=params) |
|
963 | 962 |
assert simple_role not in role.parents() |
964 | 963 | |
964 |
# try to add arbitrary role |
|
965 |
admin_role = Role.objects.get(slug='_a2-manager') |
|
966 |
response = app.get('/manage/roles/%s/add-parent/' % role.pk) |
|
967 |
token = str(response.context['csrf_token']) |
|
968 |
params = {'action': 'add', 'role': admin_role.pk, 'csrfmiddlewaretoken': token} |
|
969 |
response = app.post('/manage/roles/%s/add-parent/' % simple_role.pk, params=params) |
|
970 |
assert admin_role not in role.parents() |
|
971 | ||
965 | 972 |
# user roles view works |
966 | 973 |
response = app.get('/manage/users/%s/roles/' % admin.pk) |
967 | 974 |
q = response.pyquery.remove_namespaces() |
... | ... | |
978 | 985 |
app.get('/manage/roles/%s/delete/' % simple_role.pk, status=403) |
979 | 986 | |
980 | 987 | |
981 |
def test_manager_permission_inheritance(app, simple_user, admin, simple_role): |
|
982 |
admin_role = Role.objects.get(slug='_a2-manager') |
|
983 |
view_role_perm = get_permission_model().objects.create( |
|
984 |
operation=get_operation(VIEW_OP), |
|
985 |
target_ct=ContentType.objects.get_for_model(Role), |
|
986 |
target_id=simple_role.pk, |
|
987 |
) |
|
988 |
simple_role.permissions.add(view_role_perm) |
|
989 |
simple_user.roles.add(simple_role) |
|
990 |
login(app, simple_user, '/manage/') |
|
991 | ||
992 |
response = app.get('/manage/roles/%s/add-parent/' % simple_role.pk) |
|
993 |
form = response.form |
|
994 |
form['roles'].force_value(admin_role.pk) |
|
995 |
response = form.submit() |
|
996 | ||
997 |
assert response.status_code == 200 |
|
998 |
assert not admin_role in simple_role.parents() |
|
999 | ||
1000 | ||
1001 | 988 |
def test_manager_widget_fields_validation(app, simple_user, simple_role): |
1002 | 989 |
'''Verify that fields corresponding to widget implement queryset restrictions.''' |
1003 | 990 |
from authentic2.manager.forms import ( |
1004 | 991 |
ChooseRoleForm, |
1005 | 992 |
ChooseUserForm, |
1006 | 993 |
ChooseUserRoleForm, |
1007 |
RoleParentsForm,
|
|
994 |
RoleParentForm, |
|
1008 | 995 |
RolesForm, |
1009 | 996 |
UsersForm, |
1010 | 997 |
) |
... | ... | |
1056 | 1043 |
assert error_message in form.errors['roles'][0] |
1057 | 1044 | |
1058 | 1045 |
# For those we need manage_members permission |
1059 |
form = RoleParentsForm(request=request, data={'roles': [visible_role.pk]})
|
|
1060 |
assert error_message in form.errors['roles'][0]
|
|
1046 |
form = RoleParentForm(request=request, data={'role': visible_role.pk, 'action': 'add'})
|
|
1047 |
assert error_message in form.errors['role'][0] |
|
1061 | 1048 | |
1062 | 1049 |
form = ChooseUserRoleForm(request=request, data={'role': visible_role.pk, 'action': 'add'}) |
1063 | 1050 |
assert error_message in form.errors['role'][0] |
... | ... | |
1070 | 1057 |
simple_role.permissions.add(change_role_perm) |
1071 | 1058 |
del simple_user._rbac_perms_cache |
1072 | 1059 | |
1073 |
form = RoleParentsForm(request=request, data={'roles': [visible_role.pk]})
|
|
1060 |
form = RoleParentForm(request=request, data={'role': visible_role.pk, 'action': 'add'})
|
|
1074 | 1061 |
assert form.is_valid() |
1075 | 1062 | |
1076 | 1063 |
form = ChooseUserRoleForm(request=request, data={'role': visible_role.pk, 'action': 'add'}) |
1077 | 1064 |
assert form.is_valid() |
1078 | 1065 | |
1079 | 1066 | |
1080 |
def test_manager_role_widgets_choices(app, simple_user, simple_role): |
|
1081 |
def get_choices(response):
|
|
1082 |
select2_json = request_select2(app, response)
|
|
1083 |
assert select2_json['more'] is False
|
|
1084 |
return {result['id'] for result in select2_json['results']}
|
|
1067 |
@pytest.mark.parametrize('relation', ['child', 'parent']) |
|
1068 |
def test_manager_role_inheritance_list(app, admin, simple_role, ou1, relation):
|
|
1069 |
first_role = Role.objects.create(name='first_role', ou=simple_role.ou)
|
|
1070 |
second_role = Role.objects.create(name='second_role', ou=simple_role.ou)
|
|
1071 |
third_role = Role.objects.create(name='third_role', ou=ou1)
|
|
1085 | 1072 | |
1073 |
if relation == 'child': |
|
1074 |
simple_role.add_child(first_role) |
|
1075 |
first_role.add_child(second_role) |
|
1076 |
elif relation == 'parent': |
|
1077 |
simple_role.add_parent(first_role) |
|
1078 |
first_role.add_parent(second_role) |
|
1079 | ||
1080 |
response = login(app, admin) |
|
1081 |
response = app.get('/manage/roles/%s/add-%s/' % (simple_role.pk, relation)) |
|
1082 |
q = response.pyquery.remove_namespaces() |
|
1083 |
assert len(q('table tbody tr')) == 3 |
|
1084 |
assert {e.text_content() for e in q('table tbody td.name')} == { |
|
1085 |
first_role.name, |
|
1086 |
second_role.name, |
|
1087 |
third_role.name, |
|
1088 |
} |
|
1089 | ||
1090 |
row = q('table tbody tr')[0] |
|
1091 |
name, ou, via, member = row.getchildren() |
|
1092 |
assert name.text_content() == 'first_role' |
|
1093 |
assert ou.text_content() == 'Default organizational unit' |
|
1094 |
assert not via.text_content() |
|
1095 |
member = member.find('input') |
|
1096 |
assert member.checked |
|
1097 |
assert member.attrib['class'] == 'role-member' |
|
1098 | ||
1099 |
row = q('table tbody tr')[1] |
|
1100 |
name, ou, via, member = row.getchildren() |
|
1101 |
assert name.text_content() == 'second_role' |
|
1102 |
assert ou.text_content() == 'Default organizational unit' |
|
1103 |
assert via.text_content() == 'first_role' |
|
1104 |
member = member.find('input') |
|
1105 |
assert not member.checked |
|
1106 |
assert member.attrib['class'] == 'role-member indeterminate' |
|
1107 | ||
1108 |
row = q('table tbody tr')[2] |
|
1109 |
name, ou, via, member = row.getchildren() |
|
1110 |
assert name.text_content() == 'third_role' |
|
1111 |
assert ou.text_content() == 'OU1' |
|
1112 |
assert not via.text_content() |
|
1113 |
member = member.find('input') |
|
1114 |
assert not member.checked |
|
1115 |
assert member.attrib['class'] == 'role-member' |
|
1116 | ||
1117 | ||
1118 |
def test_manager_role_inheritance_list_search_permission(app, admin, simple_user, simple_role, ou1): |
|
1086 | 1119 |
visible_role = Role.objects.create(name='visible_role', ou=simple_user.ou) |
1087 |
Role.objects.create(name='invisible_role', ou=simple_user.ou) |
|
1120 |
visible_role_2 = Role.objects.create(name='visible_role_2', ou=ou1) |
|
1121 |
invisible_role = Role.objects.create(name='invisible_role', ou=simple_user.ou) |
|
1088 | 1122 |
admin_of_simple_role = simple_role.get_admin_role() |
1089 | 1123 | |
1090 | 1124 |
admin_of_simple_role.members.add(simple_user) |
1091 |
view_role_perm = get_permission_model().objects.create( |
|
1092 |
operation=get_operation(VIEW_OP), |
|
1093 |
target_ct=ContentType.objects.get_for_model(Role), |
|
1094 |
target_id=visible_role.pk, |
|
1095 |
) |
|
1096 |
simple_role.permissions.add(view_role_perm) |
|
1125 |
for role in (visible_role, visible_role_2): |
|
1126 |
view_role_perm = get_permission_model().objects.create( |
|
1127 |
operation=get_operation(VIEW_OP), |
|
1128 |
target_ct=ContentType.objects.get_for_model(Role), |
|
1129 |
target_id=role.pk, |
|
1130 |
) |
|
1131 |
simple_role.permissions.add(view_role_perm) |
|
1097 | 1132 |
simple_user.roles.add(simple_role) |
1098 | 1133 | |
1099 | 1134 |
response = login(app, simple_user, '/manage/roles/') |
1100 | 1135 | |
1101 |
# all visible roles are shown |
|
1136 |
# all visible roles are shown, except current role
|
|
1102 | 1137 |
response = app.get('/manage/roles/%s/add-child/' % simple_role.pk) |
1103 |
assert {visible_role.pk, simple_role.pk} == get_choices(response) |
|
1104 | ||
1105 |
# all roles with manage_members permissions are shown |
|
1106 |
response = app.get('/manage/roles/%s/add-parent/' % simple_role.pk) |
|
1107 |
assert {simple_role.pk, admin_of_simple_role.pk} == get_choices(response) |
|
1108 | ||
1109 |
response = app.get('/manage/roles/%s/add-parent/' % visible_role.pk) |
|
1110 |
assert {simple_role.pk, admin_of_simple_role.pk} == get_choices(response) |
|
1111 | ||
1112 | ||
1113 |
def test_manager_widgets_field_id_other_user(app, admin, simple_user, simple_role): |
|
1114 |
other_role = Role.objects.create(name='visible_role', ou=simple_user.ou) |
|
1115 |
simple_role.get_admin_role().members.add(simple_user) |
|
1116 | ||
1117 |
response = login(app, admin, '/manage/roles/%s/add-child/' % simple_role.pk) |
|
1118 |
select2_json = request_select2(app, response) |
|
1119 |
assert select2_json['more'] is False |
|
1138 |
q = response.pyquery.remove_namespaces() |
|
1139 |
assert len(q('table tbody tr')) == 2 |
|
1140 |
assert {e.text_content() for e in q('table tbody td.name')} == {visible_role.name, visible_role_2.name} |
|
1120 | 1141 | |
1121 |
# admin can see every roles |
|
1122 |
assert {simple_role.pk, other_role.pk} == {result['id'] for result in select2_json['results']} |
|
1142 |
# filter by ou |
|
1143 |
response.form['search-ou'] = ou1.pk |
|
1144 |
response = response.form.submit() |
|
1145 |
q = response.pyquery.remove_namespaces() |
|
1146 |
assert len(q('table tbody tr')) == 1 |
|
1147 |
assert {e.text_content() for e in q('table tbody td.name')} == {visible_role_2.name} |
|
1123 | 1148 | |
1124 |
login(app, simple_user) |
|
1125 |
# same request from the page served for admin |
|
1126 |
select2_json = request_select2(app, response) |
|
1127 |
# simple_user doesn't see all roles |
|
1128 |
assert simple_role.pk == select2_json['results'][0]['id'] |
|
1149 |
# filter by name |
|
1150 |
response.form['search-text'] = '2' |
|
1151 |
response.form['search-ou'] = 'all' |
|
1152 |
response = response.form.submit() |
|
1153 |
q = response.pyquery.remove_namespaces() |
|
1154 |
assert len(q('table tbody tr')) == 1 |
|
1155 |
assert {e.text_content() for e in q('table tbody td.name')} == {visible_role_2.name} |
|
1129 | 1156 | |
1130 |
# anymous user receive 404 |
|
1131 |
app.session.flush() |
|
1132 |
select2_json = request_select2(app, response, get_kwargs={'status': 404}) |
|
1157 |
# all roles with manage_members permissions are shown |
|
1158 |
response = app.get('/manage/roles/%s/add-parent/' % visible_role.pk) |
|
1159 |
q = response.pyquery.remove_namespaces() |
|
1160 |
assert len(q('table tbody tr')) == 1 |
|
1161 |
assert {e.text_content() for e in q('table tbody td.name')} == {simple_role.name} |
|
1133 | 1162 | |
1134 | 1163 | |
1135 | 1164 |
def test_display_parent_roles_on_role_page(app, superuser, settings): |
1136 |
- |