authentic/src/authentic2/manager/role_views.py

842 lines
29 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from functools import reduce
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import EmptyPage, Paginator
from django.db import transaction
from django.db.models import BooleanField, Count, ExpressionWrapper, F, Prefetch, Q, Value
from django.db.models.functions import Cast
from django.http import Http404, JsonResponse
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, FormView, TemplateView
from django.views.generic.detail import SingleObjectMixin
from authentic2 import data_transfer
from authentic2.a2_rbac.models import OrganizationalUnit, Permission, Role, RoleParenting
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.apps.journal.views import JournalViewWithContext
from authentic2.forms.profile import modelform_factory
from authentic2.utils import crypto, hooks
from authentic2.utils.misc import redirect
from . import forms, resources, tables, views
from .journal_views import BaseJournalView
from .utils import has_show_username, label_from_role, label_from_user
User = get_user_model()
class RolesMixin:
service_roles = True
admin_roles = False
def get_queryset(self):
qs = super().get_queryset()
qs = qs.select_related('ou')
permission_ct = ContentType.objects.get_for_model(Permission)
ct_ct = ContentType.objects.get_for_model(ContentType)
ou_ct = ContentType.objects.get_for_model(OrganizationalUnit)
permission_qs = Permission.objects.filter(target_ct_id__in=[ct_ct.id, ou_ct.id]).values_list(
'id', flat=True
)
# only non role-admin roles, they are accessed through the
# RoleManager views
if not self.admin_roles:
qs = qs.filter(
Q(admin_scope_ct__isnull=True)
| Q(admin_scope_ct=permission_ct, admin_scope_id__in=permission_qs)
)
if not self.service_roles:
qs = qs.filter(service__isnull=True)
return qs
class RolesView(views.SearchOUMixin, views.HideOUColumnMixin, RolesMixin, views.BaseTableView):
template_name = 'authentic2/manager/roles.html'
model = Role
table_class = tables.RoleTable
search_form_class = forms.RoleSearchForm
permissions = ['a2_rbac.search_role']
title = _('Roles')
def get_queryset(self):
qs = super().get_queryset()
qs = qs.annotate(member_count=Count('members'))
return qs
def get_search_form_kwargs(self):
kwargs = super().get_search_form_kwargs()
kwargs['queryset'] = self.get_queryset()
return kwargs
listing = RolesView.as_view()
class RoleAddView(views.BaseAddView):
template_name = 'authentic2/manager/role_add.html'
model = Role
title = _('Add role')
success_view_name = 'a2-manager-role-members'
exclude_fields = ('slug',)
def get_initial(self):
initial = super().get_initial()
search_ou = self.request.GET.get('search-ou')
initial['ou'] = search_ou or get_default_ou()
return initial
def get_form_class(self):
form = forms.RoleEditForm
fields = [x for x in form.base_fields.keys() if x not in self.exclude_fields]
return modelform_factory(self.model, form=form, fields=fields)
def form_valid(self, form):
response = super().form_valid(form)
hooks.call_hooks(
'event', name='manager-add-role', user=self.request.user, instance=form.instance, form=form
)
self.request.journal.record('manager.role.creation', role=form.instance)
return response
add = RoleAddView.as_view()
class RolesExportView(views.ExportMixin, RolesView):
resource_class = resources.RoleResource
export_prefix = 'roles-export-'
def get(self, request, *args, **kwargs):
export_format = kwargs['format'].lower()
if export_format == 'json':
export = data_transfer.export_site(
data_transfer.ExportContext(
role_qs=self.get_table_data(), export_roles=True, export_ous=False
)
)
return self.export_response(json.dumps(export, indent=4), 'application/json', 'json')
return super().get(request, *args, **kwargs)
export = RolesExportView.as_view()
class RoleViewMixin(RolesMixin):
model = Role
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['ou'] = self.get_object().ou
return ctx
class RoleEditView(RoleViewMixin, views.BaseEditView):
template_name = 'authentic2/manager/role_edit.html'
title = _('Edit role description')
def get_form_class(self):
return forms.RoleEditForm
def form_valid(self, form):
response = super().form_valid(form)
hooks.call_hooks(
'event', name='manager-edit-role', user=self.request.user, instance=form.instance, form=form
)
self.request.journal.record('manager.role.edit', role=form.instance, form=form)
return response
edit = RoleEditView.as_view()
class RoleMembersView(views.HideOUColumnMixin, RoleViewMixin, views.BaseSubTableView):
template_name = 'authentic2/manager/role_members.html'
form_class = forms.ChooseUserOrRoleForm
success_url = '.'
search_form_class = forms.RoleMembersSearchForm
permissions = ['a2_rbac.view_role']
slug_field = 'uuid'
admin_roles = True
@property
def table_class(self):
if self.view_all_members:
return tables.RoleMembersTable
return tables.MixedUserRoleTable
@property
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 dispatch(self, request, *args, **kwargs):
self.children = views.filter_view(
self.request, self.get_object().children(include_self=False, annotate=True)
)
return super().dispatch(request, *args, **kwargs)
def get_table_data(self):
if self.view_all_members:
return super().get_table_data()
members = views.filter_view(self.request, self.object.members.all())
members = members.annotate(direct=Value(True, output_field=BooleanField()))
members = self.filter_by_search(members)
return list(self.children) + list(members)
def get_table_queryset(self):
via_prefetch = Prefetch('roles', queryset=self.children, to_attr='via')
return self.object.all_members().prefetch_related(via_prefetch)
@property
def view_all_members(self):
return self.search_form.is_valid() and self.search_form.cleaned_data.get('all_members')
def form_valid(self, form):
action = form.cleaned_data['action']
if not self.can_manage_members:
messages.warning(self.request, _('You are not authorized'))
elif 'user' in form.cleaned_data:
if action == 'add':
self.add_user(form.cleaned_data['user'])
elif action == 'remove':
self.remove_user(form.cleaned_data['user'])
elif 'role' in form.cleaned_data:
if action == 'add':
self.add_role(form.cleaned_data['role'])
elif action == 'remove':
self.remove_role(form.cleaned_data['role'])
return super().form_valid(form)
def add_user(self, user):
if self.object.members.filter(pk=user.pk).exists():
messages.warning(self.request, _('User already in this role.'))
else:
self.object.members.add(user)
hooks.call_hooks(
'event', name='manager-add-role-member', user=self.request.user, role=self.object, member=user
)
self.request.journal.record('manager.role.membership.grant', role=self.object, member=user)
def remove_user(self, user):
if not self.object.members.filter(pk=user.pk).exists():
messages.warning(self.request, _('User was not in this role.'))
else:
self.object.members.remove(user)
hooks.call_hooks(
'event',
name='manager-remove-role-member',
user=self.request.user,
role=self.object,
member=user,
)
self.request.journal.record('manager.role.membership.removal', role=self.object, member=user)
def add_role(self, role):
self.object.add_child(role)
hooks.call_hooks(
'event', name='manager-add-child-role', user=self.request.user, parent=self.object, child=role
)
self.request.journal.record('manager.role.inheritance.addition', parent=self.object, child=role)
def remove_role(self, role):
self.object.remove_child(role)
hooks.call_hooks(
'event', name='manager-remove-child-role', user=self.request.user, parent=self.object, child=role
)
self.request.journal.record('manager.role.inheritance.removal', parent=self.object, child=role)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['role'] = self.object
return kwargs
def get_search_form_kwargs(self):
kwargs = super().get_search_form_kwargs()
if not self.children:
kwargs['data']['search-all_members'] = 'on'
kwargs['disable_all_members'] = True
return kwargs
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['parents'] = list(
views.filter_view(
self.request,
self.object.parents(include_self=False, annotate=True).order_by(
F('ou').asc(nulls_first=True), 'name'
),
)[:11]
)
ctx['has_multiple_ou'] = OrganizationalUnit.objects.count() > 1
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
def is_ou_specified(self):
return self.search_form.is_valid() and self.search_form.cleaned_data.get('ou')
def get_table(self, **kwargs):
show_username = has_show_username()
if not show_username and self.is_ou_specified():
show_username = self.is_ou_specified().show_username
if not show_username:
exclude = kwargs.setdefault('exclude', [])
if 'username' not in exclude:
exclude.append('username')
return super().get_table(**kwargs)
members = RoleMembersView.as_view()
class RoleDeleteView(RoleViewMixin, views.BaseDeleteView):
title = _('Delete role')
template_name = 'authentic2/manager/role_delete.html'
def post(self, request, *args, **kwargs):
if not self.can_delete:
raise PermissionDenied
return super().post(request, *args, **kwargs)
def get_success_url(self):
return reverse('a2-manager-roles')
def delete(self, request, *args, **kwargs):
role = self.get_object()
hooks.call_hooks('event', name='manager-delete-role', user=request.user, role=role)
self.request.journal.record('manager.role.deletion', role=role)
return super().delete(request, *args, **kwargs)
delete = RoleDeleteView.as_view()
class RoleMembersExportView(views.ExportMixin, RoleMembersView):
resource_class = resources.UserResource
permissions = ['a2_rbac.view_role']
def get_data(self):
return self.get_table_data()
members_export = RoleMembersExportView.as_view()
class RoleChildrenView(RoleViewMixin, views.HideOUColumnMixin, views.BaseSubTableView):
title = _('Add child role')
form_class = forms.ChooseRoleForm
table_class = tables.InheritanceRolesTable
search_form_class = forms.RoleSearchForm
template_name = 'authentic2/manager/roles_inheritance.html'
permissions = ['a2_rbac.manage_members_role']
success_url = '.'
slug_field = 'uuid'
def get_table_queryset(self):
qs = super().get_table_queryset()
qs = qs.exclude(pk=self.object.pk)
children = self.object.children(annotate=True, include_self=False)
children = children.annotate(is_direct=Cast('direct', output_field=BooleanField()))
qs = qs.annotate(
checked=ExpressionWrapper(Q(pk__in=children.filter(is_direct=True)), output_field=BooleanField())
)
qs = qs.annotate(
indeterminate=ExpressionWrapper(
Q(pk__in=children.filter(is_direct=False)), output_field=BooleanField()
)
)
rp_qs = RoleParenting.alive.filter(parent__in=children).annotate(name=F('parent__name'))
qs = qs.prefetch_related(Prefetch('parent_relation', queryset=rp_qs, to_attr='via'))
return qs
def form_valid(self, form):
role = form.cleaned_data['role']
action = form.cleaned_data['action']
if action == 'add':
self.object.add_child(role)
hooks.call_hooks(
'event', name='manager-add-child-role', user=self.request.user, parent=self.object, child=role
)
self.request.journal.record('manager.role.inheritance.addition', parent=self.object, child=role)
elif action == 'remove':
self.object.remove_child(role)
hooks.call_hooks(
'event',
name='manager-remove-child-role',
user=self.request.user,
parent=self.object,
child=role,
)
self.request.journal.record('manager.role.inheritance.removal', parent=self.object, child=role)
return super().form_valid(form)
def get_search_form_kwargs(self):
kwargs = super().get_search_form_kwargs()
kwargs['queryset'] = self.request.user.filter_by_perm('a2_rbac.view_role', Role.objects.all())
return kwargs
children = RoleChildrenView.as_view()
class RoleParentsView(RoleViewMixin, views.HideOUColumnMixin, views.BaseSubTableView):
title = _('Include permissions from roles')
form_class = forms.RoleParentForm
table_class = tables.InheritanceRolesTable
search_form_class = forms.RoleSearchForm
template_name = 'authentic2/manager/roles_inheritance.html'
success_url = '.'
slug_field = 'uuid'
@property
def admin_roles(self):
if not hasattr(self, 'search_form'):
return False
return self.search_form.cleaned_data.get('admin_roles', False)
def dispatch(self, request, *args, **kwargs):
if self.get_object().is_internal():
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def get_table_queryset(self):
qs = super().get_table_queryset()
qs = self.request.user.filter_by_perm('a2_rbac.manage_members_role', qs)
qs = qs.exclude(pk=self.object.pk)
parents = self.object.parents(annotate=True, include_self=False)
parents = parents.annotate(is_direct=Cast('direct', output_field=BooleanField()))
qs = qs.annotate(
checked=ExpressionWrapper(Q(pk__in=parents.filter(is_direct=True)), output_field=BooleanField())
)
qs = qs.annotate(
indeterminate=ExpressionWrapper(
Q(pk__in=parents.filter(is_direct=False)), output_field=BooleanField()
)
)
rp_qs = RoleParenting.alive.filter(child__in=parents).annotate(name=F('child__name'))
qs = qs.prefetch_related(Prefetch('child_relation', queryset=rp_qs, to_attr='via'))
return qs
def form_valid(self, form):
role = form.cleaned_data['role']
action = form.cleaned_data['action']
if action == 'add':
self.object.add_parent(role)
hooks.call_hooks(
'event', name='manager-add-child-role', user=self.request.user, parent=role, child=self.object
)
self.request.journal.record('manager.role.inheritance.addition', parent=role, child=self.object)
elif action == 'remove':
self.object.remove_parent(role)
hooks.call_hooks(
'event',
name='manager-remove-child-role',
user=self.request.user,
parent=role,
child=self.object,
)
self.request.journal.record('manager.role.inheritance.removal', parent=role, child=self.object)
return super().form_valid(form)
def get_search_form_kwargs(self):
kwargs = super().get_search_form_kwargs()
kwargs['queryset'] = self.request.user.filter_by_perm(
'a2_rbac.manage_members_role', Role.objects.all()
)
return kwargs
parents = RoleParentsView.as_view()
class RoleAddAdminRoleView(
views.AjaxFormViewMixin,
views.TitleMixin,
views.PermissionMixin,
views.FormNeedsRequest,
SingleObjectMixin,
FormView,
):
title = _('Add admin role')
model = Role
form_class = forms.RolesForm
success_url = '..'
template_name = 'authentic2/manager/form.html'
permissions = ['a2_rbac.change_role']
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
administered_role = self.get_object()
for role in form.cleaned_data['roles']:
administered_role.get_admin_role().add_child(role)
hooks.call_hooks(
'event',
name='manager-add-admin-role',
user=self.request.user,
role=administered_role,
admin_role=role,
)
self.request.journal.record(
'manager.role.administrator.role.addition', role=administered_role, admin_role=role
)
return super().form_valid(form)
add_admin_role = RoleAddAdminRoleView.as_view()
class RoleRemoveAdminRoleView(
views.TitleMixin, views.AjaxFormViewMixin, SingleObjectMixin, views.PermissionMixin, TemplateView
):
title = _('Remove admin role')
model = Role
success_url = '../..'
template_name = 'authentic2/manager/role_remove_admin_role.html'
permissions = ['a2_rbac.change_role']
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
self.child = self.get_queryset().get(pk=kwargs['role_pk'])
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['child'] = self.child
return ctx
def post(self, request, *args, **kwargs):
self.object.get_admin_role().remove_child(self.child)
hooks.call_hooks(
'event',
name='manager-remove-admin-role',
user=self.request.user,
role=self.object,
admin_role=self.child,
)
self.request.journal.record(
'manager.role.administrator.role.removal', role=self.object, admin_role=self.child
)
return redirect(self.request, self.success_url)
remove_admin_role = RoleRemoveAdminRoleView.as_view()
class RoleAddAdminUserView(
views.AjaxFormViewMixin,
views.TitleMixin,
views.PermissionMixin,
views.FormNeedsRequest,
SingleObjectMixin,
FormView,
):
title = _('Add admin user')
model = Role
form_class = forms.UsersForm
success_url = '..'
template_name = 'authentic2/manager/form.html'
permissions = ['a2_rbac.change_role']
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
administered_role = self.get_object()
for user in form.cleaned_data['users']:
administered_role.get_admin_role().members.add(user)
hooks.call_hooks(
'event',
name='manager-add-admin-role-user',
user=self.request.user,
role=administered_role,
admin=user,
)
self.request.journal.record(
'manager.role.administrator.user.addition', role=administered_role, admin_user=user
)
return super().form_valid(form)
add_admin_user = RoleAddAdminUserView.as_view()
class RoleRemoveAdminUserView(
views.TitleMixin, views.AjaxFormViewMixin, SingleObjectMixin, views.PermissionMixin, TemplateView
):
title = _('Remove admin user')
model = Role
success_url = '../..'
template_name = 'authentic2/manager/role_remove_admin_user.html'
permissions = ['a2_rbac.change_role']
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
self.user = get_user_model().objects.get(pk=kwargs['user_pk'])
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['user'] = self.user
return ctx
def post(self, request, *args, **kwargs):
self.object.get_admin_role().members.remove(self.user)
hooks.call_hooks(
'event',
name='remove-remove-admin-role-user',
user=self.request.user,
role=self.object,
admin=self.user,
)
self.request.journal.record(
'manager.role.administrator.user.removal', role=self.object, admin_user=self.user
)
return redirect(self.request, self.success_url)
remove_admin_user = RoleRemoveAdminUserView.as_view()
class RolesImportView(
views.PermissionMixin, views.TitleMixin, views.MediaMixin, views.FormNeedsRequest, FormView
):
form_class = forms.RolesImportForm
model = Role
template_name = 'authentic2/manager/import_form.html'
title = _('Roles Import')
def get_initial(self):
initial = super().get_initial()
search_ou = self.request.GET.get('search-ou')
if search_ou:
initial['ou'] = search_ou
return initial
def post(self, request, *args, **kwargs):
if not self.can_add:
raise PermissionDenied
return super().post(request, *args, **kwargs)
def form_valid(self, form):
self.ou = form.cleaned_data['ou']
try:
context = data_transfer.ImportContext(
import_ous=False, set_ou=self.ou, allowed_ous=set(form.fields['ou'].queryset)
)
with transaction.atomic():
data_transfer.import_site(form.cleaned_data['site_json'], context)
except ValidationError as e:
form.add_error('site_json', e)
return self.form_invalid(form)
return super().form_valid(form)
def get_success_url(self):
if self.ou:
message = _('Roles have been successfully imported inside "%s" organizational unit.') % self.ou
querystring = '?search-ou=%s' % self.ou.pk
else:
message = _('Roles have been successfully imported.')
querystring = ''
messages.success(self.request, message)
return reverse('a2-manager-roles') + querystring
roles_import = RolesImportView.as_view()
class RolesCsvImportView(
views.PermissionMixin, views.TitleMixin, views.MediaMixin, views.FormNeedsRequest, FormView
):
form_class = forms.RolesCsvImportForm
model = Role
template_name = 'authentic2/manager/roles_csv_import_form.html'
title = _('Roles CSV Import')
def get_initial(self):
initial = super().get_initial()
search_ou = self.request.GET.get('search-ou')
if search_ou:
initial['ou'] = search_ou
return initial
def post(self, request, *args, **kwargs):
if not self.can_add:
raise PermissionDenied
return super().post(request, *args, **kwargs)
def form_valid(self, form):
self.ou = form.cleaned_data['ou']
for role in form.roles:
role.save()
return super().form_valid(form)
def get_success_url(self):
messages.success(
self.request,
_('Roles have been successfully imported inside "%s" organizational unit.') % self.ou,
)
return reverse('a2-manager-roles') + '?search-ou=%s' % self.ou.pk
roles_csv_import = RolesCsvImportView.as_view()
class RolesCsvImportSampleView(TemplateView):
template_name = 'authentic2/manager/sample_roles.txt'
content_type = 'text/csv'
roles_csv_import_sample = RolesCsvImportSampleView.as_view()
class RoleJournal(views.PermissionMixin, JournalViewWithContext, BaseJournalView):
template_name = 'authentic2/manager/role_journal.html'
permissions = ['a2_rbac.view_role']
title = _('Journal')
@cached_property
def context(self):
return get_object_or_404(Role, pk=self.kwargs['pk'])
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['object'] = self.context
ctx['object_name'] = str(self.context)
return ctx
journal = RoleJournal.as_view()
class RolesJournal(views.SearchOUMixin, views.PermissionMixin, JournalViewWithContext, BaseJournalView):
template_name = 'authentic2/manager/roles_journal.html'
permissions = ['a2_rbac.view_role']
title = _('Journal')
@cached_property
def context(self):
return Role
roles_journal = RolesJournal.as_view()
class UserOrRoleSelect2View(DetailView):
form_class = forms.ChooseUserOrRoleForm
model = Role
def get(self, request, *args, **kwargs):
if not self.request.user.is_authenticated or not hasattr(self.request.user, 'filter_by_perm'):
raise Http404('Invalid user')
role = self.get_object()
field_id = self.kwargs.get('field_id', self.request.GET.get('field_id', None))
try:
crypto.loads(field_id)
except (crypto.SignatureExpired, crypto.BadSignature):
raise Http404('Invalid or expired signature.')
search_term = request.GET.get('term', '')
try:
page_number = int(request.GET.get('page', 1))
except ValueError:
page_number = 1
role_qs = self.form_class.get_role_queryset(self.request.user, role)
children = role.children(annotate=True)
children = children.annotate(is_direct=Cast('direct', output_field=BooleanField()))
role_qs = role_qs.exclude(pk__in=children.filter(is_direct=True))
role_qs = self.filter_queryset(role_qs, search_term, ['name', 'service__name', 'ou__name'])
role_paginator = Paginator(role_qs, 10)
try:
role_page = role_paginator.page(page_number)
except EmptyPage:
role_page = []
has_next = False
else:
has_next = role_page.has_next()
user_page = []
if not has_next:
user_qs = self.form_class.get_user_queryset(self.request.user, role)
user_qs = user_qs.exclude(roles=role)
user_qs = self.filter_queryset(
user_qs, search_term, ['username', 'first_name', 'last_name', 'email']
)
page_number = page_number - role_paginator.num_pages + 1
user_paginator = Paginator(user_qs, 10)
try:
user_page = user_paginator.page(page_number)
except EmptyPage:
has_next = False
else:
has_next = user_page.has_next()
return JsonResponse(
{
'results': [self.get_choice(obj) for obj in list(role_page) + list(user_page)],
'more': has_next,
}
)
@staticmethod
def filter_queryset(qs, search_term, search_fields):
lookups = Q()
for term in [term for term in search_term.split() if not term == '']:
lookups &= reduce(Q.__or__, (Q(**{'%s__icontains' % field: term}) for field in search_fields))
return qs.filter(lookups)
def get_choice(self, obj):
if isinstance(obj, Role):
text = label_from_role(obj)
key = 'role-%s'
elif isinstance(obj, User):
text = label_from_user(obj)
key = 'user-%s'
return {'id': key % obj.pk, 'text': text}
user_or_role_select2 = UserOrRoleSelect2View.as_view()