354 lines
12 KiB
Python
354 lines
12 KiB
Python
# pylint: skip-file
|
|
|
|
# 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 django_tables2 as tables
|
|
from django.contrib.auth import get_user_model
|
|
from django.utils import html
|
|
from django.utils.safestring import SafeText
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.utils.translation import gettext_noop
|
|
from django_tables2.utils import A
|
|
|
|
from authentic2.a2_rbac.models import OrganizationalUnit, Role
|
|
from authentic2.middleware import StoreRequestMiddleware
|
|
from authentic2.models import Service
|
|
from authentic2_idp_oidc.models import OIDCAuthorization
|
|
|
|
User = get_user_model()
|
|
|
|
TABLES_MAJOR_VERSION = int(tables.__version__.split('.', maxsplit=1)[0])
|
|
|
|
|
|
class Table(tables.Table):
|
|
class Meta:
|
|
row_attrs = {'data-pk': lambda record: record.pk}
|
|
|
|
|
|
class PermissionLinkColumn(tables.LinkColumn):
|
|
if TABLES_MAJOR_VERSION >= 2:
|
|
|
|
def render(self, record, value, **kwargs):
|
|
request = StoreRequestMiddleware.get_request()
|
|
permission = '%s.view_%s' % (record._meta.app_label, record._meta.model_name)
|
|
if request and not request.user.has_perm(permission, record):
|
|
return value
|
|
return super().render(record, value)
|
|
|
|
else:
|
|
|
|
def render(self, value, record, bound_column, **kwargs):
|
|
request = StoreRequestMiddleware.get_request()
|
|
permission = '%s.view_%s' % (record._meta.app_label, record._meta.model_name)
|
|
if request and not request.user.has_perm(permission, record):
|
|
return value
|
|
return super().render(value, record, bound_column)
|
|
|
|
|
|
class VerifiableEmailColumn(tables.Column):
|
|
def render(self, **kwargs):
|
|
user = kwargs['record']
|
|
verified = user.email_verified
|
|
value = user.email
|
|
if value and verified:
|
|
return html.format_html('<span class="verified">{value}</span>', value=value)
|
|
return value
|
|
|
|
|
|
class UserLinkColumn(PermissionLinkColumn):
|
|
if TABLES_MAJOR_VERSION >= 2:
|
|
|
|
def render(self, record, value, **kwargs):
|
|
value = super().render(record, value, **kwargs)
|
|
if isinstance(record, User) and not record.is_active:
|
|
value = html.format_html(
|
|
'<span class="disabled">{value} ({disabled})</span>', value=value, disabled=_('disabled')
|
|
)
|
|
return value
|
|
|
|
else:
|
|
|
|
def render(self, **kwargs):
|
|
record = kwargs['record']
|
|
value = super().render(**kwargs)
|
|
if isinstance(record, User) and not record.is_active:
|
|
value = html.format_html(
|
|
'<span class="disabled">{value} ({disabled})</span>', value=value, disabled=_('disabled')
|
|
)
|
|
return value
|
|
|
|
|
|
class UserTable(Table):
|
|
get_full_name = UserLinkColumn(
|
|
verbose_name=_('User'),
|
|
args=[A('pk')],
|
|
order_by=('last_name', 'first_name', 'email', 'username'),
|
|
text=lambda record: record.get_full_name(),
|
|
attrs={'td': {'class': 'link'}},
|
|
)
|
|
username = tables.Column(
|
|
attrs={
|
|
'td': {'class': 'username'},
|
|
'th': {'class': 'username orderable'},
|
|
}
|
|
)
|
|
email = VerifiableEmailColumn()
|
|
ou = tables.Column()
|
|
|
|
class Meta(Table.Meta):
|
|
model = User
|
|
attrs = {'class': 'main clickable-rows', 'id': 'user-table'}
|
|
fields = ('username', 'email', 'first_name', 'last_name', 'ou')
|
|
sequence = ('get_full_name', '...')
|
|
empty_text = _('None')
|
|
|
|
|
|
class RoleMembersTable(UserTable):
|
|
direct = tables.BooleanColumn(
|
|
verbose_name=_('Direct member'), orderable=False, attrs={'td': {'class': 'direct'}}
|
|
)
|
|
via = tables.TemplateColumn(
|
|
'{% for role in record.via %}<a href="{% url "a2-manager-role-members" pk=role.pk %}">{{ role'
|
|
' }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}',
|
|
verbose_name=_('Inherited from'),
|
|
orderable=False,
|
|
attrs={'td': {'class': 'via'}},
|
|
)
|
|
|
|
class Meta(UserTable.Meta):
|
|
row_attrs = {'data-pk': lambda record: 'user-%s' % record.pk}
|
|
|
|
|
|
class UserOrRoleColumn(UserLinkColumn):
|
|
def render(self, **kwargs):
|
|
value = super().render(**kwargs)
|
|
if isinstance(kwargs['record'], Role):
|
|
value = html.format_html(_('Members of role {value}'), value=value)
|
|
return value
|
|
|
|
|
|
class MixedUserRoleTable(Table):
|
|
pk = UserOrRoleColumn(
|
|
verbose_name=_('Members'),
|
|
text=str,
|
|
orderable=False,
|
|
attrs={'td': {'class': 'name'}},
|
|
)
|
|
|
|
class Meta(Table.Meta):
|
|
attrs = {'class': 'main clickable-rows', 'id': 'user-table'}
|
|
row_attrs = {
|
|
'data-pk': lambda record: '%s-%s' % ('user' if isinstance(record, User) else 'role', record.pk)
|
|
}
|
|
|
|
|
|
class RoleTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-role-members',
|
|
kwargs={'pk': A('pk')},
|
|
accessor='name',
|
|
verbose_name=_('label'),
|
|
attrs={'td': {'class': 'name'}},
|
|
)
|
|
ou = tables.Column()
|
|
slug = tables.Column(attrs={'td': {'class': 'slug'}})
|
|
member_count = tables.Column(
|
|
verbose_name=_('Direct member count'), orderable=False, attrs={'td': {'class': 'member_count'}}
|
|
)
|
|
|
|
def render_name(self, record, bound_column):
|
|
if TABLES_MAJOR_VERSION >= 2:
|
|
content = bound_column.column.render(record, record.name)
|
|
else:
|
|
content = bound_column.column.render(record.name, record, bound_column)
|
|
if not record.can_manage_members:
|
|
content = SafeText('%s (%s)' % (content, _('LDAP')))
|
|
return content
|
|
|
|
class Meta(Table.Meta):
|
|
model = Role
|
|
attrs = {'class': 'main clickable-rows', 'id': 'role-table'}
|
|
fields = ('name', 'slug', 'ou', 'member_count')
|
|
order_by = ('name',)
|
|
|
|
|
|
class OUTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-ou-detail',
|
|
kwargs={'pk': A('pk')},
|
|
accessor='name',
|
|
verbose_name=_('label'),
|
|
attrs={'td': {'class': 'name'}},
|
|
)
|
|
slug = tables.Column(attrs={'td': {'class': 'slug'}})
|
|
default = tables.BooleanColumn(attrs={'td': {'class': 'default'}})
|
|
|
|
class Meta(Table.Meta):
|
|
model = OrganizationalUnit
|
|
attrs = {'class': 'main clickable-rows', 'id': 'ou-table'}
|
|
fields = ('name', 'slug', 'default')
|
|
empty_text = _('None')
|
|
|
|
|
|
class OuUserRolesTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-role-members',
|
|
kwargs={'pk': A('pk')},
|
|
accessor='name',
|
|
verbose_name=_('label'),
|
|
attrs={'td': {'class': 'name'}},
|
|
)
|
|
via = tables.TemplateColumn(
|
|
'''{% for rel in record.via %}{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endfor %}''',
|
|
verbose_name=_('Inherited from'),
|
|
orderable=False,
|
|
attrs={'td': {'class': 'via'}},
|
|
)
|
|
member = tables.TemplateColumn(
|
|
'{%% load i18n %%}<input class="role-member{%% if not record.member and record.via %%}'
|
|
' indeterminate{%% endif %%}" name="role-{{ record.pk }}" type="checkbox" {%% if record.member'
|
|
' %%}checked{%% endif %%} {%% if not record.has_perm %%}disabled title="{%% trans "%s" %%}"{%% endif'
|
|
' %%} {%% if not record.can_manage_members %%}disabled title="{%% trans "%s" %%}"{%% endif %%}/>'
|
|
% (
|
|
gettext_noop('You are not authorized to manage this role'),
|
|
gettext_noop('This role is synchronised from LDAP, changing members is not allowed.'),
|
|
),
|
|
verbose_name=_('Member'),
|
|
order_by=('member', 'via', 'name'),
|
|
attrs={'td': {'class': 'member'}},
|
|
)
|
|
|
|
def render_name(self, record, bound_column):
|
|
if TABLES_MAJOR_VERSION >= 2:
|
|
content = bound_column.column.render(record, record.name)
|
|
else:
|
|
content = bound_column.column.render(record.name, record, bound_column)
|
|
if not record.can_manage_members:
|
|
content = SafeText('%s (%s)' % (content, _('LDAP')))
|
|
return content
|
|
|
|
class Meta(Table.Meta):
|
|
model = Role
|
|
attrs = {'class': 'main clickable-rows', 'id': 'role-table'}
|
|
fields = ('name', 'ou')
|
|
empty_text = _('None')
|
|
order_by = ('name',)
|
|
|
|
|
|
class UserRolesTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-role-members',
|
|
kwargs={'pk': A('pk')},
|
|
accessor='name',
|
|
verbose_name=_('label'),
|
|
attrs={'td': {'class': 'name'}},
|
|
)
|
|
ou = tables.Column()
|
|
via = tables.TemplateColumn(
|
|
'{% if not record.member %}{% for rel in record.child_relation.all %}'
|
|
'{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}',
|
|
verbose_name=_('Inherited from'),
|
|
orderable=False,
|
|
attrs={'td': {'class': 'via'}},
|
|
)
|
|
|
|
def render_name(self, record, bound_column):
|
|
if TABLES_MAJOR_VERSION >= 2:
|
|
content = bound_column.column.render(record, record.name)
|
|
else:
|
|
content = bound_column.column.render(record.name, record, bound_column)
|
|
if not record.can_manage_members:
|
|
content = SafeText('%s (%s)' % (content, _('LDAP')))
|
|
return content
|
|
|
|
class Meta(Table.Meta):
|
|
model = Role
|
|
attrs = {'class': 'main clickable-rows', 'id': 'role-table'}
|
|
fields = ('name', 'ou')
|
|
empty_text = _('None')
|
|
order_by = ('name', 'ou')
|
|
|
|
|
|
class ServiceTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-service',
|
|
kwargs={'service_pk': A('pk')},
|
|
accessor='name',
|
|
verbose_name=_('label'),
|
|
)
|
|
ou = tables.Column()
|
|
slug = tables.Column()
|
|
|
|
class Meta(Table.Meta):
|
|
model = Service
|
|
attrs = {'class': 'main clickable-rows', 'id': 'service-table'}
|
|
fields = ('name', 'slug', 'ou')
|
|
empty_text = _('None')
|
|
order_by = ('ou', 'name', 'slug')
|
|
|
|
|
|
class ServiceRolesTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-role-members', kwargs={'pk': A('pk')}, accessor='name', verbose_name=_('label')
|
|
)
|
|
|
|
class Meta(Table.Meta):
|
|
model = Role
|
|
attrs = {'class': 'main clickable-rows', 'id': 'service-role-table'}
|
|
fields = ('name',)
|
|
empty_text = _('No access restriction. All users are allowed to connect to this service.')
|
|
|
|
|
|
class UserAuthorizationsTable(Table):
|
|
client = tables.Column(orderable=False)
|
|
created = tables.Column()
|
|
expired = tables.Column()
|
|
|
|
class Meta(Table.Meta):
|
|
model = OIDCAuthorization
|
|
attrs = {'class': 'main', 'id': 'user-authorizations-table'}
|
|
fields = ('client', 'created', 'expired')
|
|
empty_text = _('This user has not granted profile data access to any service yet.')
|
|
|
|
|
|
class InheritanceRolesTable(Table):
|
|
name = tables.LinkColumn(
|
|
viewname='a2-manager-role-members',
|
|
kwargs={'pk': A('pk')},
|
|
accessor='name',
|
|
verbose_name=_('label'),
|
|
attrs={'td': {'class': 'name'}},
|
|
)
|
|
via = tables.TemplateColumn(
|
|
'''{% for rel in record.via %}{{ rel.name }}{% if not forloop.last %}, {% endif %}{% endfor %}''',
|
|
verbose_name=_('Inherited from'),
|
|
orderable=False,
|
|
attrs={'td': {'class': 'via'}},
|
|
)
|
|
member = tables.TemplateColumn(
|
|
'<input class="role-member{% if record.indeterminate %} indeterminate{% endif %}" name="role-{{ record.pk }}" '
|
|
'type="checkbox" {% if record.checked %}checked{% endif %}/>',
|
|
verbose_name='',
|
|
attrs={'td': {'class': 'member'}},
|
|
)
|
|
|
|
class Meta(Table.Meta):
|
|
model = Role
|
|
attrs = {'class': 'main clickable-rows', 'id': 'inheritance-role-table'}
|
|
fields = ('name', 'ou')
|
|
empty_text = _('None')
|