Add a public rights page to view which permissions are granted to which role, update Font Awesome to 5.13

This commit is contained in:
Yohann D'ANELLO 2020-04-26 01:20:46 +02:00
parent b0f6ec1061
commit a83ab4bf85
8 changed files with 175 additions and 29 deletions

View File

@ -305,14 +305,15 @@ class RolePermissions(models.Model):
""" """
Permissions associated with a Role Permissions associated with a Role
""" """
role = models.ForeignKey( role = models.OneToOneField(
Role, Role,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='+', related_name='permissions',
verbose_name=_('role'), verbose_name=_('role'),
) )
permissions = models.ManyToManyField( permissions = models.ManyToManyField(
Permission, Permission,
verbose_name=_("permissions"),
) )
def __str__(self): def __str__(self):

10
apps/permission/urls.py Normal file
View File

@ -0,0 +1,10 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.urls import path
from permission.views import RightsView
app_name = 'permission'
urlpatterns = [
path('rights', RightsView.as_view(), name="rights"),
]

View File

@ -1,14 +1,18 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date
from django.forms import HiddenInput from django.forms import HiddenInput
from django.views.generic import UpdateView from django.utils.translation import gettext_lazy as _
from django.views.generic import UpdateView, TemplateView
from member.models import Role, Membership
from .backends import PermissionBackend from .backends import PermissionBackend
class ProtectQuerysetMixin: class ProtectQuerysetMixin:
""" """
This is a View class decorator and not a proper View class.
Ensure that the user has the right to see or update objects. Ensure that the user has the right to see or update objects.
Display 404 error if the user can't see an object, remove the fields the user can't Display 404 error if the user can't see an object, remove the fields the user can't
update on an update form (useful if the user can't change only specified fields). update on an update form (useful if the user can't change only specified fields).
@ -32,3 +36,25 @@ class ProtectQuerysetMixin:
form.fields[key].widget = HiddenInput() form.fields[key].widget = HiddenInput()
return form return form
class RightsView(TemplateView):
template_name = "permission/all_rights.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("All rights")
roles = Role.objects.all()
context["roles"] = roles
if self.request.user.is_authenticated:
active_memberships = Membership.objects.filter(user=self.request.user,
date_start__lte=date.today(),
date_end__gte=date.today()).all()
else:
active_memberships = Membership.objects.none()
for role in roles:
role.clubs = [membership.club for membership in active_memberships if role in membership.roles.all()]
return context

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-25 19:12+0200\n" "POT-Creation-Date: 2020-04-26 00:45+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -856,7 +856,7 @@ msgstr ""
msgid "permission" msgid "permission"
msgstr "" msgstr ""
#: apps/permission/models.py:182 #: apps/permission/models.py:182 apps/permission/models.py:316
msgid "permissions" msgid "permissions"
msgstr "" msgstr ""
@ -864,7 +864,7 @@ msgstr ""
msgid "Specifying field applies only to view and change permission types." msgid "Specifying field applies only to view and change permission types."
msgstr "" msgstr ""
#: apps/permission/models.py:322 apps/permission/models.py:323 #: apps/permission/models.py:323 apps/permission/models.py:324
msgid "role permissions" msgid "role permissions"
msgstr "" msgstr ""
@ -882,13 +882,17 @@ msgid ""
"{model_name}." "{model_name}."
msgstr "" msgstr ""
#: apps/permission/signals.py:100 #: apps/permission/signals.py:99
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"You don't have the permission to delete this instance of model {app_label}." "You don't have the permission to delete this instance of model {app_label}."
"{model_name}." "{model_name}."
msgstr "" msgstr ""
#: apps/permission/views.py:47
msgid "All rights"
msgstr ""
#: apps/registration/apps.py:10 #: apps/registration/apps.py:10
msgid "registration" msgid "registration"
msgstr "" msgstr ""
@ -1517,7 +1521,11 @@ msgstr ""
msgid "Registrations" msgid "Registrations"
msgstr "" msgstr ""
#: templates/base.html:155 #: templates/base.html:120
msgid "Rights"
msgstr ""
#: templates/base.html:158
msgid "" msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click " "Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link." "on the validation link."
@ -1729,6 +1737,22 @@ msgstr ""
msgid "Unable to delete button " msgid "Unable to delete button "
msgstr "" msgstr ""
#: templates/permission/all_rights.html:10
msgid "Filter with roles that I have in at least one club"
msgstr ""
#: templates/permission/all_rights.html:21
msgid "Own this role in the clubs"
msgstr ""
#: templates/permission/all_rights.html:26
msgid "Query:"
msgstr ""
#: templates/permission/all_rights.html:28
msgid "No associated permission"
msgstr ""
#: templates/registration/email_validation_complete.html:6 #: templates/registration/email_validation_complete.html:6
msgid "Your email have successfully been validated." msgid "Your email have successfully been validated."
msgstr "" msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-25 19:12+0200\n" "POT-Creation-Date: 2020-04-26 00:45+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -864,7 +864,7 @@ msgstr "champ"
msgid "permission" msgid "permission"
msgstr "permission" msgstr "permission"
#: apps/permission/models.py:182 #: apps/permission/models.py:182 apps/permission/models.py:316
msgid "permissions" msgid "permissions"
msgstr "permissions" msgstr "permissions"
@ -874,7 +874,7 @@ msgstr ""
"Spécifie le champ concerné, ne fonctionne que pour les permissions view et " "Spécifie le champ concerné, ne fonctionne que pour les permissions view et "
"change." "change."
#: apps/permission/models.py:322 apps/permission/models.py:323 #: apps/permission/models.py:323 apps/permission/models.py:324
msgid "role permissions" msgid "role permissions"
msgstr "Permissions par rôles" msgstr "Permissions par rôles"
@ -896,7 +896,7 @@ msgstr ""
"Vous n'avez pas la permission d'ajouter cette instance du modèle {app_label}." "Vous n'avez pas la permission d'ajouter cette instance du modèle {app_label}."
"{model_name}." "{model_name}."
#: apps/permission/signals.py:100 #: apps/permission/signals.py:99
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"You don't have the permission to delete this instance of model {app_label}." "You don't have the permission to delete this instance of model {app_label}."
@ -905,6 +905,10 @@ msgstr ""
"Vous n'avez pas la permission de supprimer cette instance du modèle " "Vous n'avez pas la permission de supprimer cette instance du modèle "
"{app_label}.{model_name}." "{app_label}.{model_name}."
#: apps/permission/views.py:47
msgid "All rights"
msgstr "Tous les droits"
#: apps/registration/apps.py:10 #: apps/registration/apps.py:10
msgid "registration" msgid "registration"
msgstr "inscription" msgstr "inscription"
@ -1566,7 +1570,11 @@ msgstr "Clubs"
msgid "Registrations" msgid "Registrations"
msgstr "Inscriptions" msgstr "Inscriptions"
#: templates/base.html:155 #: templates/base.html:120
msgid "Rights"
msgstr "Droits"
#: templates/base.html:158
msgid "" msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click " "Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link." "on the validation link."
@ -1783,6 +1791,22 @@ msgstr "Le bouton a bien été supprimé"
msgid "Unable to delete button " msgid "Unable to delete button "
msgstr "Impossible de supprimer le bouton " msgstr "Impossible de supprimer le bouton "
#: templates/permission/all_rights.html:10
msgid "Filter with roles that I have in at least one club"
msgstr "Filtrer les rôles que je possède dans au moins un club"
#: templates/permission/all_rights.html:21
msgid "Own this role in the clubs"
msgstr "Possède ce rôle dans les clubs"
#: templates/permission/all_rights.html:26
msgid "Query:"
msgstr "Requête :"
#: templates/permission/all_rights.html:28
msgid "No associated permission"
msgstr "Pas de permission associée"
#: templates/registration/email_validation_complete.html:6 #: templates/registration/email_validation_complete.html:6
msgid "Your email have successfully been validated." msgid "Your email have successfully been validated."
msgstr "Votre adresse e-mail a bien été validée." msgstr "Votre adresse e-mail a bien été validée."

View File

@ -25,10 +25,11 @@ urlpatterns = [
# Include Django Contrib and Core routers # Include Django Contrib and Core routers
path('i18n/', include('django.conf.urls.i18n')), path('i18n/', include('django.conf.urls.i18n')),
path('admin/doc/', include('django.contrib.admindocs.urls')), path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls, name="admin"),
path('accounts/login/', CustomLoginView.as_view()), path('accounts/login/', CustomLoginView.as_view()),
path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django.contrib.auth.urls')),
path('api/', include('api.urls')), path('api/', include('api.urls')),
path('permission/', include('permission.urls')),
] ]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -31,8 +31,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"> crossorigin="anonymous">
<link rel="stylesheet" <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css">
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/v4-shims.css">
{# JQuery, Bootstrap and Turbolinks JavaScript #} {# JQuery, Bootstrap and Turbolinks JavaScript #}
<script src="https://code.jquery.com/jquery-3.4.1.min.js" <script src="https://code.jquery.com/jquery-3.4.1.min.js"
@ -76,44 +76,52 @@ SPDX-License-Identifier: GPL-3.0-or-later
<ul class="navbar-nav"> <ul class="navbar-nav">
{% if "note.transactiontemplate"|not_empty_model_list %} {% if "note.transactiontemplate"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'note:consos' %}"><i class="fa fa-coffee"></i> {% trans 'Consumptions' %}</a> <a class="nav-link" href="{% url 'note:consos' %}"><i class="fas fa-coffee"></i> {% trans 'Consumptions' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if "note.transaction"|not_empty_model_list %} {% if "note.transaction"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a> <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fas fa-exchange-alt"></i>{% trans 'Transfer' %} </a>
</li> </li>
{% endif %} {% endif %}
{% if "auth.user"|model_list|length >= 2 %} {% if "auth.user"|model_list|length >= 2 %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'member:user_list' %}"><i class="fa fa-user"></i> {% trans 'Users' %}</a> <a class="nav-link" href="{% url 'member:user_list' %}"><i class="fas fa-user"></i> {% trans 'Users' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if "member.club"|not_empty_model_list %} {% if "member.club"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> {% trans 'Clubs' %}</a> <a class="nav-link" href="{% url 'member:club_list' %}"><i class="fas fa-users"></i> {% trans 'Clubs' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if "member.change_profile_registration_valid"|has_perm:user %} {% if "member.change_profile_registration_valid"|has_perm:user %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'registration:future_user_list' %}"> <a class="nav-link" href="{% url 'registration:future_user_list' %}">
<i class="fa fa-user-plus"></i> {% trans "Registrations" %} <i class="fas fa-user-plus"></i> {% trans "Registrations" %}
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if "activity.activity"|not_empty_model_list %} {% if "activity.activity"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'activity:activity_list' %}"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a> <a class="nav-link" href="{% url 'activity:activity_list' %}"><i class="fas fa-calendar"></i> {% trans 'Activities' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if "treasury.invoice"|not_empty_model_list %} {% if "treasury.invoice"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'treasury:invoice_list' %}"><i class="fa fa-money"></i> {% trans 'Treasury' %}</a> <a class="nav-link" href="{% url 'treasury:invoice_list' %}"><i class="fas fa-credit-card"></i> {% trans 'Treasury' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if "wei.weiclub"|not_empty_model_list %} {% if "wei.weiclub"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'wei:current_wei_detail' %}"><i class="fa fa-bus"></i> {% trans 'WEI' %}</a> <a class="nav-link" href="{% url 'wei:current_wei_detail' %}"><i class="fas fa-bus"></i> {% trans 'WEI' %}</a>
</li>
{% endif %}
<li class="nav-item active">
<a class="nav-link" href="{% url 'permission:rights' %}"><i class="fas fa-balance-scale"></i> {% trans 'Rights' %}</a>
</li>
{% if user.is_staff %}
<li class="nav-item active">
<a class="nav-link" href="{% url 'admin:index' %}"><i class="fas fa-user-cog"></i> {% trans 'Administration' %}</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
@ -121,28 +129,28 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="dropdown"> <li class="dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-user"></i> <i class="fas fa-user"></i>
<span id="user_balance">{{ user.username }} ({{ user.note.balance | pretty_money }})</span> <span id="user_balance">{{ user.username }} ({{ user.note.balance | pretty_money }})</span>
</a> </a>
<div class="dropdown-menu dropdown-menu-right" <div class="dropdown-menu dropdown-menu-right"
aria-labelledby="navbarDropdownMenuLink"> aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="{% url 'member:user_detail' pk=user.pk %}"> <a class="dropdown-item" href="{% url 'member:user_detail' pk=user.pk %}">
<i class="fa fa-user"></i> Mon compte <i class="fas fa-user"></i> Mon compte
</a> </a>
<a class="dropdown-item" href="{% url 'logout' %}"> <a class="dropdown-item" href="{% url 'logout' %}">
<i class="fa fa-sign-out"></i> Se déconnecter <i class="fas fa-sign-out-alt"></i> Se déconnecter
</a> </a>
</div> </div>
</li> </li>
{% else %} {% else %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'registration:signup' %}"> <a class="nav-link" href="{% url 'registration:signup' %}">
<i class="fa fa-user-plus"></i> S'inscrire <i class="fas fa-user-plus"></i> S'inscrire
</a> </a>
</li> </li>
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'login' %}"> <a class="nav-link" href="{% url 'login' %}">
<i class="fa fa-sign-in"></i> Se connecter <i class="fas fa-sign-in-alt"></i> Se connecter
</a> </a>
</li> </li>
{% endif %} {% endif %}

View File

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
{% if user.is_authenticated %}
<div class="form-check">
<label for="owned_only" class="form-check-label">
<input id="owned_only" name="owned_only" type="checkbox" class="checkboxinput form-check-input">
{% trans "Filter with roles that I have in at least one club" %}
</label>
</div>
{% endif %}
<ul>
{% regroup active_memberships by roles as memberships_per_role %}
{% for role in roles %}
<li class="{% if not role.clubs %}no-club{% endif %}">
{{ role }} {% if role.weirole %}(<em>Pour le WEI</em>){% endif %}
{% if role.clubs %}
<div class="alert alert-success">
{% trans "Own this role in the clubs" %} {{ role.clubs|join:", " }}
</div>
{% endif %}
<ul>
{% for permission in role.permissions.permissions.all %}
<li data-toggle="tooltip" title="{% trans "Query:" %} {{ permission.query }}">{{ permission }} ({{ permission.type }} {{ permission.model }})</li>
{% empty %}
<em>{% trans "No associated permission" %}</em>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block extrajavascript %}
<script>
$(document).ready(function() {
let checkbox = $("#owned_only");
function update() {
if (checkbox.is(":checked"))
$(".no-club").addClass('d-none');
else
$(".no-club").removeClass('d-none');
}
checkbox.change(update);
update();
});
</script>
{% endblock %}