@@ -41,11 +43,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ article.duration }}
{{ article.type_user }}
- {% if is_trez %}
+ {% can_edit article %}
- {% endif %}
+ {% acl_end %}
diff --git a/cotisations/templates/cotisations/aff_banque.html b/cotisations/templates/cotisations/aff_banque.html
index fd962f1f..f31bf55a 100644
--- a/cotisations/templates/cotisations/aff_banque.html
+++ b/cotisations/templates/cotisations/aff_banque.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -33,11 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ banque.name }}
- {% if is_trez %}
+ {% can_edit banque %}
- {% endif %}
+ {% acl_end %}
diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html
index 34efd63d..2e6eea3f 100644
--- a/cotisations/templates/cotisations/aff_cotisations.html
+++ b/cotisations/templates/cotisations/aff_cotisations.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
{% if facture_list.paginator %}
{% include "pagination.html" with list=facture_list %}
{% endif %}
@@ -47,7 +49,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ facture.paiement }}
{{ facture.date }}
{{ facture.id }}
- {% if is_cableur %}
@@ -55,17 +56,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% endif %}
{% if facture.valid %}
diff --git a/cotisations/templates/cotisations/aff_paiement.html b/cotisations/templates/cotisations/aff_paiement.html
index 5cf8d176..f2f9abe3 100644
--- a/cotisations/templates/cotisations/aff_paiement.html
+++ b/cotisations/templates/cotisations/aff_paiement.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -33,11 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ paiement.moyen }}
- {% if is_trez %}
-
+ {% can_edit paiement %}
+
- {% endif %}
+ {% acl_end %}
diff --git a/cotisations/templates/cotisations/index_article.html b/cotisations/templates/cotisations/index_article.html
index ee9f3db1..edc71c09 100644
--- a/cotisations/templates/cotisations/index_article.html
+++ b/cotisations/templates/cotisations/index_article.html
@@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Articles{% endblock %}
{% block content %}
Liste des types d'articles
- {% if is_trez %}
+ {% can_create Article %}
Ajouter un type d'articles
+ {% acl_end %}
Supprimer un ou plusieurs types d'articles
- {% endif %}
{% include "cotisations/aff_article.html" with article_list=article_list %}
diff --git a/cotisations/templates/cotisations/index_banque.html b/cotisations/templates/cotisations/index_banque.html
index 142c163f..dda89843 100644
--- a/cotisations/templates/cotisations/index_banque.html
+++ b/cotisations/templates/cotisations/index_banque.html
@@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Banques{% endblock %}
{% block content %}
Liste des banques
+ {% can_create Banque %}
Ajouter une banque
- {% if is_trez %}
+ {% acl_end %}
Supprimer une ou plusieurs banques
- {% endif %}
{% include "cotisations/aff_banque.html" with banque_list=banque_list %}
diff --git a/cotisations/templates/cotisations/index_paiement.html b/cotisations/templates/cotisations/index_paiement.html
index d2734e46..a27cb824 100644
--- a/cotisations/templates/cotisations/index_paiement.html
+++ b/cotisations/templates/cotisations/index_paiement.html
@@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Paiements{% endblock %}
{% block content %}
Liste des types de paiements
- {% if is_trez %}
+ {% can_create Paiement %}
Ajouter un type de paiement
+ {% acl_end %}
Supprimer un ou plusieurs types de paiements
- {% endif %}
{% include "cotisations/aff_paiement.html" with paiement_list=paiement_list %}
diff --git a/cotisations/templates/cotisations/sidebar.html b/cotisations/templates/cotisations/sidebar.html
index 1860b1ee..3506e5e8 100644
--- a/cotisations/templates/cotisations/sidebar.html
+++ b/cotisations/templates/cotisations/sidebar.html
@@ -23,9 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
{% block sidebar %}
- {% if is_trez %}
+ {% can_change Facture pdf %}
Créer une facture
@@ -34,21 +35,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Contrôler les factures
- {% endif %}
+ {% acl_end %}
+ {% can_view_all Facture %}
Factures
+ {% acl_end %}
+ {% can_view_all Article %}
Articles en vente
+ {% acl_end %}
+ {% can_view_all Banque %}
Banques
+ {% acl_end %}
+ {% can_view_all Paiement %}
Moyens de paiement
+ {% acl_end %}
{% endblock %}
diff --git a/cotisations/urls.py b/cotisations/urls.py
index d3e56f36..2a0c0163 100644
--- a/cotisations/urls.py
+++ b/cotisations/urls.py
@@ -24,6 +24,7 @@ from __future__ import unicode_literals
from django.conf.urls import url
+import re2o
from . import views
urlpatterns = [
@@ -99,21 +100,12 @@ urlpatterns = [
views.index_paiement,
name='index-paiement'
),
- url(r'^history/(?Pfacture)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(r'^history/(?Particle)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(r'^history/(?Ppaiement)/(?P[0-9]+)$',
- views.history,
- name='history'),
- url(r'^history/(?Pbanque)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
+ url(
+ r'history/(?P\w+)/(?P[0-9]+)$',
+ re2o.views.history,
+ name='history',
+ kwargs={'application':'cotisations'},
+ ),
url(r'^control/$',
views.control,
name='control'
diff --git a/cotisations/views.py b/cotisations/views.py
index 1a9b8ed9..d7d953c9 100644
--- a/cotisations/views.py
+++ b/cotisations/views.py
@@ -44,11 +44,19 @@ from re2o.settings import LOGO_PATH
from re2o import settings
from re2o.views import form
from re2o.utils import SortTable
+from re2o.acl import (
+ can_create,
+ can_edit,
+ can_delete,
+ can_view,
+ can_view_all,
+ can_delete_set,
+ can_change,
+)
from preferences.models import OptionalUser, AssoOption, GeneralOption
from .models import Facture, Article, Vente, Paiement, Banque
from .forms import (
NewFactureForm,
- TrezEditFactureForm,
EditFactureForm,
ArticleForm,
DelArticleForm,
@@ -64,9 +72,11 @@ from .forms import (
from .tex import render_invoice
+
@login_required
-@permission_required('cableur')
-def new_facture(request, userid):
+@can_create(Facture)
+@can_edit(User)
+def new_facture(request, user, userid):
"""Creation d'une facture pour un user. Renvoie la liste des articles
et crée des factures dans un formset. Utilise un peu de js coté template
pour ajouter des articles.
@@ -74,11 +84,6 @@ def new_facture(request, userid):
enfin sauve la facture parente.
TODO : simplifier cette fonction, déplacer l'intelligence coté models
Facture et Vente."""
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, u"Utilisateur inexistant")
- return redirect(reverse('cotisations:index'))
facture = Facture(user=user)
# Le template a besoin de connaitre les articles pour le js
article_list = Article.objects.filter(
@@ -163,7 +168,7 @@ def new_facture(request, userid):
@login_required
-@permission_required('tresorier')
+@can_change(Facture, 'pdf')
def new_facture_pdf(request):
"""Permet de générer un pdf d'une facture. Réservée
au trésorier, permet d'emettre des factures sans objet
@@ -203,31 +208,13 @@ def new_facture_pdf(request):
@login_required
-def facture_pdf(request, factureid):
+@can_view(Facture)
+def facture_pdf(request, facture, factureid):
"""Affiche en pdf une facture. Cree une ligne par Vente de la facture,
et génére une facture avec le total, le moyen de paiement, l'adresse
de l'adhérent, etc. Réservée à self pour un user sans droits,
les droits cableurs permettent d'afficher toute facture"""
- try:
- facture = Facture.objects.get(pk=factureid)
- except Facture.DoesNotExist:
- messages.error(request, u"Facture inexistante")
- return redirect(reverse('cotisations:index'))
- if not request.user.has_perms(('cableur',))\
- and facture.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher une facture ne vous\
- appartenant pas sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid': str(request.user.id)}
- ))
- if not facture.valid:
- messages.error(request, "Vous ne pouvez pas afficher\
- une facture non valide")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid': str(request.user.id)}
- ))
+
ventes_objects = Vente.objects.all().filter(facture=facture)
ventes = []
options, _created = AssoOption.objects.get_or_create()
@@ -251,27 +238,12 @@ def facture_pdf(request, factureid):
@login_required
-@permission_required('cableur')
-def edit_facture(request, factureid):
+@can_edit(Facture)
+def edit_facture(request, facture, factureid):
"""Permet l'édition d'une facture. On peut y éditer les ventes
déjà effectuer, ou rendre une facture invalide (non payées, chèque
en bois etc). Mets à jour les durée de cotisation attenantes"""
- try:
- facture = Facture.objects.get(pk=factureid)
- except Facture.DoesNotExist:
- messages.error(request, u"Facture inexistante")
- return redirect(reverse('cotisations:index'))
- if request.user.has_perms(['tresorier']):
- facture_form = TrezEditFactureForm(
- request.POST or None,
- instance=facture
- )
- elif facture.control or not facture.valid:
- messages.error(request, "Vous ne pouvez pas editer une facture\
- controlée ou invalidée par le trésorier")
- return redirect(reverse('cotisations:index'))
- else:
- facture_form = EditFactureForm(request.POST or None, instance=facture)
+ facture_form = EditFactureForm(request.POST or None, instance=facture, user=request.user)
ventes_objects = Vente.objects.filter(facture=facture)
vente_form_set = modelformset_factory(
Vente,
@@ -297,19 +269,10 @@ def edit_facture(request, factureid):
@login_required
-@permission_required('cableur')
-def del_facture(request, factureid):
+@can_delete(Facture)
+def del_facture(request, facture, factureid):
"""Suppression d'une facture. Supprime en cascade les ventes
et cotisations filles"""
- try:
- facture = Facture.objects.get(pk=factureid)
- except Facture.DoesNotExist:
- messages.error(request, u"Facture inexistante")
- return redirect(reverse('cotisations:index'))
- if facture.control or not facture.valid:
- messages.error(request, "Vous ne pouvez pas editer une facture\
- controlée ou invalidée par le trésorier")
- return redirect(reverse('cotisations:index'))
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
facture.delete()
@@ -323,14 +286,10 @@ def del_facture(request, factureid):
@login_required
-@permission_required('cableur')
-def credit_solde(request, userid):
+@can_create(Facture)
+@can_edit(User)
+def credit_solde(request, user, userid):
""" Credit ou débit de solde """
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, u"Utilisateur inexistant")
- return redirect(reverse('cotisations:index'))
facture = CreditSoldeForm(request.POST or None)
if facture.is_valid():
facture_instance = facture.save(commit=False)
@@ -355,7 +314,7 @@ def credit_solde(request, userid):
@login_required
-@permission_required('tresorier')
+@can_create(Article)
def add_article(request):
"""Ajoute un article. Champs : désignation,
prix, est-ce une cotisation et si oui sa durée
@@ -376,15 +335,10 @@ def add_article(request):
@login_required
-@permission_required('tresorier')
-def edit_article(request, articleid):
+@can_edit(Article)
+def edit_article(request, article_instance, articleid):
"""Edition d'un article (designation, prix, etc)
Réservé au trésorier"""
- try:
- article_instance = Article.objects.get(pk=articleid)
- except Article.DoesNotExist:
- messages.error(request, u"Entrée inexistante")
- return redirect(reverse('cotisations:index-article'))
article = ArticleForm(request.POST or None, instance=article_instance)
if article.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -401,10 +355,10 @@ def edit_article(request, articleid):
@login_required
-@permission_required('tresorier')
-def del_article(request):
+@can_delete_set(Article)
+def del_article(request, instances):
"""Suppression d'un article en vente"""
- article = DelArticleForm(request.POST or None)
+ article = DelArticleForm(request.POST or None, instances=instances)
if article.is_valid():
article_del = article.cleaned_data['articles']
with transaction.atomic(), reversion.create_revision():
@@ -416,7 +370,7 @@ def del_article(request):
@login_required
-@permission_required('tresorier')
+@can_create(Paiement)
def add_paiement(request):
"""Ajoute un moyen de paiement. Relié aux factures
via foreign key"""
@@ -432,14 +386,9 @@ def add_paiement(request):
@login_required
-@permission_required('tresorier')
-def edit_paiement(request, paiementid):
+@can_edit(Paiement)
+def edit_paiement(request, paiement_instance, paiementid):
"""Edition d'un moyen de paiement"""
- try:
- paiement_instance = Paiement.objects.get(pk=paiementid)
- except Paiement.DoesNotExist:
- messages.error(request, u"Entrée inexistante")
- return redirect(reverse('cotisations:index-paiement'))
paiement = PaiementForm(request.POST or None, instance=paiement_instance)
if paiement.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -456,10 +405,10 @@ def edit_paiement(request, paiementid):
@login_required
-@permission_required('tresorier')
-def del_paiement(request):
+@can_delete_set(Paiement)
+def del_paiement(request, instances):
"""Suppression d'un moyen de paiement"""
- paiement = DelPaiementForm(request.POST or None)
+ paiement = DelPaiementForm(request.POST or None, instances=instances)
if paiement.is_valid():
paiement_dels = paiement.cleaned_data['paiements']
for paiement_del in paiement_dels:
@@ -483,7 +432,7 @@ def del_paiement(request):
@login_required
-@permission_required('cableur')
+@can_create(Banque)
def add_banque(request):
"""Ajoute une banque à la liste des banques"""
banque = BanqueForm(request.POST or None)
@@ -498,14 +447,9 @@ def add_banque(request):
@login_required
-@permission_required('tresorier')
-def edit_banque(request, banqueid):
+@can_edit(Banque)
+def edit_banque(request, banque_instance, banqueid):
"""Edite le nom d'une banque"""
- try:
- banque_instance = Banque.objects.get(pk=banqueid)
- except Banque.DoesNotExist:
- messages.error(request, u"Entrée inexistante")
- return redirect(reverse('cotisations:index-banque'))
banque = BanqueForm(request.POST or None, instance=banque_instance)
if banque.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -522,10 +466,10 @@ def edit_banque(request, banqueid):
@login_required
-@permission_required('tresorier')
-def del_banque(request):
+@can_delete_set(Banque)
+def del_banque(request, instances):
"""Supprime une banque"""
- banque = DelBanqueForm(request.POST or None)
+ banque = DelBanqueForm(request.POST or None, instances=instances)
if banque.is_valid():
banque_dels = banque.cleaned_data['banques']
for banque_del in banque_dels:
@@ -543,7 +487,8 @@ def del_banque(request):
@login_required
-@permission_required('tresorier')
+@can_view_all(Facture)
+@can_change(Facture, 'control')
def control(request):
"""Pour le trésorier, vue pour controler en masse les
factures.Case à cocher, pratique"""
@@ -583,7 +528,7 @@ def control(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Article)
def index_article(request):
"""Affiche l'ensemble des articles en vente"""
article_list = Article.objects.order_by('name')
@@ -593,7 +538,7 @@ def index_article(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Paiement)
def index_paiement(request):
"""Affiche l'ensemble des moyens de paiement en vente"""
paiement_list = Paiement.objects.order_by('moyen')
@@ -603,7 +548,7 @@ def index_paiement(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Banque)
def index_banque(request):
"""Affiche l'ensemble des banques"""
banque_list = Banque.objects.order_by('name')
@@ -613,7 +558,7 @@ def index_banque(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Facture)
def index(request):
"""Affiche l'ensemble des factures, pour les cableurs et +"""
options, _created = GeneralOption.objects.get_or_create()
@@ -639,60 +584,3 @@ def index(request):
return render(request, 'cotisations/index.html', {
'facture_list': facture_list
})
-
-
-@login_required
-def history(request, object_name, object_id):
- """Affiche l'historique de chaque objet"""
- if object_name == 'facture':
- try:
- object_instance = Facture.objects.get(pk=object_id)
- except Facture.DoesNotExist:
- messages.error(request, "Facture inexistante")
- return redirect(reverse('cotisations:index'))
- if not request.user.has_perms(('cableur',))\
- and object_instance.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher l'historique\
- d'une facture d'un autre user que vous sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object_name == 'paiement' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Paiement.objects.get(pk=object_id)
- except Paiement.DoesNotExist:
- messages.error(request, "Paiement inexistant")
- return redirect(reverse('cotisations:index'))
- elif object_name == 'article' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Article.objects.get(pk=object_id)
- except Article.DoesNotExist:
- messages.error(request, "Article inexistante")
- return redirect(reverse('cotisations:index'))
- elif object_name == 'banque' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Banque.objects.get(pk=object_id)
- except Banque.DoesNotExist:
- messages.error(request, "Banque inexistante")
- return redirect(reverse('cotisations:index'))
- else:
- messages.error(request, "Objet inconnu")
- return redirect(reverse('cotisations:index'))
- options, _created = GeneralOption.objects.get_or_create()
- pagination_number = options.pagination_number
- reversions = Version.objects.get_for_object(object_instance)
- paginator = Paginator(reversions, pagination_number)
- page = request.GET.get('page')
- try:
- reversions = paginator.page(page)
- except PageNotAnInteger:
- # If page is not an integer, deliver first page.
- reversions = paginator.page(1)
- except EmptyPage:
- # If page is out of range (e.g. 9999), deliver last page of results.
- reversions = paginator.page(paginator.num_pages)
- return render(request, 're2o/history.html', {
- 'reversions': reversions,
- 'object': object_instance
- })
diff --git a/logs/__init__.py b/logs/__init__.py
index fc1be5d7..df6e4256 100644
--- a/logs/__init__.py
+++ b/logs/__init__.py
@@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from .acl import *
diff --git a/logs/acl.py b/logs/acl.py
new file mode 100644
index 00000000..4a1417f6
--- /dev/null
+++ b/logs/acl.py
@@ -0,0 +1,40 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""logs.acl
+
+Here are defined some functions to check acl on the application.
+"""
+
+def can_view(user):
+ """Check if an user can view the application.
+
+ Args:
+ user: The user who wants to view the application.
+
+ Returns:
+ A couple (allowed, msg) where allowed is a boolean which is True if
+ viewing is granted and msg is a message (can be None).
+ """
+ can = user.has_module_perms('admin')
+ return can, None if can else "Vous ne pouvez pas voir cette application."
diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html
index 08178a38..be0b5a41 100644
--- a/logs/templates/logs/aff_stats_logs.html
+++ b/logs/templates/logs/aff_stats_logs.html
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
{% load logs_extra %}
+{% load acl %}
@@ -47,19 +48,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ revision.user }}
{{ revision.date_created }}
{{ revision.comment }}
- {% if is_bureau %}
+ {% can_edit_history %}
Annuler
- {% endif %}
+ {% acl_end %}
{% endfor %}
{% endfor %}
-
+
{% if revisions_list.paginator %}
{% include "pagination.html" with list=revisions_list %}
{% endif %}
diff --git a/logs/templates/logs/aff_summary.html b/logs/templates/logs/aff_summary.html
index 65f71aca..c6eca27b 100644
--- a/logs/templates/logs/aff_summary.html
+++ b/logs/templates/logs/aff_summary.html
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
{% load logs_extra %}
-
+{% load acl %}
@@ -51,14 +51,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
)
- {% if is_bureau %}
+ {% can_edit_history %}
Annuler
- {% endif %}
+ {% acl_end %}
{% elif v.version.content_type.model == 'whitelist' %}
@@ -74,14 +74,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
)
- {% if is_bureau %}
+ {% can_edit_history%}
Annuler
- {% endif %}
+ {% acl_end %}
{% elif v.version.content_type.model == 'user' %}
@@ -93,14 +93,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
({{ v.comment }} )
{% endif %}
- {% if is_bureau %}
+ {% can_edit_history %}
Annuler
- {% endif %}
+ {% acl_end %}
{% elif v.version.content_type.model == 'vente' %}
@@ -112,14 +112,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
(+{{ v.version.object.duration }} mois )
{% endif %}
- {% if is_bureau %}
+ {% can_edit_history %}
Annuler
- {% endif %}
+ {% acl_end %}
{% elif v.version.content_type.model == 'interface' %}
@@ -131,14 +131,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
({{ v.comment }} )
{% endif %}
- {% if is_bureau %}
+ {% can_edit_history %}
Annuler
- {% endif %}
+ {% acl_end %}
{% endif %}
{% endfor %}
diff --git a/logs/templates/logs/sidebar.html b/logs/templates/logs/sidebar.html
index 4137741f..fe7e63e7 100644
--- a/logs/templates/logs/sidebar.html
+++ b/logs/templates/logs/sidebar.html
@@ -23,9 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
{% block sidebar %}
- {% if is_cableur %}
+ {% can_view_app logs %}
Résumé
@@ -50,5 +51,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Utilisateurs
- {% endif %}
+ {% acl_end %}
{% endblock %}
diff --git a/logs/views.py b/logs/views.py
index 1bd91a97..d899d2f6 100644
--- a/logs/views.py
+++ b/logs/views.py
@@ -41,7 +41,7 @@ from django.urls import reverse
from django.shortcuts import render, redirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib import messages
-from django.contrib.auth.decorators import login_required, permission_required
+from django.contrib.auth.decorators import login_required
from django.db.models import Count
from reversion.models import Revision
@@ -50,7 +50,6 @@ from reversion.models import Version, ContentType
from users.models import (
User,
ServiceUser,
- Right,
School,
ListRight,
ListShell,
@@ -93,7 +92,17 @@ from topologie.models import (
)
from preferences.models import GeneralOption
from re2o.views import form
-from re2o.utils import all_whitelisted, all_baned, all_has_access, all_adherent
+from re2o.utils import (
+ all_whitelisted,
+ all_baned,
+ all_has_access,
+ all_adherent,
+)
+from re2o.acl import (
+ can_view_all,
+ can_view_app,
+ can_edit_history,
+)
from re2o.utils import all_active_assigned_interfaces_count
from re2o.utils import all_active_interfaces_count, SortTable
@@ -108,7 +117,7 @@ STATS_DICT = {
@login_required
-@permission_required('cableur')
+@can_view_app('logs')
def index(request):
"""Affiche les logs affinés, date reformatées, selectionne
les event importants (ajout de droits, ajout de ban/whitelist)"""
@@ -167,7 +176,7 @@ def index(request):
@login_required
-@permission_required('cableur')
+@can_view_all(GeneralOption)
def stats_logs(request):
"""Affiche l'ensemble des logs et des modifications sur les objets,
classés par date croissante, en vrac"""
@@ -197,7 +206,7 @@ def stats_logs(request):
@login_required
-@permission_required('bureau')
+@can_edit_history
def revert_action(request, revision_id):
""" Annule l'action en question """
try:
@@ -215,7 +224,9 @@ def revert_action(request, revision_id):
@login_required
-@permission_required('cableur')
+@can_view_all(IpList)
+@can_view_all(Interface)
+@can_view_all(User)
def stats_general(request):
"""Statistiques générales affinées sur les ip, activées, utilisées par
range, et les statistiques générales sur les users : users actifs,
@@ -298,7 +309,10 @@ def stats_general(request):
@login_required
-@permission_required('cableur')
+@can_view_app('users')
+@can_view_app('cotisations')
+@can_view_app('machines')
+@can_view_app('topologie')
def stats_models(request):
"""Statistiques générales, affiche les comptages par models:
nombre d'users, d'écoles, de droits, de bannissements,
@@ -310,7 +324,6 @@ def stats_models(request):
'clubs': [Club.PRETTY_NAME, Club.objects.count()],
'serviceuser': [ServiceUser.PRETTY_NAME,
ServiceUser.objects.count()],
- 'right': [Right.PRETTY_NAME, Right.objects.count()],
'school': [School.PRETTY_NAME, School.objects.count()],
'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()],
'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()],
@@ -340,7 +353,7 @@ def stats_models(request):
OuverturePortList.objects.count()
],
'vlan': [Vlan.PRETTY_NAME, Vlan.objects.count()],
- 'SOA': [Mx.PRETTY_NAME, Mx.objects.count()],
+ 'SOA': [SOA.PRETTY_NAME, SOA.objects.count()],
'Mx': [Mx.PRETTY_NAME, Mx.objects.count()],
'Ns': [Ns.PRETTY_NAME, Ns.objects.count()],
'nas': [Nas.PRETTY_NAME, Nas.objects.count()],
@@ -368,7 +381,7 @@ def stats_models(request):
@login_required
-@permission_required('cableur')
+@can_view_app('users')
def stats_users(request):
"""Affiche les statistiques base de données aggrégées par user :
nombre de machines par user, d'etablissements par user,
@@ -395,7 +408,7 @@ def stats_users(request):
num=Count('whitelist')
).order_by('-num')[:10],
'Droits': User.objects.annotate(
- num=Count('right')
+ num=Count('groups')
).order_by('-num')[:10],
},
'Etablissement': {
@@ -422,7 +435,7 @@ def stats_users(request):
@login_required
-@permission_required('cableur')
+@can_view_app('users')
def stats_actions(request):
"""Vue qui affiche les statistiques de modifications d'objets par
utilisateurs.
diff --git a/machines/__init__.py b/machines/__init__.py
index fc1be5d7..df6e4256 100644
--- a/machines/__init__.py
+++ b/machines/__init__.py
@@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from .acl import *
diff --git a/machines/acl.py b/machines/acl.py
new file mode 100644
index 00000000..f77a93c7
--- /dev/null
+++ b/machines/acl.py
@@ -0,0 +1,40 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""machines.acl
+
+Here are defined some functions to check acl on the application.
+"""
+
+def can_view(user):
+ """Check if an user can view the application.
+
+ Args:
+ user: The user who wants to view the application.
+
+ Returns:
+ A couple (allowed, msg) where allowed is a boolean which is True if
+ viewing is granted and msg is a message (can be None).
+ """
+ can = user.has_module_perms('machines')
+ return can, None if can else "Vous ne pouvez pas voir cette application."
diff --git a/machines/forms.py b/machines/forms.py
index d6aa5e3e..f23f1d7d 100644
--- a/machines/forms.py
+++ b/machines/forms.py
@@ -38,6 +38,8 @@ from __future__ import unicode_literals
from django.forms import ModelForm, Form
from django import forms
+from re2o.field_permissions import FieldPermissionFormMixin
+
from .models import (
Domain,
Machine,
@@ -58,7 +60,7 @@ from .models import (
)
-class EditMachineForm(ModelForm):
+class EditMachineForm(FieldPermissionFormMixin, ModelForm):
"""Formulaire d'édition d'une machine"""
class Meta:
model = Machine
@@ -117,10 +119,10 @@ class AddInterfaceForm(EditInterfaceForm):
fields = ['type', 'ipv4', 'mac_address', 'details']
def __init__(self, *args, **kwargs):
- infra = kwargs.pop('infra')
+ user = kwargs.pop('user')
super(AddInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
- if not infra:
+ if not IpType.can_use_all(user):
self.fields['type'].queryset = MachineType.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False)
)
@@ -146,13 +148,14 @@ class BaseEditInterfaceForm(EditInterfaceForm):
fields = ['type', 'ipv4', 'mac_address', 'details']
def __init__(self, *args, **kwargs):
- infra = kwargs.pop('infra')
+ user = kwargs.pop('user')
super(BaseEditInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
- if not infra:
+ if not MachineType.can_use_all(user):
self.fields['type'].queryset = MachineType.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False)
)
+ if not IpType.can_use_all(user):
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
@@ -177,9 +180,10 @@ class AliasForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
- infra = kwargs.pop('infra')
+ user = kwargs.pop('user')
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
- if not infra:
+ can_use_all, reason = Extension.can_use_all(user)
+ if not can_use_all:
self.fields['extension'].queryset = Extension.objects.filter(
need_infra=False
)
@@ -233,11 +237,19 @@ class MachineTypeForm(ModelForm):
class DelMachineTypeForm(Form):
"""Suppression d'un ou plusieurs machinetype"""
machinetypes = forms.ModelMultipleChoiceField(
- queryset=MachineType.objects.all(),
+ queryset=MachineType.objects.none(),
label="Types de machines actuelles",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelMachineTypeForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['machinetypes'].queryset = instances
+ else:
+ self.fields['machinetypes'].queryset = MachineType.objects.all()
+
class IpTypeForm(ModelForm):
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de
@@ -264,11 +276,19 @@ class EditIpTypeForm(IpTypeForm):
class DelIpTypeForm(Form):
"""Suppression d'un ou plusieurs iptype"""
iptypes = forms.ModelMultipleChoiceField(
- queryset=IpType.objects.all(),
+ queryset=IpType.objects.none(),
label="Types d'ip actuelles",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelIpTypeForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['iptypes'].queryset = instances
+ else:
+ self.fields['iptypes'].queryset = IpType.objects.all()
+
class ExtensionForm(ModelForm):
"""Formulaire d'ajout et edition d'une extension"""
@@ -288,11 +308,19 @@ class ExtensionForm(ModelForm):
class DelExtensionForm(Form):
"""Suppression d'une ou plusieurs extensions"""
extensions = forms.ModelMultipleChoiceField(
- queryset=Extension.objects.all(),
+ queryset=Extension.objects.none(),
label="Extensions actuelles",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelExtensionForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['extensions'].queryset = instances
+ else:
+ self.fields['extensions'].queryset = Extension.objects.all()
+
class SOAForm(ModelForm):
"""Ajout et edition d'un SOA"""
@@ -308,11 +336,19 @@ class SOAForm(ModelForm):
class DelSOAForm(Form):
"""Suppression d'un ou plusieurs SOA"""
soa = forms.ModelMultipleChoiceField(
- queryset=SOA.objects.all(),
+ queryset=SOA.objects.none(),
label="SOA actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelSOAForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['soa'].queryset = instances
+ else:
+ self.fields['soa'].queryset = SOA.objects.all()
+
class MxForm(ModelForm):
"""Ajout et edition d'un MX"""
@@ -327,15 +363,22 @@ class MxForm(ModelForm):
interface_parent=None
).select_related('extension')
-
class DelMxForm(Form):
"""Suppression d'un ou plusieurs MX"""
mx = forms.ModelMultipleChoiceField(
- queryset=Mx.objects.all(),
+ queryset=Mx.objects.none(),
label="MX actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelMxForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['mx'].queryset = instances
+ else:
+ self.fields['mx'].queryset = Mx.objects.all()
+
class NsForm(ModelForm):
"""Ajout d'un NS pour une zone
@@ -356,11 +399,19 @@ class NsForm(ModelForm):
class DelNsForm(Form):
"""Suppresion d'un ou plusieurs NS"""
ns = forms.ModelMultipleChoiceField(
- queryset=Ns.objects.all(),
+ queryset=Ns.objects.none(),
label="Enregistrements NS actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelNsForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['ns'].queryset = instances
+ else:
+ self.fields['ns'].queryset = Ns.objects.all()
+
class TxtForm(ModelForm):
"""Ajout d'un txt pour une zone"""
@@ -376,12 +427,20 @@ class TxtForm(ModelForm):
class DelTxtForm(Form):
"""Suppression d'un ou plusieurs TXT"""
txt = forms.ModelMultipleChoiceField(
- queryset=Txt.objects.all(),
+ queryset=Txt.objects.none(),
label="Enregistrements Txt actuels",
widget=forms.CheckboxSelectMultiple
)
-
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelTxtForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['txt'].queryset = instances
+ else:
+ self.fields['txt'].queryset = Txt.objects.all()
+
+
class SrvForm(ModelForm):
"""Ajout d'un srv pour une zone"""
class Meta:
@@ -396,11 +455,19 @@ class SrvForm(ModelForm):
class DelSrvForm(Form):
"""Suppression d'un ou plusieurs Srv"""
srv = forms.ModelMultipleChoiceField(
- queryset=Srv.objects.all(),
+ queryset=Srv.objects.none(),
label="Enregistrements Srv actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelSrvForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['srv'].queryset = instances
+ else:
+ self.fields['srv'].queryset = Srv.objects.all()
+
class NasForm(ModelForm):
"""Ajout d'un type de nas (machine d'authentification,
@@ -417,11 +484,19 @@ class NasForm(ModelForm):
class DelNasForm(Form):
"""Suppression d'un ou plusieurs nas"""
nas = forms.ModelMultipleChoiceField(
- queryset=Nas.objects.all(),
+ queryset=Nas.objects.none(),
label="Enregistrements Nas actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelNasForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['nas'].queryset = instances
+ else:
+ self.fields['nas'].queryset = Nas.objects.all()
+
class ServiceForm(ModelForm):
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
@@ -446,11 +521,19 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form):
"""Suppression d'un ou plusieurs service"""
service = forms.ModelMultipleChoiceField(
- queryset=Service.objects.all(),
+ queryset=Service.objects.none(),
label="Services actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelServiceForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['service'].queryset = instances
+ else:
+ self.fields['service'].queryset = Service.objects.all()
+
class VlanForm(ModelForm):
"""Ajout d'un vlan : id, nom"""
@@ -466,11 +549,19 @@ class VlanForm(ModelForm):
class DelVlanForm(Form):
"""Suppression d'un ou plusieurs vlans"""
vlan = forms.ModelMultipleChoiceField(
- queryset=Vlan.objects.all(),
+ queryset=Vlan.objects.none(),
label="Vlan actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelVlanForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['vlan'].queryset = instances
+ else:
+ self.fields['vlan'].queryset = Vlan.objects.all()
+
class EditOuverturePortConfigForm(ModelForm):
"""Edition de la liste des profils d'ouverture de ports
diff --git a/machines/migrations/0070_auto_20171231_1947.py b/machines/migrations/0070_auto_20171231_1947.py
new file mode 100644
index 00000000..ef441d01
--- /dev/null
+++ b/machines/migrations/0070_auto_20171231_1947.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 18:47
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('machines', '0069_auto_20171116_0822'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='domain',
+ options={'permissions': (('view_domain', 'Peut voir un objet domain'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='extension',
+ options={'permissions': (('view_extension', 'Peut voir un objet extension'), ('use_all_extension', 'Peut utiliser toutes les extension'))},
+ ),
+ migrations.AlterModelOptions(
+ name='interface',
+ options={'permissions': (('view_interface', 'Peut voir un objet interface'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='iplist',
+ options={'permissions': (('view_iplist', 'Peut voir un objet iplist'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='iptype',
+ options={'permissions': (('view_iptype', 'Peut voir un objet iptype'), ('use_all_iptype', 'Peut utiliser tous les iptype'))},
+ ),
+ migrations.AlterModelOptions(
+ name='machine',
+ options={'permissions': (('view_machine', 'Peut voir un objet machine quelquonque'), ('change_machine_user', "Peut changer le propriétaire d'une machine"))},
+ ),
+ migrations.AlterModelOptions(
+ name='machinetype',
+ options={'permissions': (('view_machinetype', 'Peut voir un objet machinetype'), ('use_all_machinetype', "Peut utiliser n'importe quel type de machine"))},
+ ),
+ migrations.AlterModelOptions(
+ name='mx',
+ options={'permissions': (('view_mx', 'Peut voir un objet mx'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='nas',
+ options={'permissions': (('view_nas', 'Peut voir un objet Nas'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='ns',
+ options={'permissions': (('view_nx', 'Peut voir un objet nx'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='ouvertureportlist',
+ options={'permissions': (('view_ouvertureportlist', 'Peut voir un objet ouvertureport'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='service',
+ options={'permissions': (('view_service', 'Peut voir un objet service'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='soa',
+ options={'permissions': (('view_soa', 'Peut voir un objet soa'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='srv',
+ options={'permissions': (('view_soa', 'Peut voir un objet soa'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='txt',
+ options={'permissions': (('view_txt', 'Peut voir un objet txt'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='vlan',
+ options={'permissions': (('view_vlan', 'Peut voir un objet vlan'),)},
+ ),
+ ]
diff --git a/machines/migrations/0071_auto_20171231_2100.py b/machines/migrations/0071_auto_20171231_2100.py
new file mode 100644
index 00000000..776edd82
--- /dev/null
+++ b/machines/migrations/0071_auto_20171231_2100.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 20:00
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('machines', '0070_auto_20171231_1947'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='ns',
+ options={'permissions': (('view_ns', 'Peut voir un objet ns'),)},
+ ),
+ ]
diff --git a/machines/models.py b/machines/models.py
index 9bfb4b55..f2dedee9 100644
--- a/machines/models.py
+++ b/machines/models.py
@@ -37,8 +37,13 @@ from django.core.validators import MaxValueValidator
from macaddress.fields import MACAddressField
+from re2o.field_permissions import FieldPermissionModelMixin
-class Machine(models.Model):
+import users.models
+import preferences.models
+
+
+class Machine(FieldPermissionModelMixin, models.Model):
""" Class définissant une machine, object parent user, objets fils
interfaces"""
PRETTY_NAME = "Machine"
@@ -52,6 +57,97 @@ class Machine(models.Model):
)
active = models.BooleanField(default=True)
+ class Meta:
+ permissions = (
+ ("view_machine", "Peut voir un objet machine quelquonque"),
+ ("change_machine_user", "Peut changer le propriétaire d'une machine"),
+ )
+
+ def get_instance(machineid, *args, **kwargs):
+ """Récupère une instance
+ :param machineid: Instance id à trouver
+ :return: Une instance machine évidemment"""
+ return Machine.objects.get(pk=machineid)
+
+ @staticmethod
+ def can_change_user(user_request, *args, **kwargs):
+ """Checks if an user is allowed to change the user who owns a
+ Machine.
+
+ Args:
+ user_request: The user requesting to change owner.
+
+ Returns:
+ A tuple with a boolean stating if edition is allowed and an
+ explanation message.
+ """
+ return user_request.has_perm('machines.change_machine_user'), "Vous ne pouvez pas \
+ modifier l'utilisateur de la machine."
+
+ def can_create(user_request, userid, *args, **kwargs):
+ """Vérifie qu'un user qui fait la requète peut bien créer la machine
+ et n'a pas atteint son quota, et crée bien une machine à lui
+ :param user_request: Utilisateur qui fait la requête
+ :param userid: id de l'user dont on va créer une machine
+ :return: soit True, soit False avec la raison de l'échec"""
+ try:
+ user = users.models.User.objects.get(pk=userid)
+ except users.models.User.DoesNotExist:
+ return False, u"Utilisateur inexistant"
+ options, created = preferences.models.OptionalMachine.objects.get_or_create()
+ max_lambdauser_interfaces = options.max_lambdauser_interfaces
+ if not user_request.has_perm('machines.add_machine'):
+ if user != user_request:
+ return False, u"Vous ne pouvez pas ajouter une machine à un\
+ autre user que vous sans droit"
+ if user.user_interfaces().count() >= max_lambdauser_interfaces:
+ return False, u"Vous avez atteint le maximum d'interfaces\
+ autorisées que vous pouvez créer vous même (%s) "\
+ % max_lambdauser_interfaces
+ return True, None
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien éditer cette instance particulière (soit
+ machine de soi, soit droit particulier
+ :param self: instance machine à éditer
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison le cas échéant"""
+ if not user_request.has_perm('machines.change_machine') and self.user != user_request:
+ return False, u"Vous ne pouvez pas éditer une machine d'un autre user\
+ que vous sans droit"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien supprimer cette instance particulière (soit
+ machine de soi, soit droit particulier
+ :param self: instance machine à supprimer
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.delete_machine') and self.user != user_request:
+ return False, u"Vous ne pouvez pas éditer une machine d'un autre user\
+ que vous sans droit"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des machines,
+ droit particulier correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_machine'):
+ return False, u"Vous ne pouvez pas afficher l'ensemble des machines sans permission"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière (soit
+ machine de soi, soit droit particulier
+ :param self: instance machine à éditer
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_machine') and self.user != user_request:
+ return False, u"Vous n'avez pas droit de voir les machines autre\
+ que les vôtres"
+ return True, None
+
def __str__(self):
return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name)
@@ -68,11 +164,81 @@ class MachineType(models.Model):
null=True
)
+ class Meta:
+ permissions = (
+ ("view_machinetype", "Peut voir un objet machinetype"),
+ ("use_all_machinetype", "Peut utiliser n'importe quel type de machine"),
+ )
+
def all_interfaces(self):
""" Renvoie toutes les interfaces (cartes réseaux) de type
machinetype"""
return Interface.objects.filter(type=self)
+ def get_instance(machinetypeid, *args, **kwargs):
+ """Récupère une instance
+ :param machinetypeid: Instance id à trouver
+ :return: Une instance machinetype évidemment"""
+ return MachineType.objects.get(pk=machinetypeid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un type de machine
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_machinetype'), u"Vous n'avez pas le droit\
+ de créer un type de machine"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance type de machine
+ :param self: Instance machinetype à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_machinetype'):
+ return False, u"Vous n'avez pas le droit d'éditer des types de machine"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien supprimer cette instance particulière (soit
+ machinetype de soi, soit droit particulier
+ :param self: instance machinetype à supprimer
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.delete_machinetype'):
+ return False, u"Vous n'avez pas le droit de supprimer des types de machines"
+ return True, None
+
+ def can_use_all(user_request, *args, **kwargs):
+ """Check if an user can use every MachineType.
+
+ Args:
+ user_request: The user requesting edition.
+ Returns:
+ A tuple with a boolean stating if user can acces and an explanation
+ message is acces is not allowed.
+ """
+ if not user_request.has_perm('machines.use_all_machinetype'):
+ return False, u"Vous n'avez pas le droit d'utiliser tout types de machines"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des machinetype,
+ droit particulier correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_machinetype'), u"Vous n'avez pas le droit\
+ de voir les types de machines"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance machinetype à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_machinetype'), u"Vous n'avez pas le droit\
+ de voir les types de machines"
+
def __str__(self):
return self.type
@@ -103,6 +269,12 @@ class IpType(models.Model):
null=True
)
+ class Meta:
+ permissions = (
+ ("view_iptype", "Peut voir un objet iptype"),
+ ("use_all_iptype", "Peut utiliser tous les iptype"),
+ )
+
@cached_property
def ip_range(self):
""" Renvoie un objet IPRange à partir de l'objet IpType"""
@@ -183,6 +355,63 @@ class IpType(models.Model):
self.clean()
super(IpType, self).save(*args, **kwargs)
+ def get_instance(iptypeid, *args, **kwargs):
+ """Récupère une instance
+ :param iptypeid: Instance id à trouver
+ :return: Une instance iptype évidemment"""
+ return IpType.objects.get(pk=iptypeid)
+
+ def can_use_all(user_request, *args, **kwargs):
+ """Superdroit qui permet d'utiliser toutes les extensions sans restrictions
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.use_all_iptype'), None
+
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un type d'ip
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_iptype'), u"Vous n'avez pas le droit\
+ de créer un type d'ip"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance iptype
+ :param self: Instance iptype à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_iptype'):
+ return False, u"Vous n'avez pas le droit d'éditer des types d'ip"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour supprimer
+ cette instance iptype
+ :param self: Instance iptype à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_iptype'), u"Vous n'avez pas le droit\
+ de supprimer un type d'ip"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des iptype,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_iptype'), u"Vous n'avez pas le droit\
+ de voir les types d'ip"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance iptype à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_iptype'), u"Vous n'avez pas le droit\
+ de voir les types d'ip"
+
def __str__(self):
return self.type
@@ -196,6 +425,61 @@ class Vlan(models.Model):
name = models.CharField(max_length=256)
comment = models.CharField(max_length=256, blank=True)
+ class Meta:
+ permissions = (
+ ("view_vlan", "Peut voir un objet vlan"),
+ )
+
+ def get_instance(vlanid, *args, **kwargs):
+ """Récupère une instance
+ :param vlanid: Instance id à trouver
+ :return: Une instance vlan évidemment"""
+ return Vlan.objects.get(pk=vlanid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un vlan
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_vlan'), u"Vous n'avez pas le droit\
+ de créer un vlan"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance vlan
+ :param self: Instance vlan à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_vlan'):
+ return False, u"Vous n'avez pas le droit d'éditer des vlans"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour supprimer
+ cette instance vlan
+ :param self: Instance vlan à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_vlan'), u"Vous n'avez pas le droit\
+ de suprimer un vlan"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des vlan,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_vlan'), u"Vous n'avez pas le droit\
+ de voir les vlans"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance vlan à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_vlan'), u"Vous n'avez pas le droit\
+ de voir les vlans"
+
def __str__(self):
return self.name
@@ -230,6 +514,62 @@ class Nas(models.Model):
)
autocapture_mac = models.BooleanField(default=False)
+ class Meta:
+ permissions = (
+ ("view_nas", "Peut voir un objet Nas"),
+ )
+
+ def get_instance(nasid, *args, **kwargs):
+ """Récupère une instance
+ :param nasid: Instance id à trouver
+ :return: Une instance nas évidemment"""
+ return Nas.objects.get(pk=nasid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un nas
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_nas'), u"Vous n'avez pas le droit\
+ de créer un nas"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance nas
+ :param self: Instance nas à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_nas'):
+ return False, u"Vous n'avez pas le droit d'éditer des nas"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour supprimer
+ cette instance nas
+ :param self: Instance nas à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_nas'), u"Vous n'avez pas le droit\
+ de supprimer un nas"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des nas,
+ droit particulier view objet correspondant
+
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_nas'), u"Vous n'avez pas le droit\
+ de voir les nas"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance nas à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_nas'), u"Vous n'avez pas le droit\
+ de voir les nas"
+
def __str__(self):
return self.name
@@ -266,6 +606,61 @@ class SOA(models.Model):
help_text='Time To Live'
)
+ class Meta:
+ permissions = (
+ ("view_soa", "Peut voir un objet soa"),
+ )
+
+ def get_instance(soaid, *args, **kwargs):
+ """Récupère une instance
+ :param soaid: Instance id à trouver
+ :return: Une instance soa évidemment"""
+ return SOA.objects.get(pk=soaid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un soa
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_soa'), u"Vous n'avez pas le droit\
+ de créer un enregistrement SOA"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance soa
+ :param self: Instance soa à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_soa'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregistrements SOA"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour supprimer
+ cette instance soa
+ :param self: Instance soa à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_soa'), u"Vous n'avez pas le droit\
+ de supprimer des enregistrements SOA"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des soa,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_soa'), u"Vous n'avez pas le droit\
+ de voir les enreistrement SOA"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance soa à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_soa'), u"Vous n'avez pas le droit\
+ de voir les enreistrement SOA"
+
def __str__(self):
return str(self.name)
@@ -336,6 +731,12 @@ class Extension(models.Model):
default=SOA.new_default_soa
)
+ class Meta:
+ permissions = (
+ ("view_extension", "Peut voir un objet extension"),
+ ("use_all_extension", "Peut utiliser toutes les extension"),
+ )
+
@cached_property
def dns_entry(self):
""" Une entrée DNS A et AAAA sur origin (zone self)"""
@@ -348,6 +749,62 @@ class Extension(models.Model):
entry += "@ IN AAAA " + str(self.origin_v6)
return entry
+ def get_instance(extensionid, *args, **kwargs):
+ """Récupère une instance
+ :param extensionid: Instance id à trouver
+ :return: Une instance extension évidemment"""
+ return Extension.objects.get(pk=extensionid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ une extension
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_extension'), u"Vous n'avez pas le droit\
+ de créer une extension"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance extension
+ :param self: Instance extension à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_extension'):
+ return False, u"Vous n'avez pas le droit d'éditer des extensions"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour supprimer
+ cette instance extension
+ :param self: Instance extension à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_extension'), u"Vous n'avez pas le droit\
+ de supprimer des extension"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des extension,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_extension'), u"Vous n'avez pas le droit\
+ de voir les extensions"
+
+ def can_use_all(user_request, *args, **kwargs):
+ """Superdroit qui permet d'utiliser toutes les extensions sans restrictions
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.use_all_extension'), None
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance extension à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_extension'), u"Vous n'avez pas le droit\
+ de voir les extensions"
+
def __str__(self):
return self.name
@@ -367,12 +824,67 @@ class Mx(models.Model):
priority = models.PositiveIntegerField(unique=True)
name = models.OneToOneField('Domain', on_delete=models.PROTECT)
+ class Meta:
+ permissions = (
+ ("view_mx", "Peut voir un objet mx"),
+ )
+
@cached_property
def dns_entry(self):
"""Renvoie l'entrée DNS complète pour un MX à mettre dans les
fichiers de zones"""
return "@ IN MX " + str(self.priority).ljust(3) + " " + str(self.name)
+ def get_instance(mxid, *args, **kwargs):
+ """Récupère une instance
+ :param mxid: Instance id à trouver
+ :return: Une instance mx évidemment"""
+ return Mx.objects.get(pk=mxid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un mx
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_mx'), u"Vous n'avez pas le droit\
+ de créer un enregistrement MX"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance mx
+ :param self: Instance mx à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_mx'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregstrements MX"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour del
+ cette instance mx
+ :param self: Instance mx à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_mx'), u"Vous n'avez pas le droit\
+ de supprimer un enregistrement MX"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des mx,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_mx'), u"Vous n'avez pas le droit\
+ de voir les enregistrements MX"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance mx à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_mx'), u"Vous n'avez pas le droit\
+ de voir les enregistrements MX"
+
def __str__(self):
return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name)
@@ -384,11 +896,66 @@ class Ns(models.Model):
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
ns = models.OneToOneField('Domain', on_delete=models.PROTECT)
+ class Meta:
+ permissions = (
+ ("view_ns", "Peut voir un objet ns"),
+ )
+
@cached_property
def dns_entry(self):
"""Renvoie un enregistrement NS complet pour les filezones"""
return "@ IN NS " + str(self.ns)
+ def get_instance(nsid, *args, **kwargs):
+ """Récupère une instance
+ :param nsid: Instance id à trouver
+ :return: Une instance ns évidemment"""
+ return Ns.objects.get(pk=nsid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un ns
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_ns'), u"Vous n'avez pas le droit\
+ de créer un enregistrement NS"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance ns
+ :param self: Instance ns à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_ns'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregistrements NS"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour del
+ cette instance ns
+ :param self: Instance ns à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.del_ns'), u"Vous n'avez pas le droit\
+ de supprimer un enregistrement NS"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des ns,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_ns'), u"Vous n'avez pas le droit\
+ de voir les enregistrements NS"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance ns à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_ns'), u"Vous n'avez pas le droit\
+ de voir les enregistrements NS"
+
def __str__(self):
return str(self.zone) + ' ' + str(self.ns)
@@ -401,6 +968,61 @@ class Txt(models.Model):
field1 = models.CharField(max_length=255)
field2 = models.TextField(max_length=2047)
+ class Meta:
+ permissions = (
+ ("view_txt", "Peut voir un objet txt"),
+ )
+
+ def get_instance(txtid, *args, **kwargs):
+ """Récupère une instance
+ :param txtid: Instance id à trouver
+ :return: Une instance txt évidemment"""
+ return Txt.objects.get(pk=txtid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un txt
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_txt'), u"Vous n'avez pas le droit\
+ de créer un enregistrement TXT"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance txt
+ :param self: Instance txt à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_txt'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregistrement TXT"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour del
+ cette instance txt
+ :param self: Instance txt à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_txt'), u"Vous n'avez pas le droit\
+ de supprimer des enregistrements TXT"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des txt,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_txt'), u"Vous n'avez pas le droit\
+ de voir les enregistrements TXT"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance txt à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_txt'), u"Vous n'avez pas le droit\
+ de voir les enregistrements TXT"
+
def __str__(self):
return str(self.zone) + " : " + str(self.field1) + " " +\
str(self.field2)
@@ -454,6 +1076,61 @@ class Srv(models.Model):
help_text="Serveur cible"
)
+ class Meta:
+ permissions = (
+ ("view_soa", "Peut voir un objet soa"),
+ )
+
+ def get_instance(srvid, *args, **kwargs):
+ """Récupère une instance
+ :param srvid: Instance id à trouver
+ :return: Une instance srv évidemment"""
+ return Srv.objects.get(pk=srvid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un srv
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_soa'), u"Vous n'avez pas le droit\
+ de créer un enregistrement SRV"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance srv
+ :param self: Instance srv à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_soa'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregistrements SRV"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour del
+ cette instance srv
+ :param self: Instance srv à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_soa'), u"Vous n'avez pas le droit\
+ de supprimer un enregistrement SRV"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des srv,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_soa'), u"Vous n'avez pas le droit\
+ de voir les enregistrements SRV"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance srv à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_soa'), u"Vous n'avez pas le droit\
+ de voir les enregistrements SRV"
+
def __str__(self):
return str(self.service) + ' ' + str(self.protocole) + ' ' +\
str(self.extension) + ' ' + str(self.priority) +\
@@ -490,6 +1167,11 @@ class Interface(models.Model):
details = models.CharField(max_length=255, blank=True)
port_lists = models.ManyToManyField('OuverturePortList', blank=True)
+ class Meta:
+ permissions = (
+ ("view_interface", "Peut voir un objet interface"),
+ )
+
@cached_property
def is_active(self):
""" Renvoie si une interface doit avoir accès ou non """
@@ -571,6 +1253,77 @@ class Interface(models.Model):
correspondent pas")
super(Interface, self).save(*args, **kwargs)
+ def get_instance(interfaceid, *args, **kwargs):
+ """Récupère une instance
+ :param interfaceid: Instance id à trouver
+ :return: Une instance interface évidemment"""
+ return Interface.objects.get(pk=interfaceid)
+
+ def can_create(user_request, machineid, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ une interface, ou bien que la machine appartient bien à l'user
+ :param macineid: Id de la machine parente de l'interface
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ try:
+ machine = Machine.objects.get(pk=machineid)
+ except Machine.DoesNotExist:
+ return False, u"Machine inexistante"
+ if not user_request.has_perm('machines.add_interface'):
+ options, created = preferences.models.OptionalMachine.objects.get_or_create()
+ max_lambdauser_interfaces = options.max_lambdauser_interfaces
+ if machine.user != user_request:
+ return False, u"Vous ne pouvez pas ajouter une interface à une\
+ machine d'un autre user que vous sans droit"
+ if machine.user.user_interfaces().count() >= max_lambdauser_interfaces:
+ return False, u"Vous avez atteint le maximum d'interfaces\
+ autorisées que vous pouvez créer vous même (%s) "\
+ % max_lambdauser_interfaces
+ return True, None
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance interface, ou qu'elle lui appartient
+ :param self: Instance interface à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_interface') and self.machine.user != user_request:
+ return False, u"Vous ne pouvez pas éditer une machine\
+ d'un autre user que vous sans droit"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits delete object pour del
+ cette instance interface, ou qu'elle lui appartient
+ :param self: Instance interface à del
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.delete_interface') and self.machine.user != user_request:
+ return False, u"Vous ne pouvez pas éditer une machine d'un autre\
+ user que vous sans droit"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des interfaces,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_interface'):
+ return False, u"Vous n'avez pas le droit de voir des machines autre\
+ que les vôtres"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet ou qu'elle appartient à l'user
+ :param self: instance interface à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_interface') and self.machine.user != user_request:
+ return False, u"Vous n'avez pas le droit de voir des machines autre\
+ que les vôtres"
+ return True, None
+
def __str__(self):
try:
domain = self.domain
@@ -618,6 +1371,9 @@ class Domain(models.Model):
class Meta:
unique_together = (("name", "extension"),)
+ permissions = (
+ ("view_domain", "Peut voir un objet domain"),
+ )
def get_extension(self):
""" Retourne l'extension de l'interface parente si c'est un A
@@ -670,6 +1426,96 @@ class Domain(models.Model):
self.full_clean()
super(Domain, self).save(*args, **kwargs)
+ @cached_property
+ def get_source_interface(self):
+ """Renvoie l'interface source :
+ - l'interface reliée si c'est un A
+ - si c'est un cname, suit le cname jusqu'à atteindre le A
+ et renvoie l'interface parente
+ Fonction récursive"""
+ if self.interface_parent:
+ return self.interface_parent
+ else:
+ return self.cname.get_parent_interface()
+
+ def get_instance(domainid, *args, **kwargs):
+ """Récupère une instance
+ :param domainid: Instance id à trouver
+ :return: Une instance domain évidemment"""
+ return Domain.objects.get(pk=domainid)
+
+ def can_create(user_request, interfaceid, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un domain, ou possède l'interface associée
+ :param interfaceid: Id de l'interface associée à cet objet domain
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ try:
+ interface = Interface.objects.get(pk=interfaceid)
+ except Interface.DoesNotExist:
+ return False, u"Interface inexistante"
+ if not user_request.has_perm('machines.add_domain'):
+ options, created = preferences.models.OptionalMachine.objects.get_or_create()
+ max_lambdauser_aliases = options.max_lambdauser_aliases
+ if interface.machine.user != user_request:
+ return False, u"Vous ne pouvez pas ajouter un alias à une\
+ machine d'un autre user que vous sans droit"
+ if Domain.objects.filter(
+ cname__in=Domain.objects.filter(
+ interface_parent__in=interface.machine.user.user_interfaces()
+ )
+ ).count() >= max_lambdauser_aliases:
+ return False, u"Vous avez atteint le maximum d'alias\
+ autorisés que vous pouvez créer vous même (%s) "\
+ % max_lambdauser_aliases
+ return True, None
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits pour editer
+ cette instance domain
+ :param self: Instance domain à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_domain') and\
+ self.get_source_interface.machine.user != user_request:
+ return False, u"Vous ne pouvez pas editer un alias à une machine\
+ d'un autre user que vous sans droit"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits delete object pour del
+ cette instance domain, ou qu'elle lui appartient
+ :param self: Instance domain à del
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.delete_domain') and\
+ self.get_source_interface.machine.user != user_request:
+ return False, u"Vous ne pouvez pas supprimer un alias à une machine\
+ d'un autre user que vous sans droit"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des domain,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_domain'):
+ return False, u"Vous ne pouvez pas supprimer un alias à une machine\
+ d'un autre user que vous sans droit"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet ou qu'elle appartient à l'user
+ :param self: instance domain à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_domain') and\
+ self.get_source_interface.machine.user != user_request:
+ return False, u"Vous n'avez pas le droit de voir des machines autre\
+ que les vôtres"
+ return True, None
+
def __str__(self):
return str(self.name) + str(self.extension)
@@ -680,6 +1526,11 @@ class IpList(models.Model):
ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True)
ip_type = models.ForeignKey('IpType', on_delete=models.CASCADE)
+ class Meta:
+ permissions = (
+ ("view_iplist", "Peut voir un objet iplist"),
+ )
+
@cached_property
def need_infra(self):
""" Permet de savoir si un user basique peut assigner cette ip ou
@@ -697,6 +1548,59 @@ class IpList(models.Model):
self.clean()
super(IpList, self).save(*args, **kwargs)
+ def get_instance(iplistid, *args, **kwargs):
+ """Récupère une instance
+ :param iplistid: Instance id à trouver
+ :return: Une instance iplist évidemment"""
+ return IpList.objects.get(pk=iplistid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ une ip
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_iplist'), u"Vous n'avez pas le droit\
+ de créer une ip"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance ip
+ :param self: Instance ip à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_iplist'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregistrements ip"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour delete
+ cette instance ip
+ :param self: Instance ip à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.delete_iplist'):
+ return False, u"Vous n'avez pas le droit d'éditer des enregistrements ip"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des ip,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_iplist'):
+ return False, u"Vous n'avez pas le droit de voir des enregistrements ip"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit infra
+ :param self: instance iplist à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_iplist'):
+ return False, u"Vous n'avez pas le droit de voir des enregistrements ip"
+ return True, None
+
def __str__(self):
return self.ipv4
@@ -716,6 +1620,11 @@ class Service(models.Model):
)
servers = models.ManyToManyField('Interface', through='Service_link')
+ class Meta:
+ permissions = (
+ ("view_service", "Peut voir un objet service"),
+ )
+
def ask_regen(self):
""" Marque à True la demande de régénération pour un service x """
Service_link.objects.filter(service=self).exclude(asked_regen=True)\
@@ -737,6 +1646,56 @@ class Service(models.Model):
def save(self, *args, **kwargs):
super(Service, self).save(*args, **kwargs)
+ def get_instance(serviceid, *args, **kwargs):
+ """Récupère une instance
+ :param serviceid: Instance id à trouver
+ :return: Une instance service évidemment"""
+ return Service.objects.get(pk=serviceid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un service
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_service'), u"Vous n'avez pas le droit\
+ de créer un service"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance service
+ :param self: Instance service à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_service'):
+ return False, u"Vous n'avez pas le droit d'éditer des services"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour delete
+ cette instance service
+ :param self: Instance service à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.delete_service'), u"Vous n'avez pas le droit\
+ de supprimer un service"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des services,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_service'), u"Vous n'avez pas le droit\
+ de voir des services"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance service à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_service'), u"Vous n'avez pas le droit\
+ de voir des services"
+
def __str__(self):
return str(self.service_type)
@@ -777,6 +1736,57 @@ class Service_link(models.Model):
) < timezone.now()
)
+ def get_instance(servicelinkid, *args, **kwargs):
+ """Récupère une instance
+ :param servicelinkid: Instance id à trouver
+ :return: Une instance servicelink évidemment"""
+ return ServiceLink.objects.get(pk=servicelinkid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour créer
+ un servicelink
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_service'), u"Vous n'avez pas le droit\
+ de créer un service"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour editer
+ cette instance servicelink
+ :param self: Instance servicelink à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_service'):
+ return False, u"Vous n'avez pas le droit d'éditer des services"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits infra pour delete
+ cette instance servicelink
+ :param self: Instance servicelink à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.delete_service'):
+ return False, u"Vous n'avez pas le droit d'éditer des services"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des services,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_service'), u"Vous n'avez pas le droit\
+ de voir des liens de services"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance service à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_service'), u"Vous n'avez pas le droit\
+ de voir des liens de services"
+
def __str__(self):
return str(self.server) + " " + str(self.service)
@@ -790,6 +1800,65 @@ class OuverturePortList(models.Model):
max_length=255
)
+ class Meta:
+ permissions = (
+ ("view_ouvertureportlist", "Peut voir un objet ouvertureport"),
+ )
+
+ def get_instance(ouvertureportlistid, *args, **kwargs):
+ """Récupère une instance
+ :param ouvertureportlistid: Instance id à trouver
+ :return: Une instance ouvertureportlist évidemment"""
+ return OuverturePortList.objects.get(pk=ouvertureportlistid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits bureau pour créer
+ une ouverture de port
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_ouvertureportlist') , u"Vous n'avez pas le droit\
+ d'ouvrir un port"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits bureau pour editer
+ cette instance ouvertureportlist
+ :param self: Instance ouvertureportlist à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_ouvertureportlist'):
+ return False, u"Vous n'avez pas le droit d'éditer des ouvertures de port"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits bureau pour delete
+ cette instance ouvertureportlist
+ :param self: Instance ouvertureportlist à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.delete_ouvertureportlist'):
+ return False, u"Vous n'avez pas le droit de supprimer une ouverture\
+ de port"
+ if self.interface_set.all():
+ return False, u"Cette liste de ports est utilisée"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des ouvertureport,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_ouvertureportlist'), u"Vous n'avez pas le droit\
+ de voir des ouverture de ports"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance ouvertureport à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ return user_request.has_perm('machines.view_ouvertureportlist'), u"Vous n'avez pas le droit\
+ de voir des ouverture de ports"
+
def __str__(self):
return self.name
@@ -860,6 +1929,60 @@ class OuverturePort(models.Model):
default=OUT,
)
+ def get_instance(ouvertureportid, *args, **kwargs):
+ """Récupère une instance
+ :param ouvertureportid: Instance id à trouver
+ :return: Une instance ouvertureport évidemment"""
+ return OuverturePort.objects.get(pk=ouvertureportid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits bureau pour créer
+ une ouverture de port
+ :param user_request: instance utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ return user_request.has_perm('machines.add_ouvertureportlist') , u"Vous n'avez pas le droit\
+ d'ouvrir un port"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits bureau pour editer
+ cette instance ouvertureport
+ :param self: Instance ouvertureport à editer
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.change_ouvertureportlist'):
+ return False, u"Vous n'avez pas le droit d'éditer des ouvertures de port"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Verifie que l'user a les bons droits bureau pour delete
+ cette instance ouvertureport
+ :param self: Instance ouvertureport à delete
+ :param user_request: Utilisateur qui fait la requête
+ :return: soit True, soit False avec la raison de l'échec"""
+ if not user_request.has_perm('machines.delete_ouvertureportlist'):
+ return False, u"Vous n'avez pas le droit d'éditer des ouvertures de port"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien afficher l'ensemble des ouvertureport,
+ droit particulier view objet correspondant
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_ouvertureportlist'):
+ return False, u"Vous n'avez pas le droit d'éditer des ouvertures de port"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Vérifie qu'on peut bien voir cette instance particulière avec
+ droit view objet
+ :param self: instance ouvertureport à voir
+ :param user_request: instance user qui fait l'edition
+ :return: True ou False avec la raison de l'échec le cas échéant"""
+ if not user_request.has_perm('machines.view_ouvertureportlist'):
+ return False, u"Vous n'avez pas le droit d'éditer des ouvertures de port"
+ return True, None
+
+
def __str__(self):
if self.begin == self.end:
return str(self.begin)
diff --git a/machines/templates/machines/aff_alias.html b/machines/templates/machines/aff_alias.html
index bd64c737..fb3f0486 100644
--- a/machines/templates/machines/aff_alias.html
+++ b/machines/templates/machines/aff_alias.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -33,7 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ alias }}
+ {% can_edit alias %}
{% include 'buttons/edit.html' with href='machines:edit-alias' id=alias.id %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='alias' id=alias.id %}
diff --git a/machines/templates/machines/aff_extension.html b/machines/templates/machines/aff_extension.html
index 15a4c637..0da5a08e 100644
--- a/machines/templates/machines/aff_extension.html
+++ b/machines/templates/machines/aff_extension.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -45,9 +47,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ extension.origin_v6 }}
{% endif %}
- {% if is_infra %}
+ {% can_create Extension %}
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='extension' id=extension.id %}
diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html
index 454b169d..c5ed4c10 100644
--- a/machines/templates/machines/aff_iptype.html
+++ b/machines/templates/machines/aff_iptype.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ type.vlan }}
{{ type.ouverture_ports }}
- {% if is_infra %}
+ {% can_edit type %}
{% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='iptype' id=type.id %}
diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html
index 63d35241..124aaf4d 100644
--- a/machines/templates/machines/aff_machines.html
+++ b/machines/templates/machines/aff_machines.html
@@ -22,11 +22,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
{% if machines_list.paginator %}
{% include "pagination.html" with list=machines_list %}
{% endif %}
-
+
@@ -50,30 +52,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+ {% can_create Interface machine.id %}
{% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc='Ajouter une interface' %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='machine' id=machine.id %}
+ {% can_delete machine %}
{% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %}
+ {% acl_end %}
{% for interface in machine.interface_set.all %}
{% if interface.domain.related_domain.all %}
-
-
- {{ interface.domain }}
-
-
-
+ {{ interface.domain }}
+
+ Afficher les alias
+
{% else %}
{{ interface.domain }}
{% endif %}
@@ -97,27 +92,53 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% include 'buttons/history.html' with href='machines:history' name='interface' id=interface.id %}
+ {% can_delete interface %}
{% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %}
-
+ {% if interface.domain.related_domain.all %}
+
+
+
+
+ {% for al in interface.domain.related_domain.all %}
+
+
+ {{ al }}
+
+
+
+ {% endfor %}
+
+
+
+
+ {% endif %}
{% endfor %}
@@ -126,6 +147,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+
+
{% if machines_list.paginator %}
{% include "pagination.html" with list=machines_list %}
{% endif %}
diff --git a/machines/templates/machines/aff_machinetype.html b/machines/templates/machines/aff_machinetype.html
index 7fdcbdb4..facad203 100644
--- a/machines/templates/machines/aff_machinetype.html
+++ b/machines/templates/machines/aff_machinetype.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -35,9 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ type.type }}
{{ type.ip_type }}
- {% if is_infra %}
+ {% can_edit type %}
{% include 'buttons/edit.html' with href='machines:edit-machinetype' id=type.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='machinetype' id=type.id %}
diff --git a/machines/templates/machines/aff_mx.html b/machines/templates/machines/aff_mx.html
index 176ab061..4478cdab 100644
--- a/machines/templates/machines/aff_mx.html
+++ b/machines/templates/machines/aff_mx.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -38,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ mx.priority }}
{{ mx.name }}
- {% if is_infra %}
+ {% can_edit mx %}
{% include 'buttons/edit.html' with href='machines:edit-mx' id=mx.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='mx' id=mx.id %}
diff --git a/machines/templates/machines/aff_nas.html b/machines/templates/machines/aff_nas.html
index dc8fd079..735a4ca8 100644
--- a/machines/templates/machines/aff_nas.html
+++ b/machines/templates/machines/aff_nas.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -41,9 +43,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ nas.port_access_mode }}
{{ nas.autocapture_mac }}
- {% if is_infra %}
+ {% can_edit nas %}
{% include 'buttons/edit.html' with href='machines:edit-nas' id=nas.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='nas' id=nas.id %}
diff --git a/machines/templates/machines/aff_ns.html b/machines/templates/machines/aff_ns.html
index 67a0dd81..5ee87304 100644
--- a/machines/templates/machines/aff_ns.html
+++ b/machines/templates/machines/aff_ns.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ ns.zone }}
{{ ns.ns }}
- {% if is_infra %}
+ {% can_edit ns %}
{% include 'buttons/edit.html' with href='machines:edit-ns' id=ns.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='ns' id=ns.id %}
diff --git a/machines/templates/machines/aff_service.html b/machines/templates/machines/aff_service.html
index 47bfee25..da80b4da 100644
--- a/machines/templates/machines/aff_service.html
+++ b/machines/templates/machines/aff_service.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -40,9 +42,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ service.regular_time_regen }}
{% for serv in service.servers.all %}{{ serv }}, {% endfor %}
- {% if is_infra %}
+ {% can_edit service %}
{% include 'buttons/edit.html' with href='machines:edit-service' id=service.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='service' id=service.id %}
diff --git a/machines/templates/machines/aff_soa.html b/machines/templates/machines/aff_soa.html
index 3dad11c7..5352a739 100644
--- a/machines/templates/machines/aff_soa.html
+++ b/machines/templates/machines/aff_soa.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -44,9 +46,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ soa.expire }}
{{ soa.ttl }}
- {% if is_infra %}
+ {% can_edit soa %}
{% include 'buttons/edit.html' with href='machines:edit-soa' id=soa.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='soa' id=soa.id %}
diff --git a/machines/templates/machines/aff_srv.html b/machines/templates/machines/aff_srv.html
index 773815d9..e7886cf1 100644
--- a/machines/templates/machines/aff_srv.html
+++ b/machines/templates/machines/aff_srv.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ srv.port }}
{{ srv.target }}
- {% if is_infra %}
+ {% can_edit srv %}
{% include 'buttons/edit.html' with href='machines:edit-srv' id=srv.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='srv' id=srv.id %}
diff --git a/machines/templates/machines/aff_txt.html b/machines/templates/machines/aff_txt.html
index fd7c5ee6..973fd6d9 100644
--- a/machines/templates/machines/aff_txt.html
+++ b/machines/templates/machines/aff_txt.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ txt.zone }}
{{ txt.dns_entry }}
- {% if is_infra %}
+ {% can_edit txt %}
{% include 'buttons/edit.html' with href='machines:edit-txt' id=txt.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='txt' id=txt.id %}
diff --git a/machines/templates/machines/aff_vlan.html b/machines/templates/machines/aff_vlan.html
index eaa1c82c..deb8cb11 100644
--- a/machines/templates/machines/aff_vlan.html
+++ b/machines/templates/machines/aff_vlan.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -39,9 +41,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ vlan.comment }}
{% for range in vlan.iptype_set.all %}{{ range }}, {% endfor%}
- {% if is_infra %}
+ {% can_create Vlan %}
{% include 'buttons/edit.html' with href='machines:edit-vlan' id=vlan.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='machines:history' name='vlan' id=vlan.id %}
diff --git a/machines/templates/machines/index_extension.html b/machines/templates/machines/index_extension.html
index 3169b4c9..a0159974 100644
--- a/machines/templates/machines/index_extension.html
+++ b/machines/templates/machines/index_extension.html
@@ -25,45 +25,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %}
+{% load acl %}
+
{% block title %}Machines{% endblock %}
{% block content %}
Liste des extensions
- {% if is_infra %}
+ {% can_create Extension %}
Ajouter une extension
+ {% acl_end %}
Supprimer une ou plusieurs extensions
- {% endif %}
{% include "machines/aff_extension.html" with extension_list=extension_list %}
Liste des enregistrements SOA
- {% if is_infra %}
+ {% can_create SOA %}
Ajouter un enregistrement SOA
+ {% acl_end %}
Supprimer un enregistrement SOA
- {% endif %}
{% include "machines/aff_soa.html" with soa_list=soa_list %}
Liste des enregistrements MX
- {% if is_infra %}
+ {% can_create Mx %}
Ajouter un enregistrement MX
+ {% acl_end %}
Supprimer un enregistrement MX
- {% endif %}
{% include "machines/aff_mx.html" with mx_list=mx_list %}
Liste des enregistrements NS
- {% if is_infra %}
+ {% can_create Ns %}
Ajouter un enregistrement NS
+ {% acl_end %}
Supprimer un enregistrement NS
- {% endif %}
{% include "machines/aff_ns.html" with ns_list=ns_list %}
Liste des enregistrements TXT
- {% if is_infra %}
+ {% can_create Txt %}
Ajouter un enregistrement TXT
+ {% acl_end %}
Supprimer un enregistrement TXT
- {% endif %}
{% include "machines/aff_txt.html" with txt_list=txt_list %}
Liste des enregistrements SRV
- {% if is_infra %}
+ {% can_create Srv %}
Ajouter un enregistrement SRV
+ {% acl_end %}
Supprimer un enregistrement SRV
- {% endif %}
{% include "machines/aff_srv.html" with srv_list=srv_list %}
diff --git a/machines/templates/machines/index_iptype.html b/machines/templates/machines/index_iptype.html
index 8f85130b..cd582183 100644
--- a/machines/templates/machines/index_iptype.html
+++ b/machines/templates/machines/index_iptype.html
@@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %}
+{% load acl %}
+
{% block title %}Ip{% endblock %}
{% block content %}
Liste des types d'ip
- {% if is_infra %}
+ {% can_create IpType %}
Ajouter un type d'ip
+ {% acl_end %}
Supprimer un ou plusieurs types d'ip
- {% endif %}
{% include "machines/aff_iptype.html" with iptype_list=iptype_list %}
diff --git a/machines/templates/machines/index_machinetype.html b/machines/templates/machines/index_machinetype.html
index 1e99fd2d..96fa2d7e 100644
--- a/machines/templates/machines/index_machinetype.html
+++ b/machines/templates/machines/index_machinetype.html
@@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %}
+{% load acl %}
+
{% block title %}Machines{% endblock %}
{% block content %}
Liste des types de machines
- {% if is_infra %}
+ {% can_create MachineType %}
Ajouter un type de machine
+ {% acl_end %}
Supprimer un ou plusieurs types de machines
- {% endif %}
{% include "machines/aff_machinetype.html" with machinetype_list=machinetype_list %}
diff --git a/machines/templates/machines/index_nas.html b/machines/templates/machines/index_nas.html
index ac1fe8b4..b4f99ac4 100644
--- a/machines/templates/machines/index_nas.html
+++ b/machines/templates/machines/index_nas.html
@@ -25,16 +25,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %}
+{% load acl %}
+
{% block title %}Machines{% endblock %}
{% block content %}
Liste des nas
La correpondance nas-machinetype relie le type de nas à un type de machine.
Elle est utile pour l'autoenregistrement des macs par radius, et permet de choisir le type de machine à affecter aux machines en fonction du type de nas
- {% if is_infra %}
+ {% can_create Nas %}
Ajouter un type de nas
+ {% acl_end %}
Supprimer un ou plusieurs types nas
- {% endif %}
{% include "machines/aff_nas.html" with nas_list=nas_list %}
diff --git a/machines/templates/machines/index_portlist.html b/machines/templates/machines/index_portlist.html
index 04ac66d4..5c0c148a 100644
--- a/machines/templates/machines/index_portlist.html
+++ b/machines/templates/machines/index_portlist.html
@@ -2,11 +2,15 @@
{% load bootstrap3 %}
+{% load acl %}
+
{% block title %}Configuration de ports{% endblock %}
{% block content %}
Liste des configurations de ports
+ {% can_create OuverturePortList %}
Ajouter une configuration
+ {% acl_end %}
@@ -44,8 +48,12 @@
{% endif %}
+ {% can_delete pl %}
{% include 'buttons/suppr.html' with href='machines:del-portlist' id=pl.id %}
+ {% acl_end %}
+ {% can_edit pl %}
{% include 'buttons/edit.html' with href='machines:edit-portlist' id=pl.id %}
+ {% acl_end %}
{%endfor%}
diff --git a/machines/templates/machines/index_service.html b/machines/templates/machines/index_service.html
index b07f994c..4a06761d 100644
--- a/machines/templates/machines/index_service.html
+++ b/machines/templates/machines/index_service.html
@@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Machines{% endblock %}
{% block content %}
Liste des services
- {% if is_infra %}
+ {% can_create Service %}
Ajouter un service
+ {% acl_end %}
Supprimer un ou plusieurs service
- {% endif %}
{% include "machines/aff_service.html" with service_list=service_list %}
Etat des serveurs
{% include "machines/aff_servers.html" with servers_list=servers_list %}
diff --git a/machines/templates/machines/index_vlan.html b/machines/templates/machines/index_vlan.html
index ec00b0bf..d9a4f8f9 100644
--- a/machines/templates/machines/index_vlan.html
+++ b/machines/templates/machines/index_vlan.html
@@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %}
+{% load acl %}
+
{% block title %}Machines{% endblock %}
{% block content %}
Liste des vlans
- {% if is_infra %}
+ {% can_create Vlan %}
Ajouter un vlan
+ {% acl_end %}
Supprimer un ou plusieurs vlan
- {% endif %}
{% include "machines/aff_vlan.html" with vlan_list=vlan_list %}
diff --git a/machines/templates/machines/sidebar.html b/machines/templates/machines/sidebar.html
index 6ca3a07f..355d5147 100644
--- a/machines/templates/machines/sidebar.html
+++ b/machines/templates/machines/sidebar.html
@@ -23,42 +23,55 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
{% block sidebar %}
- {% if is_cableur %}
+ {% can_view_all Machine %}
Machines
+ {% acl_end %}
+ {% can_view_all MachineType %}
Types de machines
+ {% acl_end %}
+ {% can_view_all Extension %}
Extensions et zones
+ {% acl_end %}
+ {% can_view_all IpType %}
Plages d'IP
+ {% acl_end %}
+ {% can_view_all Vlan %}
Vlans
+ {% acl_end %}
+ {% can_view_all Nas %}
Gestion des nas
+ {% acl_end %}
+ {% can_view_all Service %}
Services (dhcp, dns...)
-
- {% endif %}
- {% if is_cableur %}
+
+ {% acl_end %}
+ {% can_view_all OuverturePortList %}
Ouverture de ports
- {%endif%}
+ {% acl_end %}
{% endblock %}
diff --git a/machines/urls.py b/machines/urls.py
index c024cf56..41f443e9 100644
--- a/machines/urls.py
+++ b/machines/urls.py
@@ -24,7 +24,7 @@
from __future__ import unicode_literals
from django.conf.urls import url
-
+import re2o
from . import views
urlpatterns = [
@@ -61,7 +61,7 @@ urlpatterns = [
url(r'^del_srv/$', views.del_srv, name='del-srv'),
url(r'^index_extension/$', views.index_extension, name='index-extension'),
url(r'^add_alias/(?P[0-9]+)$', views.add_alias, name='add-alias'),
- url(r'^edit_alias/(?P[0-9]+)$', views.edit_alias, name='edit-alias'),
+ url(r'^edit_alias/(?P[0-9]+)$', views.edit_alias, name='edit-alias'),
url(r'^del_alias/(?P[0-9]+)$', views.del_alias, name='del-alias'),
url(r'^index_alias/(?P[0-9]+)$', views.index_alias, name='index-alias'),
url(r'^add_service/$', views.add_service, name='add-service'),
@@ -76,20 +76,12 @@ urlpatterns = [
url(r'^edit_nas/(?P[0-9]+)$', views.edit_nas, name='edit-nas'),
url(r'^del_nas/$', views.del_nas, name='del-nas'),
url(r'^index_nas/$', views.index_nas, name='index-nas'),
- url(r'^history/(?Pmachine)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pinterface)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pmachinetype)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pextension)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Psoa)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pmx)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pns)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Ptxt)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Psrv)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Piptype)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Palias)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pvlan)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pnas)/(?P[0-9]+)$', views.history, name='history'),
- url(r'^history/(?Pservice)/(?P[0-9]+)$', views.history, name='history'),
+ url(
+ r'history/(?P\w+)/(?P[0-9]+)$',
+ re2o.views.history,
+ name='history',
+ kwargs={'application':'machines'},
+ ),
url(r'^$', views.index, name='index'),
url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'),
url(r'^rest/regen-achieved/$', views.regen_achieved, name='regen-achieved'),
@@ -104,9 +96,9 @@ urlpatterns = [
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),
url(r'index_portlist/$', views.index_portlist, name='index-portlist'),
- url(r'^edit_portlist/(?P[0-9]+)$', views.edit_portlist, name='edit-portlist'),
- url(r'^del_portlist/(?P[0-9]+)$', views.del_portlist, name='del-portlist'),
+ url(r'^edit_portlist/(?P[0-9]+)$', views.edit_portlist, name='edit-portlist'),
+ url(r'^del_portlist/(?P[0-9]+)$', views.del_portlist, name='del-portlist'),
url(r'^add_portlist/$', views.add_portlist, name='add-portlist'),
- url(r'^port_config/(?P[0-9]+)$', views.configure_ports, name='port-config'),
+ url(r'^port_config/(?P[0-9]+)$', views.configure_ports, name='port-config'),
]
diff --git a/machines/views.py b/machines/views.py
index a59e493c..0975e58c 100644
--- a/machines/views.py
+++ b/machines/views.py
@@ -123,7 +123,15 @@ from re2o.utils import (
all_active_assigned_interfaces,
all_has_access,
filter_active_interfaces,
- SortTable
+ SortTable,
+)
+from re2o.acl import (
+ can_create,
+ can_edit,
+ can_delete,
+ can_view,
+ can_view_all,
+ can_delete_set,
)
from re2o.views import form
@@ -210,34 +218,18 @@ def generate_ipv4_mbf_param( form, is_type_tt ):
return i_mbf_param
@login_required
-def new_machine(request, userid):
- """ Fonction de creation d'une machine. Cree l'objet machine,
+@can_create(Machine)
+@can_edit(User)
+def new_machine(request, user, userid):
+ """ Fonction de creation d'une machine. Cree l'objet machine,
le sous objet interface et l'objet domain à partir de model forms.
Trop complexe, devrait être simplifié"""
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, u"Utilisateur inexistant" )
- return redirect(reverse('machines:index'))
- options, created = OptionalMachine.objects.get_or_create()
- max_lambdauser_interfaces = options.max_lambdauser_interfaces
- if not request.user.has_perms(('cableur',)):
- if user != request.user:
- messages.error(
- request,
- "Vous ne pouvez pas ajouter une machine à un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- if user.user_interfaces().count() >= max_lambdauser_interfaces:
- messages.error(request, "Vous avez atteint le maximum d'interfaces autorisées que vous pouvez créer vous même (%s) " % max_lambdauser_interfaces)
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- machine = NewMachineForm(request.POST or None)
- interface = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',)))
+
+ machine = NewMachineForm(request.POST or None, user=user)
+ interface = AddInterfaceForm(
+ request.POST or None,
+ user=request.user
+ )
domain = DomainForm(request.POST or None, user=user)
if machine.is_valid() and interface.is_valid():
new_machine = machine.save(commit=False)
@@ -264,32 +256,32 @@ def new_machine(request, userid):
return redirect(reverse(
'users:profil',
kwargs={'userid':str(user.id)}
- ))
- i_mbf_param = generate_ipv4_mbf_param( interface, False )
- return form({'machineform': machine, 'interfaceform': interface, 'domainform': domain, 'i_mbf_param': i_mbf_param}, 'machines/machine.html', request)
+ ))
+ i_mbf_param = generate_ipv4_mbf_param(interface, False)
+ return form(
+ {
+ 'machineform': machine,
+ 'interfaceform': interface,
+ 'domainform': domain,
+ 'i_mbf_param': i_mbf_param
+ },
+ 'machines/machine.html',
+ request
+ )
@login_required
-def edit_interface(request, interfaceid):
+@can_edit(Interface)
+def edit_interface(request, interface_instance, interfaceid):
""" Edition d'une interface. Distingue suivant les droits les valeurs de interfaces et machines que l'user peut modifier
infra permet de modifier le propriétaire"""
- try:
- interface = Interface.objects.get(pk=interfaceid)
- except Interface.DoesNotExist:
- messages.error(request, u"Interface inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('infra',)):
- if not request.user.has_perms(('cableur',)) and interface.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas éditer une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- machine_form = BaseEditMachineForm(request.POST or None, instance=interface.machine)
- interface_form = BaseEditInterfaceForm(request.POST or None, instance=interface, infra=False)
- else:
- machine_form = EditMachineForm(request.POST or None, instance=interface.machine)
- interface_form = EditInterfaceForm(request.POST or None, instance=interface)
- domain_form = DomainForm(request.POST or None, instance=interface.domain)
+
+ machine_form = EditMachineForm(
+ request.POST or None,
+ instance=interface_instance.machine,
+ user=request.user
+ )
+ interface_form = BaseEditInterfaceForm(request.POST or None, instance=interface_instance, user=request.user)
+ domain_form = DomainForm(request.POST or None, instance=interface_instance.domain)
if machine_form.is_valid() and interface_form.is_valid() and domain_form.is_valid():
new_machine = machine_form.save(commit=False)
new_interface = interface_form.save(commit=False)
@@ -309,26 +301,15 @@ def edit_interface(request, interfaceid):
messages.success(request, "La machine a été modifiée")
return redirect(reverse(
'users:profil',
- kwargs={'userid':str(interface.machine.user.id)}
+ kwargs={'userid':str(interface_instance.machine.user.id)}
))
i_mbf_param = generate_ipv4_mbf_param( interface_form, False )
return form({'machineform': machine_form, 'interfaceform': interface_form, 'domainform': domain_form, 'i_mbf_param': i_mbf_param}, 'machines/machine.html', request)
@login_required
-def del_machine(request, machineid):
+@can_delete(Machine)
+def del_machine(request, machine, machineid):
""" Supprime une machine, interfaces en mode cascade"""
- try:
- machine = Machine.objects.get(pk=machineid)
- except Machine.DoesNotExist:
- messages.error(request, u"Machine inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)):
- if machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas éditer une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(machine.user.id)}
- ))
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
machine.delete()
@@ -341,29 +322,12 @@ def del_machine(request, machineid):
return form({'objet': machine, 'objet_name': 'machine'}, 'machines/delete.html', request)
@login_required
-def new_interface(request, machineid):
+@can_create(Interface)
+@can_edit(Machine)
+def new_interface(request, machine, machineid):
""" Ajoute une interface et son domain associé à une machine existante"""
- try:
- machine = Machine.objects.get(pk=machineid)
- except Machine.DoesNotExist:
- messages.error(request, u"Machine inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)):
- options, created = OptionalMachine.objects.get_or_create()
- max_lambdauser_interfaces = options.max_lambdauser_interfaces
- if machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas ajouter une interface à une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- if machine.user.user_interfaces().count() >= max_lambdauser_interfaces:
- messages.error(request, "Vous avez atteint le maximum d'interfaces autorisées que vous pouvez créer vous même (%s) " % max_lambdauser_interfaces)
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- interface_form = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',)))
+
+ interface_form = AddInterfaceForm(request.POST or None, user=user)
domain_form = DomainForm(request.POST or None)
if interface_form.is_valid():
new_interface = interface_form.save(commit=False)
@@ -389,20 +353,9 @@ def new_interface(request, machineid):
return form({'interfaceform': interface_form, 'domainform': domain_form, 'i_mbf_param': i_mbf_param}, 'machines/machine.html', request)
@login_required
-def del_interface(request, interfaceid):
+@can_delete(Interface)
+def del_interface(request, interface, interfaceid):
""" Supprime une interface. Domain objet en mode cascade"""
- try:
- interface = Interface.objects.get(pk=interfaceid)
- except Interface.DoesNotExist:
- messages.error(request, u"Interface inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)):
- if interface.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas éditer une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
if request.method == "POST":
machine = interface.machine
with transaction.atomic(), reversion.create_revision():
@@ -418,9 +371,10 @@ def del_interface(request, interfaceid):
return form({'objet': interface, 'objet_name': 'interface'}, 'machines/delete.html', request)
@login_required
-@permission_required('infra')
+@can_create(IpType)
def add_iptype(request):
""" Ajoute un range d'ip. Intelligence dans le models, fonction views minimaliste"""
+
iptype = IpTypeForm(request.POST or None)
if iptype.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -432,14 +386,10 @@ def add_iptype(request):
return form({'iptypeform': iptype}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_iptype(request, iptypeid):
+@can_edit(IpType)
+def edit_iptype(request, iptype_instance, iptypeid):
""" Edition d'un range. Ne permet pas de le redimensionner pour éviter l'incohérence"""
- try:
- iptype_instance = IpType.objects.get(pk=iptypeid)
- except IpType.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-iptype'))
+
iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance)
if iptype.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -451,10 +401,10 @@ def edit_iptype(request, iptypeid):
return form({'iptypeform': iptype}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_iptype(request):
+@can_delete_set(IpType)
+def del_iptype(request, instances):
""" Suppression d'un range ip. Supprime les objets ip associés"""
- iptype = DelIpTypeForm(request.POST or None)
+ iptype = DelIpTypeForm(request.POST or None, instances=instances)
if iptype.is_valid():
iptype_dels = iptype.cleaned_data['iptypes']
for iptype_del in iptype_dels:
@@ -469,8 +419,9 @@ def del_iptype(request):
return form({'iptypeform': iptype}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(MachineType)
def add_machinetype(request):
+
machinetype = MachineTypeForm(request.POST or None)
if machinetype.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -482,13 +433,9 @@ def add_machinetype(request):
return form({'machinetypeform': machinetype}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_machinetype(request, machinetypeid):
- try:
- machinetype_instance = MachineType.objects.get(pk=machinetypeid)
- except MachineType.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-machinetype'))
+@can_edit(MachineType)
+def edit_machinetype(request, machinetype_instance, machinetypeid):
+
machinetype = MachineTypeForm(request.POST or None, instance=machinetype_instance)
if machinetype.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -500,9 +447,9 @@ def edit_machinetype(request, machinetypeid):
return form({'machinetypeform': machinetype}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_machinetype(request):
- machinetype = DelMachineTypeForm(request.POST or None)
+@can_delete_set(MachineType)
+def del_machinetype(request, instances):
+ machinetype = DelMachineTypeForm(request.POST or None, instances=instances)
if machinetype.is_valid():
machinetype_dels = machinetype.cleaned_data['machinetypes']
for machinetype_del in machinetype_dels:
@@ -517,8 +464,9 @@ def del_machinetype(request):
return form({'machinetypeform': machinetype}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Extension)
def add_extension(request):
+
extension = ExtensionForm(request.POST or None)
if extension.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -530,27 +478,23 @@ def add_extension(request):
return form({'extensionform': extension}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_extension(request, extensionid):
- try:
- extension_instance = Extension.objects.get(pk=extensionid)
- except Extension.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(Extension)
+def edit_extension(request, extension_instance, extensionid):
+
extension = ExtensionForm(request.POST or None, instance=extension_instance)
if extension.is_valid():
with transaction.atomic(), reversion.create_revision():
extension.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in extension.changed_data))
- messages.success(request, "Extension modifiée")
+ mssages.success(request, "Extension modifiée")
return redirect(reverse('machines:index-extension'))
return form({'extensionform': extension}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_extension(request):
- extension = DelExtensionForm(request.POST or None)
+@can_delete_set(Extension)
+def del_extension(request, instances):
+ extension = DelExtensionForm(request.POST or None, instances=instances)
if extension.is_valid():
extension_dels = extension.cleaned_data['extensions']
for extension_del in extension_dels:
@@ -565,8 +509,9 @@ def del_extension(request):
return form({'extensionform': extension}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(SOA)
def add_soa(request):
+
soa = SOAForm(request.POST or None)
if soa.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -578,13 +523,9 @@ def add_soa(request):
return form({'soaform': soa}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_soa(request, soaid):
- try:
- soa_instance = SOA.objects.get(pk=soaid)
- except SOA.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(SOA)
+def edit_soa(request, soa_instance, soaid):
+
soa = SOAForm(request.POST or None, instance=soa_instance)
if soa.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -596,9 +537,9 @@ def edit_soa(request, soaid):
return form({'soaform': soa}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_soa(request):
- soa = DelSOAForm(request.POST or None)
+@can_delete_set(SOA)
+def del_soa(request, instances):
+ soa = DelSOAForm(request.POST or None, instances=instances)
if soa.is_valid():
soa_dels = soa.cleaned_data['soa']
for soa_del in soa_dels:
@@ -613,8 +554,9 @@ def del_soa(request):
return form({'soaform': soa}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Mx)
def add_mx(request):
+
mx = MxForm(request.POST or None)
if mx.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -626,13 +568,9 @@ def add_mx(request):
return form({'mxform': mx}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_mx(request, mxid):
- try:
- mx_instance = Mx.objects.get(pk=mxid)
- except Mx.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(Mx)
+def edit_mx(request, mx_instance, mxid):
+
mx = MxForm(request.POST or None, instance=mx_instance)
if mx.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -644,9 +582,9 @@ def edit_mx(request, mxid):
return form({'mxform': mx}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_mx(request):
- mx = DelMxForm(request.POST or None)
+@can_delete_set(Mx)
+def del_mx(request, instances):
+ mx = DelMxForm(request.POST or None, instances=instances)
if mx.is_valid():
mx_dels = mx.cleaned_data['mx']
for mx_del in mx_dels:
@@ -661,8 +599,9 @@ def del_mx(request):
return form({'mxform': mx}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Ns)
def add_ns(request):
+
ns = NsForm(request.POST or None)
if ns.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -674,13 +613,9 @@ def add_ns(request):
return form({'nsform': ns}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_ns(request, nsid):
- try:
- ns_instance = Ns.objects.get(pk=nsid)
- except Ns.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(Ns)
+def edit_ns(request, ns_instance, nsid):
+
ns = NsForm(request.POST or None, instance=ns_instance)
if ns.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -692,9 +627,9 @@ def edit_ns(request, nsid):
return form({'nsform': ns}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_ns(request):
- ns = DelNsForm(request.POST or None)
+@can_delete_set(Ns)
+def del_ns(request, instances):
+ ns = DelNsForm(request.POST or None, instances=instances)
if ns.is_valid():
ns_dels = ns.cleaned_data['ns']
for ns_del in ns_dels:
@@ -709,8 +644,9 @@ def del_ns(request):
return form({'nsform': ns}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Txt)
def add_txt(request):
+
txt = TxtForm(request.POST or None)
if txt.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -722,13 +658,9 @@ def add_txt(request):
return form({'txtform': txt}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_txt(request, txtid):
- try:
- txt_instance = Txt.objects.get(pk=txtid)
- except Txt.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(Txt)
+def edit_txt(request, txt_instance, txtid):
+
txt = TxtForm(request.POST or None, instance=txt_instance)
if txt.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -740,9 +672,9 @@ def edit_txt(request, txtid):
return form({'txtform': txt}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_txt(request):
- txt = DelTxtForm(request.POST or None)
+@can_delete_set(Txt)
+def del_txt(request, instances):
+ txt = DelTxtForm(request.POST or None, instances=instances)
if txt.is_valid():
txt_dels = txt.cleaned_data['txt']
for txt_del in txt_dels:
@@ -757,8 +689,9 @@ def del_txt(request):
return form({'txtform': txt}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Srv)
def add_srv(request):
+
srv = SrvForm(request.POST or None)
if srv.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -770,13 +703,9 @@ def add_srv(request):
return form({'srvform': srv}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_srv(request, srvid):
- try:
- srv_instance = Srv.objects.get(pk=srvid)
- except Srv.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(Srv)
+def edit_srv(request, srv_instance, srvid):
+
srv = SrvForm(request.POST or None, instance=srv_instance)
if srv.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -788,9 +717,9 @@ def edit_srv(request, srvid):
return form({'srvform': srv}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_srv(request):
- srv = DelSrvForm(request.POST or None)
+@can_delete_set(Srv)
+def del_srv(request, instances):
+ srv = DelSrvForm(request.POST or None, instances=instances)
if srv.is_valid():
srv_dels = srv.cleaned_data['srv']
for srv_del in srv_dels:
@@ -805,28 +734,11 @@ def del_srv(request):
return form({'srvform': srv}, 'machines/machine.html', request)
@login_required
-def add_alias(request, interfaceid):
- try:
- interface = Interface.objects.get(pk=interfaceid)
- except Interface.DoesNotExist:
- messages.error(request, u"Interface inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)):
- options, created = OptionalMachine.objects.get_or_create()
- max_lambdauser_aliases = options.max_lambdauser_aliases
- if interface.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas ajouter un alias à une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- if Domain.objects.filter(cname__in=Domain.objects.filter(interface_parent__in=interface.machine.user.user_interfaces())).count() >= max_lambdauser_aliases:
- messages.error(request, "Vous avez atteint le maximum d'alias autorisées que vous pouvez créer vous même (%s) " % max_lambdauser_aliases)
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- alias = AliasForm(request.POST or None, infra=request.user.has_perms(('infra',)))
+@can_create(Domain)
+@can_edit(Interface)
+def add_alias(request, interface, interfaceid):
+
+ alias = AliasForm(request.POST or None, user=request.user)
if alias.is_valid():
alias = alias.save(commit=False)
alias.cname = interface.domain
@@ -836,50 +748,31 @@ def add_alias(request, interfaceid):
reversion.set_comment("Création")
messages.success(request, "Cet alias a été ajouté")
return redirect(reverse(
- 'machines:index-alias',
+ 'machines:index-alias',
kwargs={'interfaceid':str(interfaceid)}
))
return form({'aliasform': alias}, 'machines/machine.html', request)
@login_required
-def edit_alias(request, aliasid):
- try:
- alias_instance = Domain.objects.get(pk=aliasid)
- except Domain.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
- if not request.user.has_perms(('cableur',)) and alias_instance.cname.interface_parent.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas ajouter un alias à une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- alias = AliasForm(request.POST or None, instance=alias_instance, infra=request.user.has_perms(('infra',)))
+@can_edit(Domain)
+def edit_alias(request, domain_instance, domainid):
+
+ alias = AliasForm(request.POST or None, instance=domain_instance, user=request.user)
if alias.is_valid():
with transaction.atomic(), reversion.create_revision():
- alias_instance = alias.save()
+ domain_instance = alias.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in alias.changed_data))
messages.success(request, "Alias modifié")
return redirect(reverse(
- 'machines:index-alias',
- kwargs={'interfaceid':str(alias_instance.cname.interface_parent.id)}
+ 'machines:index-alias',
+ kwargs={'interfaceid':str(domain_instance.cname.interface_parent.id)}
))
return form({'aliasform': alias}, 'machines/machine.html', request)
@login_required
-def del_alias(request, interfaceid):
- try:
- interface = Interface.objects.get(pk=interfaceid)
- except Interface.DoesNotExist:
- messages.error(request, u"Interface inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)) and interface.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas ajouter un alias à une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
+@can_edit(Interface)
+def del_alias(request, interface, interfaceid):
alias = DelAliasForm(request.POST or None, interface=interface)
if alias.is_valid():
alias_dels = alias.cleaned_data['alias']
@@ -892,15 +785,16 @@ def del_alias(request, interfaceid):
except ProtectedError:
messages.error(request, "Erreur l'alias suivant %s ne peut être supprimé" % alias_del)
return redirect(reverse(
- 'machines:index-alias',
+ 'machines:index-alias',
kwargs={'interfaceid':str(interfaceid)}
))
return form({'aliasform': alias}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Service)
def add_service(request):
+
service = ServiceForm(request.POST or None)
if service.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -912,13 +806,9 @@ def add_service(request):
return form({'serviceform': service}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_service(request, serviceid):
- try:
- service_instance = Service.objects.get(pk=serviceid)
- except Ns.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-extension'))
+@can_edit(Service)
+def edit_service(request, service_instance, serviceid):
+
service = ServiceForm(request.POST or None, instance=service_instance)
if service.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -930,9 +820,9 @@ def edit_service(request, serviceid):
return form({'serviceform': service}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_service(request):
- service = DelServiceForm(request.POST or None)
+@can_delete_set(Service)
+def del_service(request, instances):
+ service = DelServiceForm(request.POST or None, instances=instances)
if service.is_valid():
service_dels = service.cleaned_data['service']
for service_del in service_dels:
@@ -947,8 +837,9 @@ def del_service(request):
return form({'serviceform': service}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Vlan)
def add_vlan(request):
+
vlan = VlanForm(request.POST or None)
if vlan.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -960,13 +851,9 @@ def add_vlan(request):
return form({'vlanform': vlan}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_vlan(request, vlanid):
- try:
- vlan_instance = Vlan.objects.get(pk=vlanid)
- except Vlan.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-vlan'))
+@can_edit(Vlan)
+def edit_vlan(request, vlan_instance, vlanid):
+
vlan = VlanForm(request.POST or None, instance=vlan_instance)
if vlan.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -978,9 +865,9 @@ def edit_vlan(request, vlanid):
return form({'vlanform': vlan}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_vlan(request):
- vlan = DelVlanForm(request.POST or None)
+@can_delete_set(Vlan)
+def del_vlan(request, instances):
+ vlan = DelVlanForm(request.POST or None, instances=instances)
if vlan.is_valid():
vlan_dels = vlan.cleaned_data['vlan']
for vlan_del in vlan_dels:
@@ -995,8 +882,9 @@ def del_vlan(request):
return form({'vlanform': vlan}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
+@can_create(Nas)
def add_nas(request):
+
nas = NasForm(request.POST or None)
if nas.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -1008,13 +896,9 @@ def add_nas(request):
return form({'nasform': nas}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def edit_nas(request, nasid):
- try:
- nas_instance = Nas.objects.get(pk=nasid)
- except Nas.DoesNotExist:
- messages.error(request, u"Entrée inexistante" )
- return redirect(reverse('machines:index-nas'))
+@can_edit(Nas)
+def edit_nas(request, nas_instance, nasid):
+
nas = NasForm(request.POST or None, instance=nas_instance)
if nas.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -1026,9 +910,9 @@ def edit_nas(request, nasid):
return form({'nasform': nas}, 'machines/machine.html', request)
@login_required
-@permission_required('infra')
-def del_nas(request):
- nas = DelNasForm(request.POST or None)
+@can_delete_set(Nas)
+def del_nas(request, instances):
+ nas = DelNasForm(request.POST or None, instances=instances)
if nas.is_valid():
nas_dels = nas.cleaned_data['nas']
for nas_del in nas_dels:
@@ -1043,7 +927,7 @@ def del_nas(request):
return form({'nasform': nas}, 'machines/machine.html', request)
@login_required
-@permission_required('cableur')
+@can_view_all(Machine)
def index(request):
options, created = GeneralOption.objects.get_or_create()
pagination_large_number = options.pagination_large_number
@@ -1067,31 +951,36 @@ def index(request):
return render(request, 'machines/index.html', {'machines_list': machines_list})
@login_required
-@permission_required('cableur')
+@can_view_all(IpType)
def index_iptype(request):
iptype_list = IpType.objects.select_related('extension').select_related('vlan').order_by('type')
return render(request, 'machines/index_iptype.html', {'iptype_list':iptype_list})
@login_required
-@permission_required('cableur')
+@can_view_all(Vlan)
def index_vlan(request):
vlan_list = Vlan.objects.prefetch_related('iptype_set').order_by('vlan_id')
return render(request, 'machines/index_vlan.html', {'vlan_list':vlan_list})
@login_required
-@permission_required('cableur')
+@can_view_all(MachineType)
def index_machinetype(request):
machinetype_list = MachineType.objects.select_related('ip_type').order_by('type')
return render(request, 'machines/index_machinetype.html', {'machinetype_list':machinetype_list})
@login_required
-@permission_required('cableur')
+@can_view_all(Nas)
def index_nas(request):
nas_list = Nas.objects.select_related('machine_type').select_related('nas_type').order_by('name')
return render(request, 'machines/index_nas.html', {'nas_list':nas_list})
@login_required
-@permission_required('cableur')
+@can_view_all(SOA)
+@can_view_all(Mx)
+@can_view_all(Ns)
+@can_view_all(Txt)
+@can_view_all(Srv)
+@can_view_all(Extension)
def index_extension(request):
extension_list = Extension.objects.select_related('origin').select_related('soa').order_by('name')
soa_list = SOA.objects.order_by('name')
@@ -1102,153 +991,21 @@ def index_extension(request):
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'soa_list': soa_list, 'mx_list': mx_list, 'ns_list': ns_list, 'txt_list' : txt_list, 'srv_list': srv_list})
@login_required
-def index_alias(request, interfaceid):
- try:
- interface = Interface.objects.get(pk=interfaceid)
- except Interface.DoesNotExist:
- messages.error(request, u"Interface inexistante" )
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)) and interface.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas éditer une machine d'un autre user que vous sans droit")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
+@can_edit(Interface)
+def index_alias(request, interface, interfaceid):
alias_list = Domain.objects.filter(cname=Domain.objects.filter(interface_parent=interface)).order_by('name')
return render(request, 'machines/index_alias.html', {'alias_list':alias_list, 'interface_id': interfaceid})
@login_required
-@permission_required('cableur')
+@can_view_all(Service)
def index_service(request):
service_list = Service.objects.prefetch_related('service_link_set__server__domain__extension').all()
servers_list = Service_link.objects.select_related('server__domain__extension').select_related('service').all()
return render(request, 'machines/index_service.html', {'service_list':service_list, 'servers_list':servers_list})
-@login_required
-def history(request, object, id):
- if object == 'machine':
- try:
- object_instance = Machine.objects.get(pk=id)
- except Machine.DoesNotExist:
- messages.error(request, "Machine inexistante")
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)) and object_instance.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher l'historique d'une machine d'un autre user que vous sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object == 'interface':
- try:
- object_instance = Interface.objects.get(pk=id)
- except Interface.DoesNotExist:
- messages.error(request, "Interface inexistante")
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)) and object_instance.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher l'historique d'une interface d'un autre user que vous sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object == 'alias':
- try:
- object_instance = Domain.objects.get(pk=id)
- except Domain.DoesNotExist:
- messages.error(request, "Alias inexistant")
- return redirect(reverse('machines:index'))
- if not request.user.has_perms(('cableur',)) and object_instance.cname.interface_parent.machine.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher l'historique d'un alias d'un autre user que vous sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object == 'machinetype' and request.user.has_perms(('cableur',)):
- try:
- object_instance = MachineType.objects.get(pk=id)
- except MachineType.DoesNotExist:
- messages.error(request, "Type de machine inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'iptype' and request.user.has_perms(('cableur',)):
- try:
- object_instance = IpType.objects.get(pk=id)
- except IpType.DoesNotExist:
- messages.error(request, "Type d'ip inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'extension' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Extension.objects.get(pk=id)
- except Extension.DoesNotExist:
- messages.error(request, "Extension inexistante")
- return redirect(reverse('machines:index'))
- elif object == 'soa' and request.user.has_perms(('cableur',)):
- try:
- object_instance = SOA.objects.get(pk=id)
- except SOA.DoesNotExist:
- messages.error(request, "SOA inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'mx' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Mx.objects.get(pk=id)
- except Mx.DoesNotExist:
- messages.error(request, "Mx inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'txt' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Txt.objects.get(pk=id)
- except Txt.DoesNotExist:
- messages.error(request, "Txt inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'srv' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Srv.objects.get(pk=id)
- except Srv.DoesNotExist:
- messages.error(request, "Srv inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'ns' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Ns.objects.get(pk=id)
- except Ns.DoesNotExist:
- messages.error(request, "Ns inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'service' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Service.objects.get(pk=id)
- except Service.DoesNotExist:
- messages.error(request, "Service inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'vlan' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Vlan.objects.get(pk=id)
- except Vlan.DoesNotExist:
- messages.error(request, "Vlan inexistant")
- return redirect(reverse('machines:index'))
- elif object == 'nas' and request.user.has_perms(('cableur',)):
- try:
- object_instance = Nas.objects.get(pk=id)
- except Nas.DoesNotExist:
- messages.error(request, "Nas inexistant")
- return redirect(reverse('machines:index'))
- else:
- messages.error(request, "Objet inconnu")
- return redirect(reverse('machines:index'))
- options, created = GeneralOption.objects.get_or_create()
- pagination_number = options.pagination_number
- reversions = Version.objects.get_for_object(object_instance)
- paginator = Paginator(reversions, pagination_number)
- page = request.GET.get('page')
- try:
- reversions = paginator.page(page)
- except PageNotAnInteger:
- # If page is not an integer, deliver first page.
- reversions = paginator.page(1)
- except EmptyPage:
- # If page is out of range (e.g. 9999), deliver last page of results.
- reversions = paginator.page(paginator.num_pages)
- return render(request, 're2o/history.html', {'reversions': reversions, 'object': object_instance})
-
@login_required
-@permission_required('cableur')
+@can_view_all(OuverturePortList)
def index_portlist(request):
port_list = OuverturePortList.objects.prefetch_related('ouvertureport_set')\
.prefetch_related('interface_set__domain__extension')\
@@ -1256,14 +1013,10 @@ def index_portlist(request):
return render(request, "machines/index_portlist.html", {'port_list':port_list})
@login_required
-@permission_required('bureau')
-def edit_portlist(request, pk):
- try:
- port_list_instance = OuverturePortList.objects.get(pk=pk)
- except OuverturePortList.DoesNotExist:
- messages.error(request, "Liste de ports inexistante")
- return redirect(reverse('machines:index-portlist'))
- port_list = EditOuverturePortListForm(request.POST or None, instance=port_list_instance)
+@can_edit(OuverturePortList)
+def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid):
+
+ port_list = EditOuverturePortListForm(request.POST or None, instance=ouvertureportlist_instance)
port_formset = modelformset_factory(
OuverturePort,
fields=('begin','end','protocole','io'),
@@ -1271,7 +1024,7 @@ def edit_portlist(request, pk):
can_delete=True,
min_num=1,
validate_min=True,
- )(request.POST or None, queryset=port_list_instance.ouvertureport_set.all())
+ )(request.POST or None, queryset=ouvertureportlist_instance.ouvertureport_set.all())
if port_list.is_valid() and port_formset.is_valid():
pl = port_list.save()
instances = port_formset.save(commit=False)
@@ -1285,23 +1038,16 @@ def edit_portlist(request, pk):
return form({'port_list' : port_list, 'ports' : port_formset}, 'machines/edit_portlist.html', request)
@login_required
-@permission_required('bureau')
-def del_portlist(request, pk):
- try:
- port_list_instance = OuverturePortList.objects.get(pk=pk)
- except OuverturePortList.DoesNotExist:
- messages.error(request, "Liste de ports inexistante")
- return redirect(reverse('machines:index-portlist'))
- if port_list_instance.interface_set.all():
- messages.error(request, "Cette liste de ports est utilisée")
- return redirect(reverse('machines:index-portlist'))
+@can_delete(OuverturePortList)
+def del_portlist(request, port_list_instance, ouvertureportlistid):
port_list_instance.delete()
messages.success(request, "La liste de ports a été supprimée")
return redirect(reverse('machines:index-portlist'))
@login_required
-@permission_required('bureau')
+@can_create(OuverturePortList)
def add_portlist(request):
+
port_list = EditOuverturePortListForm(request.POST or None)
port_formset = modelformset_factory(
OuverturePort,
@@ -1330,13 +1076,9 @@ def add_portlist(request):
return form({'machineform' : port_list}, 'machines/machine.html', request)
@login_required
-@permission_required('cableur')
-def configure_ports(request, pk):
- try:
- interface_instance = Interface.objects.get(pk=pk)
- except Interface.DoesNotExist:
- messages.error(request, u"Interface inexistante" )
- return redirect(reverse('machines:index'))
+@can_create(OuverturePort)
+@can_edit(Interface)
+def configure_ports(request, interface_instance, interfaceid):
if not interface_instance.may_have_port_open():
messages.error(request, "Attention, l'ipv4 n'est pas publique, l'ouverture n'aura pas d'effet en v4")
interface = EditOuverturePortConfigForm(request.POST or None, instance=interface_instance)
@@ -1356,7 +1098,7 @@ class JSONResponse(HttpResponse):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def mac_ip_list(request):
interfaces = all_active_assigned_interfaces()
seria = InterfaceSerializer(interfaces, many=True)
@@ -1364,7 +1106,7 @@ def mac_ip_list(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def full_mac_ip_list(request):
interfaces = all_active_assigned_interfaces()
seria = FullInterfaceSerializer(interfaces, many=True)
@@ -1372,7 +1114,7 @@ def full_mac_ip_list(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def alias(request):
alias = Domain.objects.filter(interface_parent=None).filter(cname__in=Domain.objects.filter(interface_parent__in=Interface.objects.exclude(ipv4=None))).select_related('extension').select_related('cname__extension')
seria = DomainSerializer(alias, many=True)
@@ -1380,7 +1122,7 @@ def alias(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def corresp(request):
type = IpType.objects.all().select_related('extension')
seria = TypeSerializer(type, many=True)
@@ -1388,7 +1130,7 @@ def corresp(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def mx(request):
mx = Mx.objects.all().select_related('zone').select_related('name__extension')
seria = MxSerializer(mx, many=True)
@@ -1396,7 +1138,7 @@ def mx(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def txt(request):
txt = Txt.objects.all().select_related('zone')
seria = TxtSerializer(txt, many=True)
@@ -1404,7 +1146,7 @@ def txt(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def srv(request):
srv = Srv.objects.all().select_related('extension').select_related('target__extension')
seria = SrvSerializer(srv, many=True)
@@ -1412,7 +1154,7 @@ def srv(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def ns(request):
ns = Ns.objects.exclude(ns__in=Domain.objects.filter(interface_parent__in=Interface.objects.filter(ipv4=None))).select_related('zone').select_related('ns__extension')
seria = NsSerializer(ns, many=True)
@@ -1420,7 +1162,7 @@ def ns(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def zones(request):
zones = Extension.objects.all().select_related('origin')
seria = ExtensionSerializer(zones, many=True)
@@ -1428,21 +1170,21 @@ def zones(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def mac_ip(request):
seria = mac_ip_list(request)
return JSONResponse(seria)
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def mac_ip_dns(request):
seria = full_mac_ip_list(request)
return JSONResponse(seria)
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def service_servers(request):
service_link = Service_link.objects.all().select_related('server__domain').select_related('service')
seria = ServiceServersSerializer(service_link, many=True)
@@ -1450,7 +1192,7 @@ def service_servers(request):
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def ouverture_ports(request):
r = {'ipv4':{}, 'ipv6':{}}
for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'):
@@ -1478,7 +1220,7 @@ def ouverture_ports(request):
return JSONResponse(r)
@csrf_exempt
@login_required
-@permission_required('serveur')
+@permission_required('machines.serveur')
def regen_achieved(request):
obj = Service_link.objects.filter(service__in=Service.objects.filter(service_type=request.POST['service']), server__in=Interface.objects.filter(domain__in=Domain.objects.filter(name=request.POST['server'])))
if obj:
diff --git a/preferences/__init__.py b/preferences/__init__.py
index e69de29b..e895e295 100644
--- a/preferences/__init__.py
+++ b/preferences/__init__.py
@@ -0,0 +1,2 @@
+
+from .acl import *
diff --git a/preferences/acl.py b/preferences/acl.py
new file mode 100644
index 00000000..8ffb4c9b
--- /dev/null
+++ b/preferences/acl.py
@@ -0,0 +1,40 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""preferences.acl
+
+Here are defined some functions to check acl on the application.
+"""
+
+def can_view(user):
+ """Check if an user can view the application.
+
+ Args:
+ user: The user who wants to view the application.
+
+ Returns:
+ A couple (allowed, msg) where allowed is a boolean which is True if
+ viewing is granted and msg is a message (can be None).
+ """
+ can = user.has_module_perms('preferences')
+ return can, None if can else "Vous ne pouvez pas voir cette application."
diff --git a/preferences/forms.py b/preferences/forms.py
index 51cbb885..7dda8620 100644
--- a/preferences/forms.py
+++ b/preferences/forms.py
@@ -173,7 +173,15 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form):
"""Suppression de services sur la page d'accueil"""
services = forms.ModelMultipleChoiceField(
- queryset=Service.objects.all(),
+ queryset=Service.objects.none(),
label="Enregistrements service actuels",
widget=forms.CheckboxSelectMultiple
)
+
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelServiceForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['services'].queryset = instances
+ else:
+ self.fields['services'].queryset = Service.objects.all()
diff --git a/preferences/migrations/0025_auto_20171231_2142.py b/preferences/migrations/0025_auto_20171231_2142.py
new file mode 100644
index 00000000..d54b8215
--- /dev/null
+++ b/preferences/migrations/0025_auto_20171231_2142.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 20:42
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('preferences', '0024_optionaluser_all_can_create'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='assooption',
+ options={'permissions': (('view_assooption', "Peut voir les options de l'asso"),)},
+ ),
+ migrations.AlterModelOptions(
+ name='generaloption',
+ options={'permissions': (('view_generaloption', 'Peut voir les options générales'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='mailmessageoption',
+ options={'permissions': (('view_mailmessageoption', 'Peut voir les options de mail'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='optionalmachine',
+ options={'permissions': (('view_optionalmachine', 'Peut voir les options de machine'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='optionaltopologie',
+ options={'permissions': (('view_optionaltopologie', 'Peut voir les options de topologie'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='optionaluser',
+ options={'permissions': (('view_optionaluser', "Peut voir les options de l'user"),)},
+ ),
+ migrations.AlterModelOptions(
+ name='service',
+ options={'permissions': (('view_service', 'Peut voir les options de service'),)},
+ ),
+ ]
diff --git a/preferences/migrations/0027_merge_20180106_2019.py b/preferences/migrations/0027_merge_20180106_2019.py
new file mode 100644
index 00000000..68ce34fb
--- /dev/null
+++ b/preferences/migrations/0027_merge_20180106_2019.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2018-01-06 19:19
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('preferences', '0025_auto_20171231_2142'),
+ ('preferences', '0026_auto_20171216_0401'),
+ ]
+
+ operations = [
+ ]
diff --git a/preferences/models.py b/preferences/models.py
index 2e803b66..8dfc4260 100644
--- a/preferences/models.py
+++ b/preferences/models.py
@@ -26,7 +26,7 @@ Reglages généraux, machines, utilisateurs, mail, general pour l'application.
from __future__ import unicode_literals
from django.db import models
-from cotisations.models import Paiement
+import cotisations.models
class OptionalUser(models.Model):
@@ -47,10 +47,67 @@ class OptionalUser(models.Model):
help_text="Tous les users peuvent en créer d'autres",
)
+ class Meta:
+ permissions = (
+ ("view_optionaluser", "Peut voir les options de l'user"),
+ )
+
+ def get_instance(*args, **kwargs):
+ return OptionalUser.objects.get_or_create()
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a OptionalUser object.
+
+ :param user_request: The user who wants to create a user object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('preferences.add_optionaluser'), u"Vous n'avez pas le droit\
+ de créer les préférences concernant les users"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a OptionalUser object.
+
+ :param self: The OptionalUser which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('preferences.change_optionaluser'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant les users"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a OptionalUser object.
+
+ :param self: The OptionalUser which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('preferences.delete_optionaluser'), u"Vous n'avez pas le droit\
+ de supprimer les préférences concernant les users"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every OptionalUser objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('preferences.view_optionaluser'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant les utilisateurs"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a OptionalUser object.
+
+ :param self: The targeted OptionalUser.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_optionaluser'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant les utilisateurs"
+
def clean(self):
"""Creation du mode de paiement par solde"""
if self.user_solde:
- Paiement.objects.get_or_create(moyen="Solde")
+ cotisations.models.Paiement.objects.get_or_create(moyen="Solde")
class OptionalMachine(models.Model):
@@ -63,6 +120,64 @@ class OptionalMachine(models.Model):
max_lambdauser_aliases = models.IntegerField(default=10)
ipv6 = models.BooleanField(default=False)
+ class Meta:
+ permissions = (
+ ("view_optionalmachine", "Peut voir les options de machine"),
+ )
+
+ def get_instance(*args, **kwargs):
+ return OptionalMachine.objects.get_or_create()
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a OptionalMachine object.
+
+ :param user_request: The user who wants to create an object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('preferences.add_optionalmachine'), u"Vous n'avez pas le droit\
+ de créer les préférences concernant les machines"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a OptionalMachine object.
+
+ :param self: The OptionalMachine which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('preferences.change_optionalmachine'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant les machines"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a OptionalMachine object.
+
+ :param self: The OptionalMachine which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+
+ return user_request.has_perm('preferences.delete_optionalmachine'), u"Vous n'avez pas le droit\
+ de supprimer les préférences concernant les machines"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every OptionalMachine objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('preferences.view_optionalmachine'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant les machines"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a OptionalMachine object.
+
+ :param self: The targeted OptionalMachine.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_optionalmachine'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant les machines"
+
class OptionalTopologie(models.Model):
"""Reglages pour la topologie : mode d'accès radius, vlan où placer
@@ -96,6 +211,63 @@ class OptionalTopologie(models.Model):
null=True
)
+ class Meta:
+ permissions = (
+ ("view_optionaltopologie", "Peut voir les options de topologie"),
+ )
+
+ def get_instance(*args, **kwargs):
+ return OptionalTopologie.objects.get_or_create()
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a OptionalTopologie object.
+
+ :param user_request: The user who wants to create an object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('preferences.add_optionaltopologie'), u"Vous n'avez pas le droit\
+ de créer les préférences concernant la topologie"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a OptionalTopologie object.
+
+ :param self: The OptionalTopologie which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('preferences.change_optionaltopologie'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant la topologie"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a OptionalTopologie object.
+
+ :param self: The OptionalTopologie which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('preferences.delete_optionaltoplogie'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant la topologie"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every OptionalTopologie objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('preferences.view_optionaltopologie'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant la topologie"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a OptionalTopologie object.
+
+ :param self: The targeted OptionalTopologie.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_optionaltopologie'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant la topologie"
+
class GeneralOption(models.Model):
"""Options générales : nombre de resultats par page, nom du site,
@@ -114,6 +286,64 @@ class GeneralOption(models.Model):
site_name = models.CharField(max_length=32, default="Re2o")
email_from = models.EmailField(default="www-data@serveur.net")
+ class Meta:
+ permissions = (
+ ("view_generaloption", "Peut voir les options générales"),
+ )
+
+ def get_instance(*args, **kwargs):
+ return GeneralOption.objects.get_or_create()
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a GeneralOption object.
+
+ :param user_request: The user who wants to create an object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('preferences.add_generaloption'), u"Vous n'avez pas le droit\
+ de créer les préférences générales"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a GeneralOption object.
+
+ :param self: The GeneralOption which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('preferences.change_generaloption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences générales"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a GeneralOption object.
+
+ :param self: The GeneralOption which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('preferences.delete_generaloption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences générales"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every GeneralOption objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+
+ return user_request.has_perm('preferences.view_generaloption'), u"Vous n'avez pas le droit\
+ de voir les préférences générales"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a GeneralOption object.
+
+ :param self: The targeted GeneralOption.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_generaloption'), u"Vous n'avez pas le droit\
+ de voir les préférences générales"
+
class Service(models.Model):
"""Liste des services affichés sur la page d'accueil : url, description,
@@ -123,6 +353,65 @@ class Service(models.Model):
description = models.TextField()
image = models.ImageField(upload_to='logo', blank=True)
+ class Meta:
+ permissions = (
+ ("view_service", "Peut voir les options de service"),
+ )
+
+ def get_instance(serviceid, *args, **kwargs):
+ return Service.objects.get(pk=serviceid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a Service object.
+
+ :param user_request: The user who wants to create an object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+
+ return user_request.has_perm('preferences.add_service'), u"Vous n'avez pas le droit\
+ de créer un service pour la page d'accueil"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a Service object.
+
+ :param self: The Service which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('preferences.change_service'), u"Vous n'avez pas le droit\
+ d'éditer les services pour la page d'accueil"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a Service object.
+
+ :param self: The Right which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('preferences.delete_service'), u"Vous n'avez pas le droit\
+ de supprimer les services pour la page d'accueil"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every Service objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+
+ return user_request.has_perm('preferences.view_service'), u"Vous n'avez pas le droit\
+ de voir les services pour la page d'accueil"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a Service object.
+
+ :param self: The targeted Service.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_service'), u"Vous n'avez pas le droit\
+ de voir les services pour la page d'accueil"
+
def __str__(self):
return str(self.name)
@@ -148,6 +437,63 @@ class AssoOption(models.Model):
null=True
)
+ class Meta:
+ permissions = (
+ ("view_assooption", "Peut voir les options de l'asso"),
+ )
+
+ def get_instance(*args, **kwargs):
+ return AssoOption.objects.get_or_create()
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a AssoOption object.
+
+ :param user_request: The user who wants to create an object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('preferences.add_assooption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant l'association"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a AssoOption object.
+
+ :param self: The AssoOption which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('preferences.change_assooption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant l'association"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a AssoOption object.
+
+ :param self: The AssoOption which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('preferences.delete_assooption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant l'association"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every AssoOption objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('preferences.view_assooption'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant l'association"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a AssoOption object.
+
+ :param self: The targeted AssoOption.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_assooption'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant l'association"
+
class MailMessageOption(models.Model):
"""Reglages, mail de bienvenue et autre"""
@@ -155,3 +501,61 @@ class MailMessageOption(models.Model):
welcome_mail_fr = models.TextField(default="")
welcome_mail_en = models.TextField(default="")
+
+ class Meta:
+ permissions = (
+ ("view_mailmessageoption", "Peut voir les options de mail"),
+ )
+
+ def get_instance(*args, **kwargs):
+ return MailMessageOption.objects.get_or_create()
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a MailMessageOption object.
+
+ :param user_request: The user who wants to create an object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('preferences.add_mailmessageoption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant les mails"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a MailMessageOption object.
+
+ :param self: The MailMessageOption which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+
+ return user_request.has_perm('preferences.change_mailmessageoption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant les mails"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a AssoOption object.
+
+ :param self: The AssoOption which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('preferences.delete_mailmessageoption'), u"Vous n'avez pas le droit\
+ d'éditer les préférences concernant les mails"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every AssoOption objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('preferences.view_mailmessageoption'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant les mails"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a AssoOption object.
+
+ :param self: The targeted AssoOption.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('preferences.view_mailmessageoption'), u"Vous n'avez pas le droit\
+ de voir les préférences concernant les mails"
diff --git a/preferences/templates/preferences/aff_service.html b/preferences/templates/preferences/aff_service.html
index e5f8aecf..b4d4894c 100644
--- a/preferences/templates/preferences/aff_service.html
+++ b/preferences/templates/preferences/aff_service.html
@@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
-
+{% load acl %}
@@ -40,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ service.description }}
{{ service.image }}
- {% if is_admin %}
- {% include 'buttons/edit.html' with href='preferences:edit-services' id=service.id %}
- {% endif %}
+ {% can_edit service%}
+ {% include 'buttons/edit.html' with href='preferences:edit-service' id=service.id %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='preferences:history' name='service' id=service.id %}
diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html
index ad807155..2b1caec8 100644
--- a/preferences/templates/preferences/display_preferences.html
+++ b/preferences/templates/preferences/display_preferences.html
@@ -24,17 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Création et modification des préférences{% endblock %}
{% block content %}
Préférences utilisateur
- {% if is_bureau %}
Editer
- {% endif %}
@@ -58,12 +57,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Préférences machines
- {% if is_bureau %}
Editer
- {% endif %}
@@ -81,12 +78,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Préférences topologie
- {% if is_bureau %}
Editer
- {% endif %}
@@ -104,14 +99,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Préférences generales
- {% if is_bureau %}
Editer
- {% endif %}
-
+
Nom du site web
@@ -137,12 +130,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Données de l'association
- {% if is_bureau %}
Editer
- {% endif %}
@@ -164,19 +155,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ assooptions.telephone }}
Pseudo d'usage
{{ assooptions.pseudo }}
-
+
Objet utilisateur de l'association
{{ assooptions.utilisateur_asso }}
-
-
+
+
Messages personalisé dans les mails
- {% if is_bureau %}
Editer
- {% endif %}
@@ -190,10 +179,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Liste des services page d'accueil
- {% if is_infra %}
- Ajouter un service
+ {% can_create Service%}
+ Ajouter un service
+ {% acl_end %}
Supprimer un ou plusieurs service
- {% endif %}
{% include "preferences/aff_service.html" with service_list=service_list %}
diff --git a/preferences/urls.py b/preferences/urls.py
index f10d25a0..3bc15275 100644
--- a/preferences/urls.py
+++ b/preferences/urls.py
@@ -28,6 +28,7 @@ from __future__ import unicode_literals
from django.conf.urls import url
from . import views
+import re2o
urlpatterns = [
@@ -61,17 +62,18 @@ urlpatterns = [
views.edit_options,
name='edit-options'
),
- url(r'^add_services/$', views.add_services, name='add-services'),
+ url(r'^add_service/$', views.add_service, name='add-service'),
url(
- r'^edit_services/(?P[0-9]+)$',
- views.edit_services,
- name='edit-services'
+ r'^edit_service/(?P[0-9]+)$',
+ views.edit_service,
+ name='edit-service'
),
url(r'^del_services/$', views.del_services, name='del-services'),
url(
- r'^history/(?Pservice)/(?P[0-9]+)$',
- views.history,
- name='history'
+ r'^history/(?P\w+)/(?P[0-9]+)$',
+ re2o.views.history,
+ name='history',
+ kwargs={'application':'preferences'},
),
url(r'^$', views.display_options, name='display-options'),
]
diff --git a/preferences/views.py b/preferences/views.py
index 493c1d14..45324f53 100644
--- a/preferences/views.py
+++ b/preferences/views.py
@@ -42,6 +42,7 @@ from reversion.models import Version
from reversion import revisions as reversion
from re2o.views import form
+from re2o.acl import can_create, can_edit, can_delete_set, can_view_all
from .forms import ServiceForm, DelServiceForm
from .models import Service, OptionalUser, OptionalMachine, AssoOption
from .models import MailMessageOption, GeneralOption, OptionalTopologie
@@ -50,7 +51,12 @@ from . import forms
@login_required
-@permission_required('cableur')
+@can_view_all(OptionalUser)
+@can_view_all(OptionalMachine)
+@can_view_all(OptionalTopologie)
+@can_view_all(GeneralOption)
+@can_view_all(AssoOption)
+@can_view_all(MailMessageOption)
def display_options(request):
"""Vue pour affichage des options (en vrac) classé selon les models
correspondants dans un tableau"""
@@ -80,6 +86,11 @@ def edit_options(request, section):
form_instance = getattr(forms, 'Edit' + section + 'Form', None)
if model and form:
options_instance, _created = model.objects.get_or_create()
+ can, msg = options_instance.can_edit(request.user)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas éditer cette\
+ option.")
+ return redirect('/')
options = form_instance(
request.POST or None,
instance=options_instance
@@ -106,57 +117,52 @@ def edit_options(request, section):
@login_required
-@permission_required('admin')
-def add_services(request):
+@can_create(Service)
+def add_service(request):
"""Ajout d'un service de la page d'accueil"""
- services = ServiceForm(request.POST or None)
- if services.is_valid():
+ service = ServiceForm(request.POST or None)
+ if service.is_valid():
with transaction.atomic(), reversion.create_revision():
- services.save()
+ service.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Ce service a été ajouté")
return redirect(reverse('preferences:display-options'))
return form(
- {'preferenceform': services},
+ {'preferenceform': service},
'preferences/preferences.html',
request
)
@login_required
-@permission_required('admin')
-def edit_services(request, servicesid):
+@can_edit(Service)
+def edit_service(request, service_instance, serviceid):
"""Edition des services affichés sur la page d'accueil"""
- try:
- services_instance = Service.objects.get(pk=servicesid)
- except Service.DoesNotExist:
- messages.error(request, u"Entrée inexistante")
- return redirect(reverse('preferences:display-options'))
- services = ServiceForm(request.POST or None, instance=services_instance)
- if services.is_valid():
+ service = ServiceForm(request.POST or None, instance=service_instance)
+ if service.is_valid():
with transaction.atomic(), reversion.create_revision():
- services.save()
+ service.save()
reversion.set_user(request.user)
reversion.set_comment(
"Champs modifié(s) : %s" % ', '.join(
- field for field in services.changed_data
+ field for field in service.changed_data
)
)
messages.success(request, "Service modifié")
return redirect(reverse('preferences:display-options'))
return form(
- {'preferenceform': services},
+ {'preferenceform': service},
'preferences/preferences.html',
request
)
@login_required
-@permission_required('admin')
-def del_services(request):
+@can_delete_set(Service)
+def del_services(request, instances):
"""Suppression d'un service de la page d'accueil"""
- services = DelServiceForm(request.POST or None)
+ services = DelServiceForm(request.POST or None, instances=instances)
if services.is_valid():
services_dels = services.cleaned_data['services']
for services_del in services_dels:
@@ -164,7 +170,7 @@ def del_services(request):
with transaction.atomic(), reversion.create_revision():
services_del.delete()
reversion.set_user(request.user)
- messages.success(request, "Le services a été supprimée")
+ messages.success(request, "Le service a été supprimée")
except ProtectedError:
messages.error(request, "Erreur le service\
suivant %s ne peut être supprimé" % services_del)
@@ -174,33 +180,3 @@ def del_services(request):
'preferences/preferences.html',
request
)
-
-
-@login_required
-@permission_required('cableur')
-def history(request, object_name, object_id):
- """Historique de creation et de modification d'un service affiché sur
- la page d'accueil"""
- if object_name == 'service':
- try:
- object_instance = Service.objects.get(pk=object_id)
- except Service.DoesNotExist:
- messages.error(request, "Service inexistant")
- return redirect(reverse('preferences:display-options'))
- options, _created = GeneralOption.objects.get_or_create()
- pagination_number = options.pagination_number
- reversions = Version.objects.get_for_object(object_instance)
- paginator = Paginator(reversions, pagination_number)
- page = request.GET.get('page')
- try:
- reversions = paginator.page(page)
- except PageNotAnInteger:
- # If page is not an integer, deliver first page.
- reversions = paginator.page(1)
- except EmptyPage:
- # If page is out of range (e.g. 9999), deliver last page of results.
- reversions = paginator.page(paginator.num_pages)
- return render(request, 're2o/history.html', {
- 'reversions': reversions,
- 'object': object_instance
- })
diff --git a/re2o/acl.py b/re2o/acl.py
new file mode 100644
index 00000000..ffbbea42
--- /dev/null
+++ b/re2o/acl.py
@@ -0,0 +1,235 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""Handles ACL for re2o.
+
+Here are defined some decorators that can be used in views to handle ACL.
+"""
+from __future__ import unicode_literals
+
+import sys
+
+from django.contrib import messages
+from django.shortcuts import redirect
+from django.urls import reverse
+
+import cotisations, logs, machines, preferences, search, topologie, users
+
+
+def can_create(model):
+ """Decorator to check if an user can create a model.
+ It assumes that a valid user exists in the request and that the model has a
+ method can_create(user) which returns true if the user can create this kind
+ of models.
+ """
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ can, msg = model.can_create(request.user, *args, **kwargs)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_edit(model, *field_list):
+ """Decorator to check if an user can edit a model.
+ It tries to get an instance of the model, using
+ `model.get_instance(*args, **kwargs)` and assumes that the model has a
+ method `can_edit(user)` which returns `true` if the user can edit this
+ kind of models.
+ """
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ try:
+ instance = model.get_instance(*args, **kwargs)
+ except model.DoesNotExist:
+ messages.error(request, u"Entrée inexistante")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ can, msg = instance.can_edit(request.user)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ for field in field_list:
+ can_change = getattr(instance, 'can_change_' + field)
+ can, msg = can_change(request.user, *args, **kwargs)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, instance, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_change(model, *field_list):
+ """Decorator to check if an user can edit a field of a model class.
+ Difference with can_edit : take a class and not an instance
+ """
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ for field in field_list:
+ can_change = getattr(model, 'can_change_' + field)
+ can, msg = can_change(request.user, *args, **kwargs)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_delete(model):
+ """Decorator to check if an user can delete a model.
+ It tries to get an instance of the model, using
+ `model.get_instance(*args, **kwargs)` and assumes that the model has a
+ method `can_delete(user)` which returns `true` if the user can delete this
+ kind of models.
+ """
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ try:
+ instance = model.get_instance(*args, **kwargs)
+ except model.DoesNotExist:
+ messages.error(request, u"Entrée inexistante")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ can, msg = instance.can_delete(request.user)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, instance, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_delete_set(model):
+ """Decorator which returns a list of detable models by request user.
+ If none of them, return an error"""
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ all_objects = model.objects.all()
+ instances_id = []
+ for instance in all_objects:
+ can, msg = instance.can_delete(request.user)
+ if can:
+ instances_id.append(instance.id)
+ instances = model.objects.filter(id__in=instances_id)
+ if not instances:
+ messages.error(request, "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, instances, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_view(model):
+ """Decorator to check if an user can view a model.
+ It tries to get an instance of the model, using
+ `model.get_instance(*args, **kwargs)` and assumes that the model has a
+ method `can_view(user)` which returns `true` if the user can view this
+ kind of models.
+ """
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ try:
+ instance = model.get_instance(*args, **kwargs)
+ except model.DoesNotExist:
+ messages.error(request, u"Entrée inexistante")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ can, msg = instance.can_view(request.user)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, instance, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_view_all(model):
+ """Decorator to check if an user can view a class of model.
+ """
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ can, msg = model.can_view_all(request.user)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return view(request, *args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def can_view_app(app_name):
+ """Decorator to check if an user can view an application.
+ """
+ assert app_name in sys.modules.keys()
+ def decorator(view):
+ def wrapper(request, *args, **kwargs):
+ app = sys.modules[app_name]
+ can,msg = app.can_view(request.user)
+ if can:
+ return view(request, *args, **kwargs)
+ messages.error(request, msg)
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return wrapper
+ return decorator
+
+
+def can_edit_history(view):
+ """Decorator to check if an user can edit history."""
+ def wrapper(request, *args, **kwargs):
+ if request.user.has_perm('admin.change_logentry'):
+ return view(request, *args, **kwargs)
+ messages.error(
+ request,
+ "Vous ne pouvez pas éditer l'historique."
+ )
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ return wrapper
+
diff --git a/re2o/context_processors.py b/re2o/context_processors.py
index 0f9ea3fc..f696c3f4 100644
--- a/re2o/context_processors.py
+++ b/re2o/context_processors.py
@@ -39,28 +39,10 @@ def context_user(request):
messages.warning(request, global_message)
if user.is_authenticated():
interfaces = user.user_interfaces()
- is_cableur = user.is_cableur
- is_bureau = user.is_bureau
- is_bofh = user.is_bofh
- is_trez = user.is_trez
- is_infra = user.is_infra
- is_admin = user.is_admin
else:
interfaces = None
- is_cableur = False
- is_bureau = False
- is_bofh = False
- is_trez = False
- is_infra = False
- is_admin = False
return {
'request_user': user,
- 'is_cableur': is_cableur,
- 'is_bureau': is_bureau,
- 'is_bofh': is_bofh,
- 'is_trez': is_trez,
- 'is_infra': is_infra,
- 'is_admin': is_admin,
'interfaces': interfaces,
'site_name': general_options.site_name,
'ipv6_enabled': machine_options.ipv6,
diff --git a/re2o/field_permissions.py b/re2o/field_permissions.py
new file mode 100644
index 00000000..dc5466c4
--- /dev/null
+++ b/re2o/field_permissions.py
@@ -0,0 +1,79 @@
+from django.db import models
+from django import forms
+from functools import partial
+
+
+class FieldPermissionModelMixin:
+ field_permissions = {} # {'field_name': callable}
+ FIELD_PERM_CODENAME = 'can_change_{model}_{name}'
+ FIELD_PERMISSION_GETTER = 'can_change_{name}'
+ FIELD_PERMISSION_MISSING_DEFAULT = True
+
+ def has_field_perm(self, user, field):
+ if field in self.field_permissions:
+ checks = self.field_permissions[field]
+ if not isinstance(checks, (list, tuple)):
+ checks = [checks]
+
+ else:
+ checks = []
+
+ # Consult the optional field-specific hook.
+ getter_name = self.FIELD_PERMISSION_GETTER.format(name=field)
+ if hasattr(self, getter_name):
+ checks.append(getattr(self, getter_name))
+
+ # Try to find a static permission for the field
+ else:
+ perm_label = self.FIELD_PERM_CODENAME.format(**{
+ 'model': self._meta.model_name,
+ 'name': field,
+ })
+ if perm_label in dict(self._meta.permissions):
+ checks.append(perm_label)
+
+ # No requirements means no restrictions.
+ if not len(checks):
+ return self.FIELD_PERMISSION_MISSING_DEFAULT
+
+ # Try to find a user setting that qualifies them for permission.
+ for perm in checks:
+ if callable(perm):
+ result, reason = perm(user_request=user)
+ if result is not None:
+ return result
+ else:
+ result = user.has_perm(perm) # Don't supply 'obj', or else infinite recursion.
+ if result:
+ return True
+
+ # If no requirement can be met, then permission is denied.
+ return False
+
+class FieldPermissionModel(FieldPermissionModelMixin, models.Model):
+ class Meta:
+ abstract = True
+
+
+class FieldPermissionFormMixin:
+ """
+ Construit le formulaire et retire les champs interdits
+ """
+ def __init__(self, *args, **kwargs):
+ user = kwargs.pop('user')
+
+ super(FieldPermissionFormMixin, self).__init__(*args, **kwargs)
+ to_be_deleted = []
+ for name in self.fields:
+ if not self.instance.has_field_perm(user, field=name):
+ to_be_deleted.append(name)
+ for name in to_be_deleted:
+ self.remove_unauthorized_field(name)
+
+ def remove_unauthorized_field(self, name):
+ del self.fields[name]
+
+
+class FieldPermissionForm(FieldPermissionFormMixin, forms.ModelForm):
+ pass
+
diff --git a/re2o/templates/re2o/index.html b/re2o/templates/re2o/index.html
index 477c64e2..877faf10 100644
--- a/re2o/templates/re2o/index.html
+++ b/re2o/templates/re2o/index.html
@@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block title %}Accueil{% endblock %}
{% block content %}
- Bienvenue sur {{ site_name }} !
+Bienvenue sur {{ request.get_host }} !
{% for service_list in services_urls %}
diff --git a/re2o/templatetags/__init__.py b/re2o/templatetags/__init__.py
index d9561b4b..5be3ef33 100644
--- a/re2o/templatetags/__init__.py
+++ b/re2o/templatetags/__init__.py
@@ -2,19 +2,19 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
-#
+#
# Copyright © 2017 Maël Kervella
-#
+#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py
new file mode 100644
index 00000000..79bdbc5f
--- /dev/null
+++ b/re2o/templatetags/acl.py
@@ -0,0 +1,410 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Maël Kervella
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""
+Set of templatetags for using acl in templates:
+ - can_create (model)
+ - cannot_create (model)
+ - can_edit (instance)
+ - cannot_edit (instance)
+
+Some templatetags require a model to calculate the acl while others are need
+an instance of a model (either Model.can_xxx or instance.can_xxx)
+
+**Parameters**:
+ model_name or instance - Either the model_name (if templatetag is based on
+ model) or an instantiated object (if templatetag is base on instance)
+ that needs to be checked for the current user
+ args - Any other argument that is interpreted as a python object and passed
+ to the acl function (can_xxx)
+
+**Usage**:
+ {%
[arg1 [arg2 [...]]]%}
+
+ [{% acl_else %}
+ ]
+ {% acl_end %}
+
+ where is one of the templatetag names available
+ (can_xxx or cannot_xxx)
+
+**Example**:
+ {% can_create Machine targeted_user %}
+ I'm authorized to create new machines.models.for this guy \\o/
+ {% acl_else %}
+ Why can't I create a little machine for this guy ? :(
+ {% acl_end %}
+
+ {% can_edit user %}
+ Oh I can edit myself oO
+ {% acl_else %}
+ Sniff can't edit my own infos ...
+ {% acl_end %}
+
+**How to modify**:
+ To add a new acl function (can_xxx or cannot_xxx),
+ - if it's based on a model (like can_create), add an entry in
+ 'get_callback' and register your tag with the other ones juste before
+ 'acl_model_generic' definition
+ - if it's bases on an instance (like can_edit), just register yout tag with
+ the other ones juste before 'acl_instance_generic' definition
+ To add support for a new model, add an entry in 'get_model' and be sure
+ the acl function exists in the model definition
+
+"""
+import sys
+
+from django import template
+from django.template.base import Node, NodeList
+
+import cotisations
+import machines
+import preferences
+import topologie
+import users
+
+register = template.Library()
+
+MODEL_NAME = {
+ # cotisations
+ 'Facture' : cotisations.models.Facture,
+ 'Vente' : cotisations.models.Vente,
+ 'Article' : cotisations.models.Article,
+ 'Banque' : cotisations.models.Banque,
+ 'Paiement' : cotisations.models.Paiement,
+ 'Cotisation' : cotisations.models.Cotisation,
+ # machines
+ 'Machine' : machines.models.Machine,
+ 'MachineType' : machines.models.MachineType,
+ 'IpType' : machines.models.IpType,
+ 'Vlan' : machines.models.Vlan,
+ 'Nas' : machines.models.Nas,
+ 'SOA' : machines.models.SOA,
+ 'Extension' : machines.models.Extension,
+ 'Mx' : machines.models.Mx,
+ 'Ns' : machines.models.Ns,
+ 'Txt' : machines.models.Txt,
+ 'Srv' : machines.models.Srv,
+ 'Interface' : machines.models.Interface,
+ 'Domain' : machines.models.Domain,
+ 'IpList' : machines.models.IpList,
+ 'Service' : machines.models.Service,
+ 'Service_link' : machines.models.Service_link,
+ 'OuverturePortList' : machines.models.OuverturePortList,
+ 'OuverturePort' : machines.models.OuverturePort,
+ # preferences
+ 'OptionalUser': preferences.models.OptionalUser,
+ 'OptionalMachine': preferences.models.OptionalMachine,
+ 'OptionalTopologie': preferences.models.OptionalTopologie,
+ 'GeneralOption': preferences.models.GeneralOption,
+ 'Service': preferences.models.Service,
+ 'AssoOption': preferences.models.AssoOption,
+ 'MailMessageOption': preferences.models.MailMessageOption,
+ # topologie
+ 'Stack' : topologie.models.Stack,
+ 'Switch' : topologie.models.Switch,
+ 'ModelSwitch' : topologie.models.ModelSwitch,
+ 'ConstructorSwitch' : topologie.models.ConstructorSwitch,
+ 'Port' : topologie.models.Port,
+ 'Room' : topologie.models.Room,
+ # users
+ 'User' : users.models.User,
+ 'Adherent' : users.models.Adherent,
+ 'Club' : users.models.Club,
+ 'ServiceUser' : users.models.ServiceUser,
+ 'School' : users.models.School,
+ 'ListRight' : users.models.ListRight,
+ 'Ban' : users.models.Ban,
+ 'Whitelist' : users.models.Whitelist,
+}
+
+
+def get_model(model_name):
+ """Retrieve the model object from its name"""
+ try:
+ return MODEL_NAME[model_name]
+ except KeyError:
+ raise template.TemplateSyntaxError(
+ "%r is not a valid model for an acl tag" % model_name
+ )
+
+
+def get_callback(tag_name, obj=None):
+ """Return the right function to call back to check for acl"""
+
+ if tag_name == 'can_create':
+ return acl_fct(obj.can_create, False)
+ if tag_name == 'cannot_create':
+ return acl_fct(obj.can_create, True)
+ if tag_name == 'can_edit':
+ return acl_fct(obj.can_edit, False)
+ if tag_name == 'cannot_edit':
+ return acl_fct(obj.can_edit, True)
+ if tag_name == 'can_edit_all':
+ return acl_fct(obj.can_edit_all, False)
+ if tag_name == 'cannot_edit_all':
+ return acl_fct(obj.can_edit_all, True)
+ if tag_name == 'can_delete':
+ return acl_fct(obj.can_delete, False)
+ if tag_name == 'cannot_delete':
+ return acl_fct(obj.can_delete, True)
+ if tag_name == 'can_delete_all':
+ return acl_fct(obj.can_delete_all, False)
+ if tag_name == 'cannot_delete_all':
+ return acl_fct(obj.can_delete_all, True)
+ if tag_name == 'can_view':
+ return acl_fct(obj.can_view, False)
+ if tag_name == 'cannot_view':
+ return acl_fct(obj.can_view, True)
+ if tag_name == 'can_view_all':
+ return acl_fct(obj.can_view_all, False)
+ if tag_name == 'cannot_view_all':
+ return acl_fct(obj.can_view_all, True)
+ if tag_name == 'can_view_app':
+ return acl_fct(sys.modules[obj].can_view, False)
+ if tag_name == 'cannot_view_app':
+ return acl_fct(sys.modules[obj].can_view, True)
+ if tag_name == 'can_edit_history':
+ return acl_fct(lambda user:(user.has_perm('admin.change_logentry'),None),False)
+ if tag_name == 'cannot_edit_history':
+ return acl_fct(lambda user:(user.has_perm('admin.change_logentry'),None),True)
+
+ raise template.TemplateSyntaxError(
+ "%r tag is not a valid can_xxx tag" % tag_name
+ )
+
+
+def acl_fct(callback, reverse):
+ """Build a function to use as an acl checker"""
+
+ def acl_fct_normal(user, *args, **kwargs):
+ """The can_xxx checker callback"""
+ return callback(user, *args, **kwargs)
+
+ def acl_fct_reverse(user, *args, **kwargs):
+ """The cannot_xxx checker callback"""
+ can, msg = callback(user, *args, **kwargs)
+ return not can, msg
+
+ return acl_fct_reverse if reverse else acl_fct_normal
+
+
+@register.tag('can_edit_history')
+@register.tag('cannot_edit_history')
+def acl_history_filter(parser, token):
+ """Templatetag for acl checking on history."""
+ tag_name, = token.split_contents()
+
+ callback = get_callback(tag_name)
+ oknodes = parser.parse(('acl_else', 'acl_end'))
+ token = parser.next_token()
+ if token.contents == 'acl_else':
+ konodes = parser.parse(('acl_end'))
+ token = parser.next_token()
+ else:
+ konodes = NodeList()
+
+ assert token.contents == 'acl_end'
+
+ return AclNode(callback, oknodes, konodes)
+
+
+@register.tag('can_view_app')
+@register.tag('cannot_view_app')
+def acl_app_filter(parser, token):
+ """Templatetag for acl checking on applications."""
+ try:
+ tag_name, app_name = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError(
+ "%r tag require 1 argument : the application"
+ % token.contents.split()[0]
+ )
+ if not app_name in sys.modules.keys():
+ raise template.TemplateSyntaxError(
+ "%r is not a registered application for acl."
+ % app_name
+ )
+
+ callback = get_callback(tag_name, app_name)
+
+ oknodes = parser.parse(('acl_else', 'acl_end'))
+ token = parser.next_token()
+ if token.contents == 'acl_else':
+ konodes = parser.parse(('acl_end'))
+ token = parser.next_token()
+ else:
+ konodes = NodeList()
+
+ assert token.contents == 'acl_end'
+
+ return AclNode(callback, oknodes, konodes)
+
+@register.tag('can_change')
+@register.tag('cannot_change')
+def acl_change_filter(parser, token):
+ """Templatetag for acl checking a can_change_xxx function"""
+
+ try:
+ tag_content = token.split_contents()
+ tag_name = tag_content[0]
+ model_name = tag_content[1]
+ field_name = tag_content[2]
+ args = tag_content[3:]
+ except ValueError:
+ raise template.TemplateSyntaxError(
+ "%r tag require at least 2 argument : the model and the field"
+ % token.contents.split()[0]
+ )
+
+ model = get_model(model_name)
+ callback = getattr(model, 'can_change_'+field_name)
+
+ # {% can_create %}
+ oknodes = parser.parse(('acl_else', 'acl_end'))
+ token = parser.next_token()
+
+ # {% can_create_else %}
+ if token.contents == 'acl_else':
+ konodes = parser.parse(('acl_end'))
+ token = parser.next_token()
+ else:
+ konodes = NodeList()
+
+ # {% can_create_end %}
+ assert token.contents == 'acl_end'
+
+ return AclNode(callback, oknodes, konodes, *args)
+
+@register.tag('can_create')
+@register.tag('cannot_create')
+@register.tag('can_edit_all')
+@register.tag('cannot_edit_all')
+@register.tag('can_delete_all')
+@register.tag('cannot_delete_all')
+@register.tag('can_view_all')
+@register.tag('cannot_view_all')
+def acl_model_filter(parser, token):
+ """Generic definition of an acl templatetag for acl based on model"""
+
+ try:
+ tag_content = token.split_contents()
+ tag_name = tag_content[0]
+ model_name = tag_content[1]
+ args = tag_content[2:]
+ except ValueError:
+ raise template.TemplateSyntaxError(
+ "%r tag require at least 1 argument : the model"
+ % token.contents.split()[0]
+ )
+
+ model = get_model(model_name)
+ callback = get_callback(tag_name, model)
+
+ # {% can_create %}
+ oknodes = parser.parse(('acl_else', 'acl_end'))
+ token = parser.next_token()
+
+ # {% can_create_else %}
+ if token.contents == 'acl_else':
+ konodes = parser.parse(('acl_end'))
+ token = parser.next_token()
+ else:
+ konodes = NodeList()
+
+ # {% can_create_end %}
+ assert token.contents == 'acl_end'
+
+ return AclNode(callback, oknodes, konodes, *args)
+
+
+@register.tag('can_edit')
+@register.tag('cannot_edit')
+@register.tag('can_delete')
+@register.tag('cannot_delete')
+@register.tag('can_view')
+@register.tag('cannot_view')
+def acl_instance_filter(parser, token):
+ """Generic definition of an acl templatetag for acl based on instance"""
+
+ try:
+ tag_content = token.split_contents()
+ tag_name = tag_content[0]
+ instance_name = tag_content[1]
+ args = tag_content[2:]
+ except ValueError:
+ raise template.TemplateSyntaxError(
+ "%r tag require at least 1 argument : the instance"
+ % token.contents.split()[0]
+ )
+
+ # {% can_create %}
+ oknodes = parser.parse(('acl_else', 'acl_end'))
+ token = parser.next_token()
+
+ # {% can_create_else %}
+ if token.contents == 'acl_else':
+ konodes = parser.parse(('acl_end'))
+ token = parser.next_token()
+ else:
+ konodes = NodeList()
+
+ # {% can_create_end %}
+ assert token.contents == 'acl_end'
+
+ return AclInstanceNode(tag_name, instance_name, oknodes, konodes, *args)
+
+
+class AclNode(Node):
+ """A node for the compiled ACL block when acl callback doesn't require
+ context."""
+
+ def __init__(self, callback, oknodes, konodes, *args):
+ self.callback = callback
+ self.oknodes = oknodes
+ self.konodes = konodes
+ self.args = [template.Variable(arg) for arg in args]
+
+ def render(self, context):
+ resolved_args = [arg.resolve(context) for arg in self.args]
+ can, _ = self.callback(context['user'], *(resolved_args))
+ if can:
+ return self.oknodes.render(context)
+ return self.konodes.render(context)
+
+
+class AclInstanceNode(Node):
+ """A node for the compiled ACL block when acl is based on instance"""
+
+ def __init__(self, tag_name, instance_name, oknodes, konodes, *args):
+ self.tag_name = tag_name
+ self.instance = template.Variable(instance_name)
+ self.oknodes = oknodes
+ self.konodes = konodes
+ self.args = [template.Variable(arg) for arg in args]
+
+ def render(self, context):
+ callback = get_callback(self.tag_name, self.instance.resolve(context))
+ resolved_args = [arg.resolve(context) for arg in self.args]
+ can, _ = callback(context['user'], *(resolved_args))
+ if can:
+ return self.oknodes.render(context)
+ return self.konodes.render(context)
diff --git a/re2o/utils.py b/re2o/utils.py
index a6e5c851..23c6e052 100644
--- a/re2o/utils.py
+++ b/re2o/utils.py
@@ -39,6 +39,9 @@ from __future__ import unicode_literals
from django.utils import timezone
from django.db.models import Q
+from django.contrib import messages
+from django.shortcuts import redirect
+from django.urls import reverse
from cotisations.models import Cotisation, Facture, Paiement, Vente
from machines.models import Domain, Interface, Machine
diff --git a/re2o/views.py b/re2o/views.py
index 9cab6273..dce28b5d 100644
--- a/re2o/views.py
+++ b/re2o/views.py
@@ -26,10 +26,17 @@ les views
from __future__ import unicode_literals
-from django.shortcuts import render
+from django.http import Http404
+from django.urls import reverse
+from django.shortcuts import render, redirect
from django.template.context_processors import csrf
+from django.contrib.auth.decorators import login_required, permission_required
+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+from reversion.models import Version
+from django.contrib import messages
from preferences.models import Service
-
+from preferences.models import OptionalUser, GeneralOption
+import users, preferences, cotisations, topologie, machines
def form(ctx, template, request):
"""Form générique, raccourci importé par les fonctions views du site"""
@@ -44,3 +51,106 @@ def index(request):
for indice, serv in enumerate(Service.objects.all()):
services[indice % 3].append(serv)
return form({'services_urls': services}, 're2o/index.html', request)
+
+#: Binding the corresponding char sequence of history url to re2o models.
+HISTORY_BIND = {
+ 'users' : {
+ 'user' : users.models.User,
+ 'ban' : users.models.Ban,
+ 'whitelist' : users.models.Whitelist,
+ 'school' : users.models.School,
+ 'listright' : users.models.ListRight,
+ 'serviceuser' : users.models.ServiceUser,
+ },
+ 'preferences' : {
+ 'service' : preferences.models.Service,
+ },
+ 'cotisations' : {
+ 'facture' : cotisations.models.Facture,
+ 'article' : cotisations.models.Article,
+ 'paiement' : cotisations.models.Paiement,
+ 'banque' : cotisations.models.Banque,
+ },
+ 'topologie' : {
+ 'switch' : topologie.models.Switch,
+ 'port' : topologie.models.Port,
+ 'room' : topologie.models.Room,
+ 'stack' : topologie.models.Stack,
+ 'model_switch' : topologie.models.ModelSwitch,
+ 'constructor_switch' : topologie.models.ConstructorSwitch,
+ },
+ 'machines' : {
+ 'machine' : machines.models.Machine,
+ 'interface' : machines.models.Interface,
+ 'alias' : machines.models.Domain,
+ 'machinetype' : machines.models.MachineType,
+ 'iptype' : machines.models.IpType,
+ 'extension' : machines.models.Extension,
+ 'soa' : machines.models.SOA,
+ 'mx' : machines.models.Mx,
+ 'txt' : machines.models.Txt,
+ 'srv' : machines.models.Srv,
+ 'ns' : machines.models.Ns,
+ 'service' : machines.models.Service,
+ 'vlan' : machines.models.Vlan,
+ 'nas' : machines.models.Vlan,
+ },
+}
+
+@login_required
+def history(request, application, object_name, object_id):
+ """Render history for a model.
+
+ The model is determined using the `HISTORY_BIND` dictionnary if none is
+ found, raises a Http404. The view checks if the user is allowed to see the
+ history using the `can_view` method of the model.
+
+ Args:
+ request: The request sent by the user.
+ object_name: Name of the model.
+ object_id: Id of the object you want to acces history.
+
+ Returns:
+ The rendered page of history if access is granted, else the user is
+ redirected to their profile page, with an error message.
+
+ Raises:
+ Http404: This kind of models doesn't have history.
+ """
+ try:
+ model = HISTORY_BIND[application][object_name]
+ except KeyError as e:
+ raise Http404(u"Il n'existe pas d'historique pour ce modèle.")
+ try:
+ instance = model.get_instance(object_id)
+ except model.DoesNotExist:
+ messages.error(request, u"Entrée inexistante")
+ return redirect(reverse('users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ can, msg = instance.can_view(request.user)
+ if not can:
+ messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
+ return redirect(reverse(
+ 'users:profil',
+ kwargs={'userid':str(request.user.id)}
+ ))
+ options, _created = GeneralOption.objects.get_or_create()
+ pagination_number = options.pagination_number
+ reversions = Version.objects.get_for_object(instance)
+ paginator = Paginator(reversions, pagination_number)
+ page = request.GET.get('page')
+ try:
+ reversions = paginator.page(page)
+ except PageNotAnInteger:
+ # If page is not an integer, deliver first page.
+ reversions = paginator.page(1)
+ except EmptyPage:
+ # If page is out of range (e.g. 9999), deliver last page of result
+ reversions = paginator.page(paginator.num_pages)
+ return render(
+ request,
+ 're2o/history.html',
+ {'reversions': reversions, 'object': instance}
+ )
+
diff --git a/search/__init__.py b/search/__init__.py
index fc1be5d7..df6e4256 100644
--- a/search/__init__.py
+++ b/search/__init__.py
@@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from .acl import *
diff --git a/search/acl.py b/search/acl.py
new file mode 100644
index 00000000..5c80e473
--- /dev/null
+++ b/search/acl.py
@@ -0,0 +1,39 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""search.acl
+
+Here are defined some functions to check acl on the application.
+"""
+
+def can_view(user):
+ """Check if an user can view the application.
+
+ Args:
+ user: The user who wants to view the application.
+
+ Returns:
+ A couple (allowed, msg) where allowed is a boolean which is True if
+ viewing is granted and msg is a message (can be None).
+ """
+ return True, None
diff --git a/search/views.py b/search/views.py
index a561dd11..5b88febd 100644
--- a/search/views.py
+++ b/search/views.py
@@ -120,7 +120,7 @@ def finish_results(results, col, order):
return results
-def search_single_word(word, filters, is_cableur, user_id,
+def search_single_word(word, filters, user,
start, end, user_state, aff):
""" Construct the correct filters to match differents fields of some models
with the given query according to the given filters.
@@ -144,8 +144,8 @@ def search_single_word(word, filters, is_cableur, user_id,
adherent__room__name__icontains=word
)
) & Q(state__in=user_state)
- if not is_cableur:
- filter_users &= Q(id=user_id)
+ if not User.can_view_all(user)[0]:
+ filter_users &= Q(id=user.id)
filters['users'] |= filter_users
# Machines
@@ -167,8 +167,8 @@ def search_single_word(word, filters, is_cableur, user_id,
) | Q(
interface__ipv4__ipv4__icontains=word
)
- if not is_cableur:
- filter_machines &= Q(user__id=user_id)
+ if not Machine.can_view_all(user)[0]:
+ filter_machines &= Q(user__id=user.id)
filters['machines'] |= filter_machines
# Factures
@@ -243,7 +243,7 @@ def search_single_word(word, filters, is_cableur, user_id,
filters['whitelists'] |= filter_whitelists
# Rooms
- if '5' in aff and is_cableur:
+ if '5' in aff and Room.can_view_all(user):
filter_rooms = Q(
details__icontains=word
) | Q(
@@ -254,7 +254,7 @@ def search_single_word(word, filters, is_cableur, user_id,
filters['rooms'] |= filter_rooms
# Switch ports
- if '6' in aff and is_cableur:
+ if '6' in aff and User.can_view_all(user):
filter_ports = Q(
room__name__icontains=word
) | Q(
@@ -275,7 +275,7 @@ def search_single_word(word, filters, is_cableur, user_id,
filters['ports'] |= filter_ports
# Switches
- if '7' in aff and is_cableur:
+ if '7' in aff and Switch.can_view_all(user):
filter_switches = Q(
switch_interface__domain__name__icontains=word
) | Q(
@@ -374,8 +374,7 @@ def get_results(query, request, params):
filters = search_single_word(
word,
filters,
- request.user.has_perms(('cableur',)),
- request.user.id,
+ request.user,
start,
end,
user_state,
diff --git a/templates/base.html b/templates/base.html
index 01f1ac4e..b3a9bb5d 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{# Load the tag library #}
{% load bootstrap3 %}
-
+{% load acl %}
@@ -73,13 +73,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Mon profil
- {% if is_cableur %}
+ {% can_view_app users %}
+
Adhérents
+ {% acl_end %}
+ {% can_view_app machines %}
Machines
+ {% acl_end %}
+ {% can_view_app cotisations %}
Cotisations
+ {% acl_end %}
+ {% can_view_app topologie %}
Topologie
+ {% acl_end %}
+ {% can_view_app logs %}
Statistiques
- {% endif %}
+ {% acl_end %}
@@ -205,7 +214,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
-
Re2o 2016 - Gabriel Détraz, Goulven Kermarec , Augustin Lemesle, Maël Kervella
+
Re2o 2016-2018 - Gabriel Détraz, Goulven Kermarec , Augustin Lemesle, Maël Kervella, Hugo Levy-Falk
{# Read the documentation for more information #}
diff --git a/topologie/__init__.py b/topologie/__init__.py
index fc1be5d7..df6e4256 100644
--- a/topologie/__init__.py
+++ b/topologie/__init__.py
@@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from .acl import *
diff --git a/topologie/acl.py b/topologie/acl.py
new file mode 100644
index 00000000..aa6adbde
--- /dev/null
+++ b/topologie/acl.py
@@ -0,0 +1,40 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""topologie.acl
+
+Here are defined some functions to check acl on the application.
+"""
+
+def can_view(user):
+ """Check if an user can view the application.
+
+ Args:
+ user: The user who wants to view the application.
+
+ Returns:
+ A couple (allowed, msg) where allowed is a boolean which is True if
+ viewing is granted and msg is a message (can be None).
+ """
+ can = user.has_module_perms('topologie')
+ return can, None if can else "Vous ne pouvez pas voir cette application."
diff --git a/topologie/migrations/0033_auto_20171231_1743.py b/topologie/migrations/0033_auto_20171231_1743.py
new file mode 100644
index 00000000..c79412d7
--- /dev/null
+++ b/topologie/migrations/0033_auto_20171231_1743.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 16:43
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('topologie', '0032_auto_20171026_0338'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='constructorswitch',
+ options={'permissions': (('view_constructorswitch', 'Peut voir un objet constructorswitch'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='modelswitch',
+ options={'permissions': (('view_modelswitch', 'Peut voir un objet modelswitch'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='port',
+ options={'permissions': (('view_port', 'Peut voir un objet port'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='room',
+ options={'ordering': ['name'], 'permissions': (('view_room', 'Peut voir un objet chambre'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='stack',
+ options={'permissions': (('view_stack', 'Peut voir un objet stack'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='switch',
+ options={'permissions': (('view_switch', 'Peut voir un objet switch'),)},
+ ),
+ ]
diff --git a/topologie/models.py b/topologie/models.py
index d63ccf18..c2b7ee0d 100644
--- a/topologie/models.py
+++ b/topologie/models.py
@@ -60,6 +60,38 @@ class Stack(models.Model):
member_id_min = models.PositiveIntegerField()
member_id_max = models.PositiveIntegerField()
+ class Meta:
+ permissions = (
+ ("view_stack", "Peut voir un objet stack"),
+ )
+
+ def get_instance(stack_id, *args, **kwargs):
+ return Stack.objects.get(pk=stack_id)
+
+ def can_create(user_request, *args, **kwargs):
+ return user_request.has_perm('topologie.add_stack') , u"Vous n'avez pas le droit\
+ de créer un stack"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.change_stack'):
+ return False, u"Vous n'avez pas le droit d'éditer des stack"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.delete_stack'):
+ return False, u"Vous n'avez pas le droit de supprimer une stack"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_stack'):
+ return False, u"Vous n'avez pas le droit de voir une stack"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_stack'):
+ return False, u"Vous n'avez pas le droit de voir une stack"
+ return True, None
+
def __str__(self):
return " ".join([self.name, self.stack_id])
@@ -113,7 +145,37 @@ class Switch(models.Model):
class Meta:
unique_together = ('stack', 'stack_member_id')
+ permissions = (
+ ("view_switch", "Peut voir un objet switch"),
+ )
+ def get_instance(switch_id, *args, **kwargs):
+ return Switch.objects.get(pk=switch_id)
+
+ def can_create(user_request, *args, **kwargs):
+ return user_request.has_perm('topologie.add_switch') , u"Vous n'avez pas le droit\
+ de créer un switch"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.change_switch'):
+ return False, u"Vous n'avez pas le droit d'éditer des switch"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.delete_switch'):
+ return False, u"Vous n'avez pas le droit de supprimer un switch"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_switch'):
+ return False, u"Vous n'avez pas le droit de coir les switch"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_switch'):
+ return False, u"Vous n'avez pas le droit de coir les switch"
+ return True, None
+
def __str__(self):
return self.location + ' ' + str(self.switch_interface)
@@ -167,6 +229,38 @@ class ModelSwitch(models.Model):
on_delete=models.PROTECT
)
+ class Meta:
+ permissions = (
+ ("view_modelswitch", "Peut voir un objet modelswitch"),
+ )
+
+ def get_instance(model_switch_id, *args, **kwargs):
+ return ModelSwitch.objects.get(pk=model_switch_id)
+
+ def can_create(user_request, *args, **kwargs):
+ return user_request.has_perm('topologie.add_modelswitch') , u"Vous n'avez pas le droit\
+ de créer un modèle de switch"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.change_modelswitch'):
+ return False, u"Vous n'avez pas le droit d'éditer des modèle de switchs"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.delete_modelswitch'):
+ return False, u"Vous n'avez pas le droit de supprimer un modèle switch"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_modelswitch'):
+ return False, u"Vous n'avez pas le droit de voir un modèle switch"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_modelswitch'):
+ return False, u"Vous n'avez pas le droit de voir un modèle switch"
+ return True, None
+
def __str__(self):
return str(self.constructor) + ' ' + self.reference
@@ -176,6 +270,39 @@ class ConstructorSwitch(models.Model):
PRETTY_NAME = "Constructeur de switch"
name = models.CharField(max_length=255)
+ class Meta:
+ permissions = (
+ ("view_constructorswitch", "Peut voir un objet constructorswitch"),
+ )
+
+ def get_instance(constructor_switch_id, *args, **kwargs):
+ return ConstructorSwitch.objects.get(pk=constructor_switch_id)
+
+ def can_create(user_request, *args, **kwargs):
+ return user_request.has_perm('topologie.add_constructorswitch') , u"Vous n'avez pas le droit\
+ de créer un constructeur de switch"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.change_constructorswitch'):
+ return False, u"Vous n'avez pas le droit d'éditer des\
+ constructeurs de switchs"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.delete_constructorswitch'):
+ return False, u"Vous n'avez pas le droit de supprimer un constructeur"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_constructorswitch'):
+ return False, u"Vous n'avez pas le droit de voir un constructeur"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_constructorswitch'):
+ return False, u"Vous n'avez pas le droit de voir un constructeur"
+ return True, None
+
def __str__(self):
return self.name
@@ -239,7 +366,43 @@ class Port(models.Model):
class Meta:
unique_together = ('switch', 'port')
+ permissions = (
+ ("view_port", "Peut voir un objet port"),
+ )
+ def get_instance(port_id, *args, **kwargs):
+ return Port.objects\
+ .select_related('switch__switch_interface__domain__extension')\
+ .select_related('machine_interface__domain__extension')\
+ .select_related('machine_interface__switch')\
+ .select_related('room')\
+ .select_related('related')\
+ .get(pk=port_id)
+
+ def can_create(user_request, *args, **kwargs):
+ return user_request.has_perm('topologie.add_port') , u"Vous n'avez pas le droit\
+ de créer un port"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.change_port'):
+ return False, u"Vous n'avez pas le droit d'éditer des ports"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.delete_port'):
+ return False, u"Vous n'avez pas le droit de supprimer un port"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_port'):
+ return False, u"Vous n'avez pas le droit de voir les ports"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_port'):
+ return False, u"Vous n'avez pas le droit de voir les ports"
+ return True, None
+
def make_port_related(self):
""" Synchronise le port distant sur self"""
related_port = self.related
@@ -293,6 +456,36 @@ class Room(models.Model):
class Meta:
ordering = ['name']
+ permissions = (
+ ("view_room", "Peut voir un objet chambre"),
+ )
+
+ def get_instance(room_id, *args, **kwargs):
+ return Room.objects.get(pk=room_id)
+
+ def can_create(user_request, *args, **kwargs):
+ return user_request.has_perm('topologie.add_room') , u"Vous n'avez pas le droit\
+ de créer une chambre"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.change_room'):
+ return False, u"Vous n'avez pas le droit d'éditer une chambre"
+ return True, None
+
+ def can_delete(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.delete_room'):
+ return False, u"Vous n'avez pas le droit de supprimer une chambre"
+ return True, None
+
+ def can_view_all(user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_room'):
+ return False, u"Vous n'avez pas le droit de voir les chambres"
+ return True, None
+
+ def can_view(self, user_request, *args, **kwargs):
+ if not user_request.has_perm('topologie.view_room'):
+ return False, u"Vous n'avez pas le droit de voir les chambres"
+ return True, None
def __str__(self):
return self.name
diff --git a/topologie/templates/topologie/aff_chambres.html b/topologie/templates/topologie/aff_chambres.html
index 5e488606..6d0cee37 100644
--- a/topologie/templates/topologie/aff_chambres.html
+++ b/topologie/templates/topologie/aff_chambres.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
{% if room_list.paginator %}
{% include "pagination.html" with list=room_list %}
{% endif %}
@@ -42,14 +44,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit room %}
+ {% acl_end %}
+ {% can_delete room %}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/aff_constructor_switch.html b/topologie/templates/topologie/aff_constructor_switch.html
index 94d63b39..9b601e1b 100644
--- a/topologie/templates/topologie/aff_constructor_switch.html
+++ b/topologie/templates/topologie/aff_constructor_switch.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
{% if constructor_switch_list.paginator %}
{% include "pagination.html" with list=constructor_switch_list %}
{% endif %}
@@ -40,14 +42,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit constructor_switch %}
+ {% acl_end %}
+ {% can_delete constructor_switch %}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/aff_model_switch.html b/topologie/templates/topologie/aff_model_switch.html
index 97e45fcd..f6c0d3b8 100644
--- a/topologie/templates/topologie/aff_model_switch.html
+++ b/topologie/templates/topologie/aff_model_switch.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
{% if model_switch_list.paginator %}
{% include "pagination.html" with list=model_switch_list %}
{% endif %}
@@ -42,14 +44,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit model_switch %}
+ {% acl_end %}
+ {% can_delete model_switch %}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html
index 6d1ca08e..61c4bf4b 100644
--- a/topologie/templates/topologie/aff_port.html
+++ b/topologie/templates/topologie/aff_port.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -60,14 +62,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit port %}
+ {% acl_end %}
+ {% can_delete port %}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/aff_stacks.html b/topologie/templates/topologie/aff_stacks.html
index 1a9d316e..8d91d41c 100644
--- a/topologie/templates/topologie/aff_stacks.html
+++ b/topologie/templates/topologie/aff_stacks.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -46,14 +48,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit stack %}
+ {% acl_end %}
+ {% can_delete stack %}
- {% endif %}
+ {% acl_end %}
{% endif %}
@@ -67,14 +71,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit stack %}
+ {% acl_end %}
+ {% can_delete stack %}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html
index e1e818a1..38980736 100644
--- a/topologie/templates/topologie/aff_switch.html
+++ b/topologie/templates/topologie/aff_switch.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
{% if switch_list.paginator %}
{% include "pagination.html" with list=switch_list %}
{% endif %}
@@ -56,11 +58,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{switch.details}}
{% include 'buttons/history.html' with href='topologie:history' name='switch' id=switch.pk%}
- {% if is_infra %}
- {% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.pk %}
+ {% can_edit switch %}
+ {% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.pk %}
+ {% acl_end %}
+ {% can_delete switch %}
{% include 'buttons/suppr.html' with href='machines:del-interface' id=switch.switch_interface.id %}
+ {% acl_end %}
+ {% can_create Port %}
{% include 'buttons/add.html' with href='topologie:create-ports' id=switch.pk desc='Création de ports'%}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/edit_stack_sw.html b/topologie/templates/topologie/edit_stack_sw.html
index e4ca286d..04987add 100644
--- a/topologie/templates/topologie/edit_stack_sw.html
+++ b/topologie/templates/topologie/edit_stack_sw.html
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
+{% load acl %}
+
@@ -39,14 +41,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% if is_infra %}
+ {% can_edit stack %}
+ {% acl_end %}
+ {% can_delete stack %}
- {% endif %}
+ {% acl_end %}
{% endfor %}
diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html
index 6b17b6de..11f533c4 100644
--- a/topologie/templates/topologie/index.html
+++ b/topologie/templates/topologie/index.html
@@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Switchs{% endblock %}
{% block content %}
Switchs
-{% if is_infra %}
+{% can_create Switch %}
Ajouter un switch
-{% endif %}
+{% acl_end %}
{% include "topologie/aff_switch.html" with switch_list=switch_list %}
diff --git a/topologie/templates/topologie/index_model_switch.html b/topologie/templates/topologie/index_model_switch.html
index 784b5ea6..e185a9b8 100644
--- a/topologie/templates/topologie/index_model_switch.html
+++ b/topologie/templates/topologie/index_model_switch.html
@@ -24,21 +24,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Modèles de switches{% endblock %}
{% block content %}
Modèles de switches
-{% if is_infra %}
+{% can_create ModelSwitch %}
Ajouter un modèle
-{% endif %}
+{% acl_end %}
{% include "topologie/aff_model_switch.html" with model_switch_list=model_switch_list %}
Constructeurs de switches
-{% if is_infra %}
+{% can_create ConstructorSwitch %}
Ajouter un constructeur
-{% endif %}
+{% acl_end %}
{% include "topologie/aff_constructor_switch.html" with constructor_switch_list=constructor_switch_list %}
diff --git a/topologie/templates/topologie/index_p.html b/topologie/templates/topologie/index_p.html
index 3f9356f2..3cea6968 100644
--- a/topologie/templates/topologie/index_p.html
+++ b/topologie/templates/topologie/index_p.html
@@ -24,16 +24,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Ports du switch{% endblock %}
{% block content %}
Switch {{ nom_switch }}
-{% if is_infra %}
Editer
+{% can_create Port %}
Ajouter un port
+{% acl_end %}
Ajouter des ports
-{% endif %}
{% include "topologie/aff_port.html" with port_list=port_list %}
diff --git a/topologie/templates/topologie/index_room.html b/topologie/templates/topologie/index_room.html
index bff4b1c2..15493d92 100644
--- a/topologie/templates/topologie/index_room.html
+++ b/topologie/templates/topologie/index_room.html
@@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Chambres{% endblock %}
{% block content %}
Chambres
-{% if is_infra %}
+{% can_create Room %}
Ajouter une chambre
-{% endif %}
+{% acl_end %}
{% include "topologie/aff_chambres.html" with room_list=room_list %}
diff --git a/topologie/templates/topologie/index_stack.html b/topologie/templates/topologie/index_stack.html
index b358e07d..fb39009d 100644
--- a/topologie/templates/topologie/index_stack.html
+++ b/topologie/templates/topologie/index_stack.html
@@ -24,14 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Stacks{% endblock %}
{% block content %}
Stacks
-{% if is_infra %}
+{% can_create Stack %}
Ajouter une stack
-{% endif %}
+{% acl_end %}
{% include "topologie/aff_stacks.html" with stack_list=stack_list %}
diff --git a/topologie/urls.py b/topologie/urls.py
index 752ebcc5..3caf22c5 100644
--- a/topologie/urls.py
+++ b/topologie/urls.py
@@ -30,6 +30,7 @@ from __future__ import unicode_literals
from django.conf.urls import url
+import re2o
from . import views
urlpatterns = [
@@ -45,24 +46,12 @@ urlpatterns = [
url(r'^switch/(?P[0-9]+)$',
views.index_port,
name='index-port'),
- url(r'^history/(?Pswitch)/(?P[0-9]+)$',
- views.history,
- name='history'),
- url(r'^history/(?Pport)/(?P[0-9]+)$',
- views.history,
- name='history'),
- url(r'^history/(?Proom)/(?P[0-9]+)$',
- views.history,
- name='history'),
- url(r'^history/(?Pstack)/(?P[0-9]+)$',
- views.history,
- name='history'),
- url(r'^history/(?Pmodel_switch)/(?P[0-9]+)$',
- views.history,
- name='history'),
- url(r'^history/(?Pconstructor_switch)/(?P[0-9]+)$',
- views.history,
- name='history'),
+ url(
+ r'^history/(?P\w+)/(?P[0-9]+)$',
+ re2o.views.history,
+ name='history',
+ kwargs={'application':'topologie'},
+ ),
url(r'^edit_port/(?P[0-9]+)$', views.edit_port, name='edit-port'),
url(r'^new_port/(?P[0-9]+)$', views.new_port, name='new-port'),
url(r'^del_port/(?P[0-9]+)$', views.del_port, name='del-port'),
diff --git a/topologie/views.py b/topologie/views.py
index 43174249..25cc8aa2 100644
--- a/topologie/views.py
+++ b/topologie/views.py
@@ -60,12 +60,19 @@ from topologie.forms import (
AddPortForm,
EditRoomForm,
StackForm,
- EditModelSwitchForm,
+ EditModelSwitchForm,
EditConstructorSwitchForm,
CreatePortsForm
)
from users.views import form
from re2o.utils import SortTable
+from re2o.acl import (
+ can_create,
+ can_edit,
+ can_delete,
+ can_view,
+ can_view_all,
+)
from machines.forms import (
DomainForm,
NewMachineForm,
@@ -78,7 +85,7 @@ from preferences.models import AssoOption, GeneralOption
@login_required
-@permission_required('cableur')
+@can_view_all(Switch)
def index(request):
""" Vue d'affichage de tous les swicthes"""
switch_list = Switch.objects\
@@ -110,76 +117,10 @@ def index(request):
@login_required
-@permission_required('cableur')
-def history(request, object_name, object_id):
- """ Vue générique pour afficher l'historique complet d'un objet"""
- if object_name == 'switch':
- try:
- object_instance = Switch.objects.get(pk=object_id)
- except Switch.DoesNotExist:
- messages.error(request, "Switch inexistant")
- return redirect(reverse('topologie:index'))
- elif object_name == 'port':
- try:
- object_instance = Port.objects.get(pk=object_id)
- except Port.DoesNotExist:
- messages.error(request, "Port inexistant")
- return redirect(reverse('topologie:index'))
- elif object_name == 'room':
- try:
- object_instance = Room.objects.get(pk=object_id)
- except Room.DoesNotExist:
- messages.error(request, "Chambre inexistante")
- return redirect(reverse('topologie:index'))
- elif object_name == 'stack':
- try:
- object_instance = Stack.objects.get(pk=object_id)
- except Room.DoesNotExist:
- messages.error(request, "Stack inexistante")
- return redirect(reverse('topologie:index'))
- elif object_name == 'model_switch':
- try:
- object_instance = ModelSwitch.objects.get(pk=object_id)
- except ModelSwitch.DoesNotExist:
- messages.error(request, "SwitchModel inexistant")
- return redirect(reverse('topologie:index'))
- elif object_name == 'constructor_switch':
- try:
- object_instance = ConstructorSwitch.objects.get(pk=object_id)
- except ConstructorSwitch.DoesNotExist:
- messages.error(request, "SwitchConstructor inexistant")
- return redirect(reverse('topologie:index'))
- else:
- messages.error(request, "Objet inconnu")
- return redirect(reverse('topologie:index'))
- options, _created = GeneralOption.objects.get_or_create()
- pagination_number = options.pagination_number
- reversions = Version.objects.get_for_object(object_instance)
- paginator = Paginator(reversions, pagination_number)
- page = request.GET.get('page')
- try:
- reversions = paginator.page(page)
- except PageNotAnInteger:
- # If page is not an integer, deliver first page.
- reversions = paginator.page(1)
- except EmptyPage:
- # If page is out of range (e.g. 9999), deliver last page of results.
- reversions = paginator.page(paginator.num_pages)
- return render(request, 're2o/history.html', {
- 'reversions': reversions,
- 'object': object_instance
- })
-
-
-@login_required
-@permission_required('cableur')
-def index_port(request, switch_id):
+@can_view_all(Port)
+@can_view(Switch)
+def index_port(request, switch, switch_id):
""" Affichage de l'ensemble des ports reliés à un switch particulier"""
- try:
- switch = Switch.objects.get(pk=switch_id)
- except Switch.DoesNotExist:
- messages.error(request, u"Switch inexistant")
- return redirect(reverse('topologie:index'))
port_list = Port.objects.filter(switch=switch)\
.select_related('room')\
.select_related('machine_interface__domain__extension')\
@@ -202,7 +143,7 @@ def index_port(request, switch_id):
@login_required
-@permission_required('cableur')
+@can_view_all(Room)
def index_room(request):
""" Affichage de l'ensemble des chambres"""
room_list = Room.objects
@@ -230,7 +171,7 @@ def index_room(request):
@login_required
-@permission_required('infra')
+@can_view_all(Stack)
def index_stack(request):
"""Affichage de la liste des stacks (affiche l'ensemble des switches)"""
stack_list = Stack.objects\
@@ -247,7 +188,8 @@ def index_stack(request):
@login_required
-@permission_required('cableur')
+@can_view_all(ModelSwitch)
+@can_view_all(ConstructorSwitch)
def index_model_switch(request):
""" Affichage de l'ensemble des modèles de switches"""
model_switch_list = ModelSwitch.objects
@@ -271,7 +213,7 @@ def index_model_switch(request):
@login_required
-@permission_required('infra')
+@can_create(Port)
def new_port(request, switch_id):
""" Nouveau port"""
try:
@@ -292,28 +234,18 @@ def new_port(request, switch_id):
except IntegrityError:
messages.error(request, "Ce port existe déjà")
return redirect(reverse(
- 'topologie:index-port',
+ 'topologie:index-port',
kwargs={'switch_id':switch_id}
))
return form({'id_switch': switch_id,'topoform': port}, 'topologie/topo.html', request)
@login_required
-@permission_required('infra')
-def edit_port(request, port_id):
+@can_edit(Port)
+def edit_port(request, port_object, port_id):
""" Edition d'un port. Permet de changer le switch parent et
l'affectation du port"""
- try:
- port_object = Port.objects\
- .select_related('switch__switch_interface__domain__extension')\
- .select_related('machine_interface__domain__extension')\
- .select_related('machine_interface__switch')\
- .select_related('room')\
- .select_related('related')\
- .get(pk=port_id)
- except Port.DoesNotExist:
- messages.error(request, u"Port inexistant")
- return redirect(reverse('topologie:index'))
+
port = EditPortForm(request.POST or None, instance=port_object)
if port.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -331,14 +263,9 @@ def edit_port(request, port_id):
@login_required
-@permission_required('infra')
-def del_port(request, port_id):
+@can_delete(Port)
+def del_port(request, port, port_id):
""" Supprime le port"""
- try:
- port = Port.objects.get(pk=port_id)
- except Port.DoesNotExist:
- messages.error(request, u"Port inexistant")
- return redirect(reverse('topologie:index'))
if request.method == "POST":
try:
with transaction.atomic(), reversion.create_revision():
@@ -357,7 +284,7 @@ def del_port(request, port_id):
@login_required
-@permission_required('infra')
+@can_create(Stack)
def new_stack(request):
"""Ajoute un nouveau stack : stack_id_min, max, et nombre de switches"""
stack = StackForm(request.POST or None)
@@ -371,14 +298,10 @@ def new_stack(request):
@login_required
-@permission_required('infra')
-def edit_stack(request, stack_id):
+@can_edit(Stack)
+def edit_stack(request, stack, stack_id):
"""Edition d'un stack (nombre de switches, nom...)"""
- try:
- stack = Stack.objects.get(pk=stack_id)
- except Stack.DoesNotExist:
- messages.error(request, u"Stack inexistante")
- return redirect(reverse('topologie:index-stack'))
+
stack = StackForm(request.POST or None, instance=stack)
if stack.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -394,14 +317,9 @@ def edit_stack(request, stack_id):
@login_required
-@permission_required('infra')
-def del_stack(request, stack_id):
+@can_delete(Stack)
+def del_stack(request, stack, stack_id):
"""Supprime un stack"""
- try:
- stack = Stack.objects.get(pk=stack_id)
- except Stack.DoesNotExist:
- messages.error(request, u"Stack inexistante")
- return redirect(reverse('topologie:index-stack'))
if request.method == "POST":
try:
with transaction.atomic(), reversion.create_revision():
@@ -412,19 +330,15 @@ def del_stack(request, stack_id):
except ProtectedError:
messages.error(request, "La stack %s est affectée à un autre\
objet, impossible de la supprimer" % stack)
- return redirect(reverse('topologie:index-stack'))
+ return redirect(reverse('topologie:index-stack'))
return form({'objet': stack}, 'topologie/delete.html', request)
@login_required
-@permission_required('infra')
-def edit_switchs_stack(request, stack_id):
+@can_edit(Stack)
+def edit_switchs_stack(request, stack, stack_id):
"""Permet d'éditer la liste des switches dans une stack et l'ajouter"""
- try:
- stack = Stack.objects.get(pk=stack_id)
- except Stack.DoesNotExist:
- messages.error(request, u"Stack inexistante")
- return redirect(reverse('topologie:index-stack'))
+
if request.method == "POST":
pass
else:
@@ -434,16 +348,19 @@ def edit_switchs_stack(request, stack_id):
@login_required
-@permission_required('infra')
+@can_create(Switch)
def new_switch(request):
""" Creation d'un switch. Cree en meme temps l'interface et la machine
associée. Vue complexe. Appelle successivement les 4 models forms
adaptés : machine, interface, domain et switch"""
switch = NewSwitchForm(request.POST or None)
- machine = NewMachineForm(request.POST or None)
+ machine = NewMachineForm(
+ request.POST or None,
+ user=request.user
+ )
interface = AddInterfaceForm(
request.POST or None,
- infra=request.user.has_perms(('infra',))
+ user=request.user
)
domain = DomainForm(
request.POST or None,
@@ -492,7 +409,7 @@ def new_switch(request):
@login_required
-@permission_required('infra')
+@can_create(Port)
def create_ports(request, switch_id):
""" Création d'une liste de ports pour un switch."""
try:
@@ -500,14 +417,14 @@ def create_ports(request, switch_id):
except Switch.DoesNotExist:
messages.error(request, u"Switch inexistant")
return redirect("/topologie/")
-
+
s_begin = s_end = 0
nb_ports = switch.ports.count()
if nb_ports > 0:
ports = switch.ports.order_by('port').values('port')
s_begin = ports.first().get('port')
s_end = ports.last().get('port')
-
+
port_form = CreatePortsForm(
request.POST or None,
initial={'begin': s_begin, 'end': s_end}
@@ -528,15 +445,11 @@ def create_ports(request, switch_id):
@login_required
-@permission_required('infra')
-def edit_switch(request, switch_id):
+@can_edit(Switch)
+def edit_switch(request, switch, switch_id):
""" Edition d'un switch. Permet de chambre nombre de ports,
place dans le stack, interface et machine associée"""
- try:
- switch = Switch.objects.get(pk=switch_id)
- except Switch.DoesNotExist:
- messages.error(request, u"Switch inexistant")
- return redirect(reverse('topologie:index'))
+
switch_form = EditSwitchForm(request.POST or None, instance=switch)
machine_form = EditMachineForm(
request.POST or None,
@@ -596,7 +509,7 @@ def edit_switch(request, switch_id):
@login_required
-@permission_required('infra')
+@can_create(Room)
def new_room(request):
"""Nouvelle chambre """
room = EditRoomForm(request.POST or None)
@@ -611,14 +524,10 @@ def new_room(request):
@login_required
-@permission_required('infra')
-def edit_room(request, room_id):
+@can_edit(Room)
+def edit_room(request, room, room_id):
""" Edition numero et details de la chambre"""
- try:
- room = Room.objects.get(pk=room_id)
- except Room.DoesNotExist:
- messages.error(request, u"Chambre inexistante")
- return redirect(reverse('topologie:index-room'))
+
room = EditRoomForm(request.POST or None, instance=room)
if room.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -633,14 +542,9 @@ def edit_room(request, room_id):
@login_required
-@permission_required('infra')
-def del_room(request, room_id):
+@can_delete(Room)
+def del_room(request, room, room_id):
""" Suppression d'un chambre"""
- try:
- room = Room.objects.get(pk=room_id)
- except Room.DoesNotExist:
- messages.error(request, u"Chambre inexistante")
- return redirect(reverse('topologie:index-room'))
if request.method == "POST":
try:
with transaction.atomic(), reversion.create_revision():
@@ -651,7 +555,7 @@ def del_room(request, room_id):
except ProtectedError:
messages.error(request, "La chambre %s est affectée à un autre objet,\
impossible de la supprimer (switch ou user)" % room)
- return redirect(reverse('topologie:index-room'))
+ return redirect(reverse('topologie:index-room'))
return form({
'objet': room,
'objet_name': 'Chambre'
@@ -659,7 +563,7 @@ def del_room(request, room_id):
@login_required
-@permission_required('infra')
+@can_create(ModelSwitch)
def new_model_switch(request):
"""Nouveau modèle de switch"""
model_switch = EditModelSwitchForm(request.POST or None)
@@ -674,14 +578,10 @@ def new_model_switch(request):
@login_required
-@permission_required('infra')
-def edit_model_switch(request, model_switch_id):
+@can_edit(ModelSwitch)
+def edit_model_switch(request, model_switch, model_switch_id):
""" Edition d'un modèle de switch"""
- try:
- model_switch = ModelSwitch.objects.get(pk=model_switch_id)
- except ModelSwitch.DoesNotExist:
- messages.error(request, u"Modèle inconnu")
- return redirect("/topologie/index_model_switch/")
+
model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch)
if model_switch.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -696,14 +596,9 @@ def edit_model_switch(request, model_switch_id):
@login_required
-@permission_required('infra')
+@can_delete(ModelSwitch)
def del_model_switch(request, model_switch_id):
""" Suppression d'un modèle de switch"""
- try:
- model_switch = ModelSwitch.objects.get(pk=model_switch_id)
- except ModelSwitch.DoesNotExist:
- messages.error(request, u"Modèle inexistant")
- return redirect("/topologie/index_model_switch/")
if request.method == "POST":
try:
with transaction.atomic(), reversion.create_revision():
@@ -722,7 +617,7 @@ def del_model_switch(request, model_switch_id):
@login_required
-@permission_required('infra')
+@can_create(ConstructorSwitch)
def new_constructor_switch(request):
"""Nouveau constructeur de switch"""
constructor_switch = EditConstructorSwitchForm(request.POST or None)
@@ -737,14 +632,10 @@ def new_constructor_switch(request):
@login_required
-@permission_required('infra')
-def edit_constructor_switch(request, constructor_switch_id):
+@can_edit(ConstructorSwitch)
+def edit_constructor_switch(request, constructor_switch, constructor_switch_id):
""" Edition d'un constructeur de switch"""
- try:
- constructor_switch = ConstructorSwitch.objects.get(pk=constructor_switch_id)
- except ConstructorSwitch.DoesNotExist:
- messages.error(request, u"Constructeur inconnu")
- return redirect("/topologie/index_model_switch/")
+
constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch)
if constructor_switch.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -759,14 +650,9 @@ def edit_constructor_switch(request, constructor_switch_id):
@login_required
-@permission_required('infra')
+@can_delete(ConstructorSwitch)
def del_constructor_switch(request, constructor_switch_id):
""" Suppression d'un constructeur de switch"""
- try:
- constructor_switch = ConstructorSwitch.objects.get(pk=constructor_switch_id)
- except ConstructorSwitch.DoesNotExist:
- messages.error(request, u"Constructeur inexistant")
- return redirect("/topologie/index_model_switch/")
if request.method == "POST":
try:
with transaction.atomic(), reversion.create_revision():
diff --git a/users/__init__.py b/users/__init__.py
index fc1be5d7..df6e4256 100644
--- a/users/__init__.py
+++ b/users/__init__.py
@@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from .acl import *
diff --git a/users/acl.py b/users/acl.py
new file mode 100644
index 00000000..2e82b0f3
--- /dev/null
+++ b/users/acl.py
@@ -0,0 +1,40 @@
+# -*- mode: python; coding: utf-8 -*-
+# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
+# se veut agnostique au réseau considéré, de manière à être installable en
+# quelques clics.
+#
+# Copyright © 2017 Gabriel Détraz
+# Copyright © 2017 Goulven Kermarec
+# Copyright © 2017 Augustin Lemesle
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""users.acl
+
+Here are defined some functions to check acl on the application.
+"""
+
+def can_view(user):
+ """Check if an user can view the application.
+
+ Args:
+ user: The user who wants to view the application.
+
+ Returns:
+ A couple (allowed, msg) where allowed is a boolean which is True if
+ viewing is granted and msg is a message (can be None).
+ """
+ can = user.has_module_perms('users')
+ return can, None if can else "Vous ne pouvez pas voir cette application."
diff --git a/users/admin.py b/users/admin.py
index 488bf7bc..f8acb855 100644
--- a/users/admin.py
+++ b/users/admin.py
@@ -32,7 +32,7 @@ from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from reversion.admin import VersionAdmin
-from .models import User, ServiceUser, School, Right, ListRight, ListShell
+from .models import User, ServiceUser, School, ListRight, ListShell
from .models import Ban, Whitelist, Request, LdapUser, LdapServiceUser
from .models import LdapServiceUserGroup, LdapUserGroup
from .forms import UserChangeForm, UserCreationForm
@@ -86,7 +86,7 @@ class SchoolAdmin(VersionAdmin):
class ListRightAdmin(VersionAdmin):
"""Gestion de la liste des droits existants
Ne permet pas l'edition du gid (primarykey pour ldap)"""
- list_display = ('listright',)
+ list_display = ('unix_name',)
class ListShellAdmin(VersionAdmin):
@@ -94,11 +94,6 @@ class ListShellAdmin(VersionAdmin):
pass
-class RightAdmin(VersionAdmin):
- """Gestion de la liste des droits affectés"""
- pass
-
-
class RequestAdmin(admin.ModelAdmin):
"""Gestion des request objet, ticket pour lien de reinit mot de passe"""
list_display = ('user', 'type', 'created_at', 'expires_at')
@@ -206,7 +201,6 @@ admin.site.register(LdapUserGroup, LdapUserGroupAdmin)
admin.site.register(LdapServiceUser, LdapServiceUserAdmin)
admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin)
admin.site.register(School, SchoolAdmin)
-admin.site.register(Right, RightAdmin)
admin.site.register(ListRight, ListRightAdmin)
admin.site.register(ListShell, ListShellAdmin)
admin.site.register(Ban, BanAdmin)
diff --git a/users/forms.py b/users/forms.py
index a47c1436..ac8e9923 100644
--- a/users/forms.py
+++ b/users/forms.py
@@ -38,12 +38,15 @@ from django.forms import ModelForm, Form
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.validators import MinLengthValidator
from django.utils import timezone
+from django.contrib.auth.models import Group, Permission
from preferences.models import OptionalUser
-from .models import User, ServiceUser, Right, School, ListRight, Whitelist
+from .models import User, ServiceUser, School, ListRight, Whitelist
from .models import Ban, Adherent, Club
from re2o.utils import remove_user_room
+from re2o.field_permissions import FieldPermissionFormMixin
+
NOW = timezone.now()
@@ -253,7 +256,7 @@ class MassArchiveForm(forms.Form):
utilisateurs dont la fin d'accès se situe dans le futur !")
-class AdherentForm(ModelForm):
+class AdherentForm(FieldPermissionFormMixin, ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis"""
@@ -278,6 +281,7 @@ class AdherentForm(ModelForm):
'school',
'comment',
'room',
+ 'shell',
'telephone',
]
@@ -306,7 +310,7 @@ class AdherentForm(ModelForm):
return
-class ClubForm(ModelForm):
+class ClubForm(FieldPermissionFormMixin, ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis"""
@@ -330,6 +334,7 @@ class ClubForm(ModelForm):
'comment',
'room',
'telephone',
+ 'shell',
]
def clean_telephone(self):
@@ -344,41 +349,6 @@ class ClubForm(ModelForm):
return telephone
-class FullAdherentForm(AdherentForm):
- """Edition complète d'un user. Utilisé par admin,
- permet d'editer normalement la chambre, ou le shell
- Herite de la base"""
- class Meta(AdherentForm.Meta):
- fields = [
- 'name',
- 'surname',
- 'pseudo',
- 'email',
- 'school',
- 'comment',
- 'room',
- 'shell',
- 'telephone',
- ]
-
-
-class FullClubForm(ClubForm):
- """Edition complète d'un user. Utilisé par admin,
- permet d'editer normalement la chambre, ou le shell
- Herite de la base"""
- class Meta(ClubForm.Meta):
- fields = [
- 'surname',
- 'pseudo',
- 'email',
- 'school',
- 'comment',
- 'room',
- 'shell',
- 'telephone',
- ]
-
-
class ClubAdminandMembersForm(ModelForm):
"""Permet d'éditer la liste des membres et des administrateurs
d'un club"""
@@ -440,6 +410,23 @@ class StateForm(ModelForm):
super(StateForm, self).__init__(*args, prefix=prefix, **kwargs)
+class GroupForm(ModelForm):
+ """ Gestion des groupes d'un user"""
+ groups = forms.ModelMultipleChoiceField(
+ Group.objects.all(),
+ widget=forms.CheckboxSelectMultiple,
+ required=False
+ )
+
+ class Meta:
+ model = User
+ fields = ['groups']
+
+ def __init__(self, *args, **kwargs):
+ prefix = kwargs.pop('prefix', self.Meta.model.__name__)
+ super(GroupForm, self).__init__(*args, prefix=prefix, **kwargs)
+
+
class SchoolForm(ModelForm):
"""Edition, creation d'un école"""
class Meta:
@@ -455,14 +442,20 @@ class SchoolForm(ModelForm):
class ListRightForm(ModelForm):
"""Edition, d'un groupe , équivalent à un droit
Ne peremet pas d'editer le gid, car il sert de primary key"""
+ permissions = forms.ModelMultipleChoiceField(
+ Permission.objects.all(),
+ widget=forms.CheckboxSelectMultiple,
+ required=False
+ )
+
class Meta:
model = ListRight
- fields = ['listright', 'details']
+ fields = ['name', 'unix_name', 'permissions', 'details']
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ListRightForm, self).__init__(*args, prefix=prefix, **kwargs)
- self.fields['listright'].label = 'Nom du droit/groupe'
+ self.fields['unix_name'].label = 'Nom du droit/groupe'
class NewListRightForm(ListRightForm):
@@ -479,45 +472,35 @@ class NewListRightForm(ListRightForm):
class DelListRightForm(Form):
"""Suppression d'un ou plusieurs groupes"""
listrights = forms.ModelMultipleChoiceField(
- queryset=ListRight.objects.all(),
+ queryset=ListRight.objects.none(),
label="Droits actuels",
widget=forms.CheckboxSelectMultiple
)
+ def __init__(self, *args, **kwargs):
+ instances = kwargs.pop('instances', None)
+ super(DelListRightForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['listrights'].queryset = instances
+ else:
+ self.fields['listrights'].queryset = ListRight.objects.all()
+
class DelSchoolForm(Form):
"""Suppression d'une ou plusieurs écoles"""
schools = forms.ModelMultipleChoiceField(
- queryset=School.objects.all(),
+ queryset=School.objects.none(),
label="Etablissements actuels",
widget=forms.CheckboxSelectMultiple
)
-
-class RightForm(ModelForm):
- """Assignation d'un droit à un user"""
def __init__(self, *args, **kwargs):
- prefix = kwargs.pop('prefix', self.Meta.model.__name__)
- super(RightForm, self).__init__(*args, prefix=prefix, **kwargs)
- self.fields['right'].label = 'Droit'
- self.fields['right'].empty_label = "Choisir un nouveau droit"
-
- class Meta:
- model = Right
- fields = ['right']
-
-
-class DelRightForm(Form):
- """Suppression d'un droit d'un user"""
- rights = forms.ModelMultipleChoiceField(
- queryset=Right.objects.select_related('user'),
- widget=forms.CheckboxSelectMultiple
- )
-
- def __init__(self, right, *args, **kwargs):
- super(DelRightForm, self).__init__(*args, **kwargs)
- self.fields['rights'].queryset = Right.objects.select_related('user')\
- .select_related('right').filter(right=right)
+ instances = kwargs.pop('instances', None)
+ super(DelSchoolForm, self).__init__(*args, **kwargs)
+ if instances:
+ self.fields['schools'].queryset = instances
+ else:
+ self.fields['schools'].queryset = School.objects.all()
class BanForm(ModelForm):
@@ -531,14 +514,6 @@ class BanForm(ModelForm):
model = Ban
exclude = ['user']
- def clean_date_end(self):
- """Verification que date_end est après now"""
- date_end = self.cleaned_data['date_end']
- if date_end < NOW:
- raise forms.ValidationError("Triple buse, la date de fin ne peut\
- pas être avant maintenant... Re2o ne voyage pas dans le temps")
- return date_end
-
class WhitelistForm(ModelForm):
"""Creation, edition d'un objet whitelist"""
@@ -550,11 +525,3 @@ class WhitelistForm(ModelForm):
class Meta:
model = Whitelist
exclude = ['user']
-
- def clean_date_end(self):
- """Verification que la date_end est posterieur à now"""
- date_end = self.cleaned_data['date_end']
- if date_end < NOW:
- raise forms.ValidationError("Triple buse, la date de fin ne peut pas\
- être avant maintenant... Re2o ne voyage pas dans le temps")
- return date_end
diff --git a/users/management/commands/email.py b/users/management/commands/email.py
new file mode 100644
index 00000000..a7518b9f
--- /dev/null
+++ b/users/management/commands/email.py
@@ -0,0 +1,32 @@
+from django.core.management.base import BaseCommand, CommandError
+
+from datetime import datetime, timedelta
+from pytz
+
+from users.models import User
+
+UTC = pytz.timezone('UTC')
+
+class Command(BaseCommand):
+ commands = ['email_remainder',]
+ args = '[command]'
+ help = 'Send email remainders'
+
+ def handle(self, *args, **options):
+ '''
+ Sends an email before the end of a user's subscription
+ '''
+ users = User.objects.filter(state="STATE_ACTIVE")
+
+ for user in users:
+ remaining = user.end_adhesion() - datetime.today(tz=UTC)
+ if (timedelta(weeks=4) - remaining).days == 1:
+ 4_weeks_reminder()
+ elif (timedelta(weeks=1) - remaining).days == 1:
+ week_reminder()
+ elif remaining.days == 1:
+ last_day_reminder()
+
+def month_reminder():
+ pass
+
diff --git a/users/migrations/0061_auto_20171230_2033.py b/users/migrations/0061_auto_20171230_2033.py
new file mode 100644
index 00000000..f2751fe2
--- /dev/null
+++ b/users/migrations/0061_auto_20171230_2033.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-30 19:33
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0008_alter_user_username_max_length'),
+ ('users', '0060_auto_20171120_0317'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='user',
+ name='groups',
+ field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
+ ),
+ migrations.AddField(
+ model_name='user',
+ name='is_superuser',
+ field=models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status'),
+ ),
+ migrations.AddField(
+ model_name='user',
+ name='user_permissions',
+ field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
+ ),
+ ]
diff --git a/users/migrations/0062_auto_20171231_0056.py b/users/migrations/0062_auto_20171231_0056.py
new file mode 100644
index 00000000..2eaedb50
--- /dev/null
+++ b/users/migrations/0062_auto_20171231_0056.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-30 23:56
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0008_alter_user_username_max_length'),
+ ('users', '0061_auto_20171230_2033'),
+ ]
+
+ def create_groups(apps, schema_editor):
+ group = apps.get_model("auth", "Group")
+ listrights = apps.get_model("users", "ListRight")
+ db_alias = schema_editor.connection.alias
+ for gr in listrights.objects.using(db_alias).all():
+ grp = group()
+ grp.name=gr.unix_name
+ grp.save()
+ gr.group_ptr=grp
+ gr.save()
+
+ def delete_groups(apps, schema_editor):
+ group = apps.get_model("auth", "Group")
+ db_alias = schema_editor.connection.alias
+ group.objects.using(db_alias).all().delete()
+
+ operations = [
+ migrations.RenameField(
+ model_name='listright',
+ old_name='listright',
+ new_name='unix_name',
+ ),
+ migrations.AddField(
+ model_name='listright',
+ name='group_ptr',
+ field=models.OneToOneField(blank=True, null=True, auto_created=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='auth.Group'),
+ preserve_default=False,
+ ),
+ migrations.RunPython(create_groups, delete_groups),
+ ]
diff --git a/users/migrations/0063_auto_20171231_0140.py b/users/migrations/0063_auto_20171231_0140.py
new file mode 100644
index 00000000..56762014
--- /dev/null
+++ b/users/migrations/0063_auto_20171231_0140.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 00:40
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0062_auto_20171231_0056'),
+ ]
+
+ def transfer_right(apps, schema_editor):
+ rights = apps.get_model("users", "Right")
+ db_alias = schema_editor.connection.alias
+ for rg in rights.objects.using(db_alias).all():
+ group = rg.right
+ u=rg.user
+ u.groups.add(group.group_ptr)
+ u.save()
+
+ def untransfer_right(apps, schema_editor):
+ return
+
+ operations = [
+ migrations.RunPython(transfer_right, untransfer_right),
+ ]
diff --git a/users/migrations/0064_auto_20171231_0150.py b/users/migrations/0064_auto_20171231_0150.py
new file mode 100644
index 00000000..a08561eb
--- /dev/null
+++ b/users/migrations/0064_auto_20171231_0150.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 00:50
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0063_auto_20171231_0140'),
+ ]
+
+ operations = [
+ migrations.AlterUniqueTogether(
+ name='right',
+ unique_together=set([]),
+ ),
+ migrations.RemoveField(
+ model_name='right',
+ name='right',
+ ),
+ migrations.RemoveField(
+ model_name='right',
+ name='user',
+ ),
+ migrations.DeleteModel(
+ name='Right',
+ ),
+ migrations.RemoveField(
+ model_name='listright',
+ name='id',
+ ),
+ migrations.AlterField(
+ model_name='listright',
+ name='group_ptr',
+ field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.Group'),
+ ),
+
+ ]
diff --git a/users/migrations/0065_auto_20171231_2053.py b/users/migrations/0065_auto_20171231_2053.py
new file mode 100644
index 00000000..7f2d135b
--- /dev/null
+++ b/users/migrations/0065_auto_20171231_2053.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 19:53
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0064_auto_20171231_0150'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='ban',
+ options={'permissions': (('view_ban', "Peut voir un objet ban quelqu'il soit"),)},
+ ),
+ migrations.AlterModelOptions(
+ name='listright',
+ options={'permissions': (('view_listright', 'Peut voir un objet Group/ListRight'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='school',
+ options={'permissions': (('view_school', 'Peut voir un objet school'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='serviceuser',
+ options={'permissions': (('view_serviceuser', 'Peut voir un objet serviceuser'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='user',
+ options={'permissions': (('change_user_password', "Peut changer le mot de passe d'un user"), ('change_user_state', "Peut éditer l'etat d'un user"), ('change_user_force', 'Peut forcer un déménagement'), ('change_user_shell', "Peut éditer le shell d'un user"), ('change_user_groups', "Peut éditer les groupes d'un user ! Permission critique"), ('view_user', 'Peut voir un objet user quelquonque'))},
+ ),
+ migrations.AlterModelOptions(
+ name='whitelist',
+ options={'permissions': (('view_whitelist', 'Peut voir un objet whitelist'),)},
+ ),
+ ]
diff --git a/users/migrations/0066_grouppermissions.py b/users/migrations/0066_grouppermissions.py
new file mode 100644
index 00000000..d3253e64
--- /dev/null
+++ b/users/migrations/0066_grouppermissions.py
@@ -0,0 +1,254 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 19:53
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0065_auto_20171231_2053'),
+ ('cotisations', '0028_auto_20171231_0007'),
+ ('machines', '0071_auto_20171231_2100'),
+ ('preferences', '0025_auto_20171231_2142'),
+ ('topologie', '0033_auto_20171231_1743'),
+ ]
+ def transfer_permissions(apps, schema_editor):
+ permission_groups = {'bofh': ['add_ban',
+ 'change_ban',
+ 'delete_ban',
+ 'view_ban',
+ 'add_club',
+ 'change_club',
+ 'delete_club',
+ 'add_user',
+ 'change_user',
+ 'change_user_force',
+ 'change_user_password',
+ 'change_user_shell',
+ 'view_user',
+ 'add_whitelist',
+ 'change_whitelist',
+ 'delete_whitelist',
+ 'view_whitelist'],
+ 'bureau': ['add_logentry',
+ 'change_logentry',
+ 'delete_logentry',
+ 'add_group',
+ 'change_group',
+ 'delete_group',
+ 'add_permission',
+ 'change_permission',
+ 'delete_permission',
+ 'add_adherent',
+ 'change_adherent',
+ 'delete_adherent',
+ 'add_ban',
+ 'change_ban',
+ 'delete_ban',
+ 'view_ban',
+ 'add_club',
+ 'change_club',
+ 'delete_club',
+ 'add_listright',
+ 'change_listright',
+ 'delete_listright',
+ 'view_listright',
+ 'add_school',
+ 'change_school',
+ 'delete_school',
+ 'view_school',
+ 'add_user',
+ 'change_user',
+ 'change_user_force',
+ 'change_user_groups',
+ 'change_user_password',
+ 'change_user_shell',
+ 'change_user_state',
+ 'delete_user',
+ 'view_user',
+ 'add_whitelist',
+ 'change_whitelist',
+ 'delete_whitelist',
+ 'view_whitelist'],
+ 'cableur': ['add_logentry',
+ 'view_article',
+ 'add_banque',
+ 'change_banque',
+ 'delete_banque',
+ 'view_banque',
+ 'add_cotisation',
+ 'change_cotisation',
+ 'delete_cotisation',
+ 'view_cotisation',
+ 'add_facture',
+ 'can_create',
+ 'can_delete',
+ 'can_edit',
+ 'can_view',
+ 'can_view_all',
+ 'change_facture',
+ 'delete_facture',
+ 'view_facture',
+ 'view_paiement',
+ 'add_vente',
+ 'change_vente',
+ 'delete_vente',
+ 'view_vente',
+ 'add_domain',
+ 'change_domain',
+ 'delete_domain',
+ 'view_domain',
+ 'use_all_extension',
+ 'view_extension',
+ 'add_interface',
+ 'change_interface',
+ 'delete_interface',
+ 'view_interface',
+ 'view_iplist',
+ 'view_iptype',
+ 'add_machine',
+ 'change_machine',
+ 'view_machine',
+ 'view_machinetype',
+ 'view_mx',
+ 'view_nas',
+ 'view_ns',
+ 'view_ouvertureportlist',
+ 'view_service',
+ 'view_soa',
+ 'view_soa',
+ 'view_txt',
+ 'view_vlan',
+ 'view_assooption',
+ 'view_generaloption',
+ 'view_mailmessageoption',
+ 'view_optionalmachine',
+ 'view_optionaltopologie',
+ 'view_optionaluser',
+ 'view_service',
+ 'view_constructorswitch',
+ 'view_modelswitch',
+ 'view_port',
+ 'view_room',
+ 'view_stack',
+ 'view_switch',
+ 'add_adherent',
+ 'change_adherent',
+ 'view_ban',
+ 'add_club',
+ 'change_club',
+ 'view_listright',
+ 'add_school',
+ 'change_school',
+ 'delete_school',
+ 'view_school',
+ 'view_serviceuser',
+ 'add_user',
+ 'change_user',
+ 'change_user_force',
+ 'change_user_password',
+ 'view_user',
+ 'add_whitelist',
+ 'change_whitelist',
+ 'delete_whitelist',
+ 'view_whitelist'],
+ 'tresorier': ['add_article',
+ 'change_article',
+ 'delete_article',
+ 'view_article',
+ 'add_banque',
+ 'change_banque',
+ 'delete_banque',
+ 'view_banque',
+ 'add_cotisation',
+ 'change_all_cotisation',
+ 'change_cotisation',
+ 'delete_cotisation',
+ 'view_cotisation',
+ 'add_facture',
+ 'can_change_control',
+ 'can_change_pdf',
+ 'can_create',
+ 'can_delete',
+ 'can_edit',
+ 'can_view',
+ 'can_view_all',
+ 'change_all_facture',
+ 'change_facture',
+ 'change_facture_control',
+ 'change_facture_pdf',
+ 'delete_facture',
+ 'view_facture',
+ 'add_paiement',
+ 'change_paiement',
+ 'delete_paiement',
+ 'view_paiement',
+ 'add_vente',
+ 'change_all_vente',
+ 'change_vente',
+ 'delete_vente',
+ 'view_vente'],
+ 'admin': ['add_logentry',
+ 'change_logentry',
+ 'delete_logentry',
+ 'add_assooption',
+ 'change_assooption',
+ 'delete_assooption',
+ 'view_assooption',
+ 'add_generaloption',
+ 'change_generaloption',
+ 'delete_generaloption',
+ 'view_generaloption',
+ 'add_mailmessageoption',
+ 'change_mailmessageoption',
+ 'delete_mailmessageoption',
+ 'view_mailmessageoption',
+ 'add_optionalmachine',
+ 'change_optionalmachine',
+ 'delete_optionalmachine',
+ 'view_optionalmachine',
+ 'add_optionaltopologie',
+ 'change_optionaltopologie',
+ 'delete_optionaltopologie',
+ 'view_optionaltopologie',
+ 'add_optionaluser',
+ 'change_optionaluser',
+ 'delete_optionaluser',
+ 'view_optionaluser',
+ 'add_service',
+ 'add_services',
+ 'change_service',
+ 'change_services',
+ 'delete_service',
+ 'delete_services',
+ 'view_service']}
+
+ rights = apps.get_model("users", "ListRight")
+ permissions = apps.get_model("auth", "Permission")
+ groups = apps.get_model("auth", "Group")
+ db_alias = schema_editor.connection.alias
+ for group in permission_groups:
+ lr_object = rights.objects.using(db_alias).filter(unix_name=group).first()
+ if not lr_object:
+ last = rights.objects.using(db_alias).all().order_by('gid').last()
+ if last:
+ gid = last.gid + 1
+ else:
+ gid = 501
+ group_object = groups.objects.using(db_alias).create(name=group)
+ lr_object = rights.objects.using(db_alias).create(unix_name=group, gid=gid, group_ptr=group_object)
+ lr_object = lr_object.group_ptr
+ for permission in permission_groups[group]:
+ perm = permissions.objects.using(db_alias).filter(codename=permission).first()
+ if perm:
+ lr_object.permissions.add(perm)
+ lr_object.save()
+
+ def untransfer_permissions(apps, schema_editor):
+ return
+
+ operations = [
+ migrations.RunPython(transfer_permissions, untransfer_permissions),
+ ]
diff --git a/users/migrations/0067_serveurpermission.py b/users/migrations/0067_serveurpermission.py
new file mode 100644
index 00000000..e5e3380c
--- /dev/null
+++ b/users/migrations/0067_serveurpermission.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-31 19:53
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0066_grouppermissions'),
+ ]
+
+ def transfer_permissions(apps, schema_editor):
+ db_alias = schema_editor.connection.alias
+ contenttype = apps.get_model("contenttypes", "ContentType")
+ rights = apps.get_model("users", "ListRight")
+ permissions = apps.get_model("auth", "Permission")
+ groups = apps.get_model("auth", "Group")
+ machine = apps.get_model("machines", "Machine")
+ perm = permissions.objects.using(db_alias).filter(codename='serveur').first()
+ if not perm:
+ perm = permissions.objects.using(db_alias).create(
+ codename='serveur',
+ name='Serveur',
+ content_type=contenttype.objects.using(db_alias).get_for_model(machine)
+ )
+ group_object = rights.objects.using(db_alias).filter(unix_name='serveur').first()
+ if not group_object:
+ last_gid = rights.objects.using(db_alias).all().order_by('gid').last().gid
+ gid = last_gid + 1
+ abstract_group = groups.objects.using(db_alias).create(name='serveur')
+ group_object = rights.objects.using(db_alias).create(group_ptr=abstract_group, unix_name='serveur', gid=gid)
+ group_object = group_object.group_ptr
+ group_object.permissions.add(perm)
+ group_object.save()
+
+ def untransfer_permissions(apps, schema_editor):
+ return
+
+ operations = [
+ migrations.RunPython(transfer_permissions, untransfer_permissions),
+ ]
diff --git a/users/models.py b/users/models.py
index 614f15fd..9761cc20 100644
--- a/users/models.py
+++ b/users/models.py
@@ -62,7 +62,9 @@ from django.db import transaction
from django.utils import timezone
from django.contrib.auth.models import (
AbstractBaseUser,
- BaseUserManager
+ BaseUserManager,
+ PermissionsMixin,
+ Group
)
from django.core.validators import RegexValidator
@@ -73,6 +75,7 @@ import ldapdb.models.fields
from re2o.settings import RIGHTS_LINK, LDAP, GID_RANGES, UID_RANGES
from re2o.login import hashNT
+from re2o.field_permissions import FieldPermissionModelMixin
from cotisations.models import Cotisation, Facture, Paiement, Vente
from machines.models import Domain, Interface, Machine, regen
@@ -126,18 +129,6 @@ def get_fresh_gid():
return min(free_gids)
-def get_admin_right():
- """ Renvoie l'instance droit admin. La crée si elle n'existe pas
- Lui attribue un gid libre"""
- try:
- admin_right = ListRight.objects.get(listright="admin")
- except ListRight.DoesNotExist:
- admin_right = ListRight(listright="admin")
- admin_right.gid = get_fresh_gid()
- admin_right.save()
- return admin_right
-
-
class UserManager(BaseUserManager):
"""User manager basique de django"""
def _create_user(
@@ -161,9 +152,9 @@ class UserManager(BaseUserManager):
)
user.set_password(password)
- user.save(using=self._db)
if su:
- user.make_admin()
+ user.is_superuser=True
+ user.save(using=self._db)
return user
def create_user(self, pseudo, surname, email, password=None):
@@ -180,8 +171,7 @@ class UserManager(BaseUserManager):
"""
return self._create_user(pseudo, surname, email, password, True)
-
-class User(AbstractBaseUser):
+class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin):
""" Definition de l'utilisateur de base.
Champs principaux : name, surnname, pseudo, email, room, password
Herite du django BaseUser et du système d'auth django"""
@@ -236,6 +226,16 @@ class User(AbstractBaseUser):
objects = UserManager()
+ class Meta:
+ permissions = (
+ ("change_user_password", "Peut changer le mot de passe d'un user"),
+ ("change_user_state", "Peut éditer l'etat d'un user"),
+ ("change_user_force", "Peut forcer un déménagement"),
+ ("change_user_shell", "Peut éditer le shell d'un user"),
+ ("change_user_groups", "Peut éditer les groupes d'un user ! Permission critique"),
+ ("view_user", "Peut voir un objet user quelquonque"),
+ )
+
@cached_property
def name(self):
"""Si il s'agit d'un adhérent, on renvoie le prénom"""
@@ -285,20 +285,7 @@ class User(AbstractBaseUser):
@property
def is_admin(self):
""" Renvoie si l'user est admin"""
- try:
- Right.objects.get(user=self, right__listright='admin')
- except Right.DoesNotExist:
- return False
- return True
-
- @is_admin.setter
- def is_admin(self, value):
- """ Change la valeur de admin à true ou false suivant la valeur de
- value"""
- if value and not self.is_admin:
- self.make_admin()
- elif not value and self.is_admin:
- self.un_admin()
+ return self.is_superuser
def get_full_name(self):
""" Renvoie le nom complet de l'user formaté nom/prénom"""
@@ -312,66 +299,6 @@ class User(AbstractBaseUser):
""" Renvoie seulement le nom"""
return self.surname
- def has_perms(self, perms, obj=None):
- """ Renvoie true si l'user dispose de la permission.
- Prend en argument une liste de permissions.
- TODO : Arranger cette fonction"""
- for perm in perms:
- if perm in RIGHTS_LINK:
- query = Q()
- for right in RIGHTS_LINK[perm]:
- query = query | Q(right__listright=right)
- if Right.objects.filter(Q(user=self) & query):
- return True
- try:
- Right.objects.get(user=self, right__listright=perm)
- except Right.DoesNotExist:
- return False
- return True
-
- def has_perm(self, perm, obj=None):
- """Ne sert à rien"""
- return True
-
- def has_right(self, right):
- """ Renvoie si un user a un right donné. Crée le right si il n'existe
- pas"""
- try:
- list_right = ListRight.objects.get(listright=right)
- except:
- list_right = ListRight(listright=right, gid=get_fresh_gid())
- list_right.save()
- return Right.objects.filter(user=self).filter(
- right=list_right
- ).exists()
-
- @cached_property
- def is_bureau(self):
- """ True si user a les droits bureau """
- return self.has_right('bureau')
-
- @cached_property
- def is_bofh(self):
- """ True si l'user a les droits bofh"""
- return self.has_right('bofh')
-
- @cached_property
- def is_cableur(self):
- """ True si l'user a les droits cableur
- (également true si bureau, infra ou bofh)"""
- return self.has_right('cableur') or self.has_right('bureau') or\
- self.has_right('infra') or self.has_right('bofh')
-
- @cached_property
- def is_trez(self):
- """ Renvoie true si droits trésorier pour l'user"""
- return self.has_right('tresorier')
-
- @cached_property
- def is_infra(self):
- """ True si a les droits infra"""
- return self.has_right('infra')
-
def end_adhesion(self):
""" Renvoie la date de fin d'adhésion d'un user. Examine les objets
cotisation"""
@@ -551,23 +478,6 @@ class User(AbstractBaseUser):
self.assign_ips()
self.state = User.STATE_ACTIVE
- def has_module_perms(self, app_label):
- """True, a toutes les permissions de module"""
- return True
-
- def make_admin(self):
- """ Make User admin """
- user_admin_right = Right(user=self, right=get_admin_right())
- user_admin_right.save()
-
- def un_admin(self):
- """Supprime les droits admin d'un user"""
- try:
- user_right = Right.objects.get(user=self, right=get_admin_right())
- except Right.DoesNotExist:
- return
- user_right.delete()
-
def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False):
""" Synchronisation du ldap. Synchronise dans le ldap les attributs de
self
@@ -610,8 +520,9 @@ class User(AbstractBaseUser):
machine__user=self
).values_list('mac_address', flat=True).distinct()]
if group_refresh:
- for right in Right.objects.filter(user=self):
- right.right.ldap_sync()
+ for group in self.groups.all():
+ if hasattr(group, 'listright'):
+ group.listright.ldap_sync()
user_ldap.save()
def ldap_del(self):
@@ -762,27 +673,133 @@ class User(AbstractBaseUser):
num += 1
return composed_pseudo(num)
- def can_create(user):
+ def get_instance(userid, *args, **kwargs):
+ """Get the User instance with userid.
+
+ :param userid: The id
+ :return: The user
+ """
+ return User.objects.get(pk=userid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create an user object.
+
+ :param user_request: The user who wants to create a user object.
+ :return: a message and a boolean which is True if the user can create
+ an user or if the `options.all_can_create` is set.
+ """
options, _created = OptionalUser.objects.get_or_create()
if options.all_can_create:
- return True
+ return True, None
else:
- return user.has_perms(('cableur',))
+ return user_request.has_perm('users.add_user'), u"Vous n'avez pas le\
+ droit de créer un utilisateur"
- def can_edit(self, user):
- if self.is_class_club and user.is_class_adherent:
- return self == user or user.has_perms(('cableur',)) or\
- user.adherent in self.club.administrators.all()
- else:
- return self == user or user.has_perms(('cableur',))
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit an user object.
- def can_view(self, user):
- if self.is_class_club and user.is_class_adherent:
- return self == user or user.has_perms(('cableur',)) or\
- user.adherent in self.club.administrators.all() or\
- user.adherent in self.club.members.all()
+ :param self: The user which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if self is a club and
+ user_request one of its member, or if user_request is self, or if
+ user_request has the 'cableur' right.
+ """
+ if self.is_class_club and user_request.is_class_adherent:
+ if self == user_request or \
+ user_request.has_perm('users.change_user') or \
+ user_request.adherent in self.club.administrators.all():
+ return True, None
+ else:
+ return False, u"Vous n'avez pas le droit d'éditer ce club"
else:
- return self == user or user.has_perms(('cableur',))
+ if self == user_request or user_request.has_perm('users.change_user'):
+ return True, None
+ else:
+ return False, u"Vous ne pouvez éditer un autre utilisateur que vous même"
+
+ def can_change_password(self, user_request, *args, **kwargs):
+ if self.is_class_club and user_request.is_class_adherent:
+ if self == user_request or \
+ user_request.has_perm('users.change_user_password') or \
+ user_request.adherent in self.club.administrators.all():
+ return True, None
+ else:
+ return False, u"Vous n'avez pas le droit d'éditer ce club"
+ else:
+ if self == user_request or \
+ user_request.has_perm('users.change_user_groups'):
+ # Peut éditer les groupes d'un user, c'est un privilège élevé, True
+ return True, None
+ elif user_request.has_perm('users.change_user') and not self.groups.all():
+ return True, None
+ else:
+ return False, u"Vous ne pouvez éditer un autre utilisateur que vous même"
+
+ @staticmethod
+ def can_change_state(user_request, *args, **kwargs):
+ return user_request.has_perm('users.change_user_state'), "Droit requis pour changer l'état"
+
+ @staticmethod
+ def can_change_shell(user_request, *args, **kwargs):
+ return user_request.has_perm('users.change_user_shell'), "Droit requis pour changer le shell"
+
+ @staticmethod
+ def can_change_force(user_request, *args, **kwargs):
+ return user_request.has_perm('users.change_user_force'), "Droit requis pour forcer le déménagement"
+
+ @staticmethod
+ def can_change_groups(user_request, *args, **kwargs):
+ return user_request.has_perm('users.change_user_groups'), "Droit requis pour éditer les groupes de l'user"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete an user object.
+
+ :param self: The user who is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if user_request has the right 'bureau', and a message.
+ """
+ if user_request.has_perm('users.delete_user'):
+ return True, None
+ else:
+ return False, u"Vous ne pouvez pas supprimer cet utilisateur."
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every user objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ if user_request.has_perm('users.view_user'):
+ return True, None
+ else:
+ return False, u"Vous n'avez pas accès à la liste des utilisateurs."
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view an user object.
+
+ :param self: The targeted user.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ if self.is_class_club and user_request.is_class_adherent:
+ if self == user_request or \
+ user_request.has_perm('users.view_user') or \
+ user_request.adherent in self.club.administrators.all() or \
+ user_request.adherent in self.club.members.all():
+ return True, None
+ else:
+ return False, u"Vous n'avez pas le droit de voir ce club"
+ else:
+ if self == user_request or user_request.has_perm('users.view_user'):
+ return True, None
+ else:
+ return False, u"Vous ne pouvez voir un autre utilisateur que vous même"
+
+ field_permissions = {
+ 'shell' : can_change_shell,
+ 'force' : can_change_force,
+ }
def __str__(self):
return self.pseudo
@@ -797,8 +814,14 @@ class Adherent(User):
blank=True,
null=True
)
- pass
+ def get_instance(adherentid, *args, **kwargs):
+ """Try to find an instance of `Adherent` with the given id.
+
+ :param adherentid: The id of the adherent we are looking for.
+ :return: An adherent.
+ """
+ return Adherent.objects.get(pk=adherentid)
class Club(User):
@@ -820,7 +843,26 @@ class Club(User):
related_name='club_members'
)
- pass
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every user objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ if user_request.has_perm('users.view_user'):
+ return True, None
+ if user_request.is_class_adherent:
+ if user_request.adherent.club_administrator.all() or user_request.adherent.club_members.all():
+ return True, None
+ return False, u"Vous n'avez pas accès à la liste des utilisateurs."
+
+ def get_instance(clubid, *args, **kwargs):
+ """Try to find an instance of `Club` with the given id.
+
+ :param clubid: The id of the adherent we are looking for.
+ :return: A club.
+ """
+ return Club.objects.get(pk=clubid)
@receiver(post_save, sender=Adherent)
@@ -846,7 +888,6 @@ def user_post_delete(sender, **kwargs):
user.ldap_del()
regen('mailing')
-
class ServiceUser(AbstractBaseUser):
""" Classe des users daemons, règle leurs accès au ldap"""
readonly = 'readonly'
@@ -878,6 +919,11 @@ class ServiceUser(AbstractBaseUser):
USERNAME_FIELD = 'pseudo'
objects = UserManager()
+ class Meta:
+ permissions = (
+ ("view_serviceuser", "Peut voir un objet serviceuser"),
+ )
+
def ldap_sync(self):
""" Synchronisation du ServiceUser dans sa version ldap"""
try:
@@ -909,10 +955,64 @@ class ServiceUser(AbstractBaseUser):
)]).values_list('dn', flat=True))
group.save()
+ def get_instance(userid, *args, **kwargs):
+ return ServiceUser.objects.get(pk=userid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a ServiceUser object.
+
+ :param user_request: The user who wants to create a user object.
+ :return: a message and a boolean which is True if the user can create
+ or if the `options.all_can_create` is set.
+ """
+ return user_request.has_perm('users.add_serviceuser'), (
+ u"Vous n'avez pas le droit de créer un service user"
+ )
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a ServiceUser object.
+
+ :param self: The ServiceUser which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('users.change_serviceuser'), (
+ u"Vous n'avez pas le droit d'éditer les services users"
+ )
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a ServiceUser object.
+
+ :param self: The ServiceUser who is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if user_request has the right 'infra', and a message.
+ """
+ return user_request.has_perm('users.delete_serviceuser'), u"Vous n'avez pas le droit de\
+ supprimer un service user"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every ServiceUser objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('users.view_serviceuser'), u"Vous n'avez pas le droit de\
+ voir un service user"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a ServiceUser object.
+
+ :param self: The targeted ServiceUser.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('users.view_serviceuser'), u"Vous n'avez pas le droit de\
+ voir un service user"
+
def __str__(self):
return self.pseudo
-
@receiver(post_save, sender=ServiceUser)
def service_user_post_save(sender, **kwargs):
""" Synchronise un service user ldap après modification django"""
@@ -927,47 +1027,74 @@ def service_user_post_delete(sender, **kwargs):
service_user.ldap_del()
-class Right(models.Model):
- """ Couple droit/user. Peut-être aurait-on mieux fait ici d'utiliser un
- manytomany
- Ceci dit le résultat aurait été le même avec une table intermediaire"""
- PRETTY_NAME = "Droits affectés à des users"
-
- user = models.ForeignKey('User', on_delete=models.PROTECT)
- right = models.ForeignKey('ListRight', on_delete=models.PROTECT)
-
- class Meta:
- unique_together = ("user", "right")
-
- def __str__(self):
- return str(self.user)
-
-
-@receiver(post_save, sender=Right)
-def right_post_save(sender, **kwargs):
- """ Synchronise les users ldap groups avec les groupes de droits"""
- right = kwargs['instance'].right
- right.ldap_sync()
-
-
-@receiver(post_delete, sender=Right)
-def right_post_delete(sender, **kwargs):
- """ Supprime l'user du groupe"""
- right = kwargs['instance'].right
- right.ldap_sync()
-
-
class School(models.Model):
""" Etablissement d'enseignement"""
- PRETTY_NAME = "Etablissements enregistrés"
+ PRETTY_NAME = "Établissements enregistrés"
name = models.CharField(max_length=255)
+ class Meta:
+ permissions = (
+ ("view_school", "Peut voir un objet school"),
+ )
+
+ def get_instance(schoolid, *args, **kwargs):
+ return School.objects.get(pk=schoolid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a School object.
+
+ :param user_request: The user who wants to create a user object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('users.add_school'), u"Vous n'avez pas le\
+ droit de créer des écoles"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a School object.
+
+ :param self: The School which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('users.change_school'), u"Vous n'avez pas le\
+ droit d'éditer des écoles"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a School object.
+
+ :param self: The School which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('users.delete_school'), u"Vous n'avez pas le\
+ droit de supprimer des écoles"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every School objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('users.view_school'), u"Vous n'avez pas le\
+ droit de voir les écoles"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a School object.
+
+ :param self: The targeted School.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('users.view_school'), u"Vous n'avez pas le\
+ droit de voir les écoles"
+
def __str__(self):
return self.name
-class ListRight(models.Model):
+class ListRight(Group):
""" Ensemble des droits existants. Chaque droit crée un groupe
ldap synchronisé, avec gid.
Permet de gérer facilement les accès serveurs et autres
@@ -975,7 +1102,7 @@ class ListRight(models.Model):
il n'est plus modifiable après creation"""
PRETTY_NAME = "Liste des droits existants"
- listright = models.CharField(
+ unix_name = models.CharField(
max_length=255,
unique=True,
validators=[RegexValidator(
@@ -991,8 +1118,65 @@ class ListRight(models.Model):
blank=True
)
+ class Meta:
+ permissions = (
+ ("view_listright", "Peut voir un objet Group/ListRight"),
+ )
+
+ def get_instance(listrightid, *args, **kwargs):
+ return ListRight.objects.get(pk=listrightid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a ListRight object.
+
+ :param user_request: The user who wants to create a ListRight object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('users.add_listright'), u"Vous n'avez pas le droit\
+ de créer des groupes de droits"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a ListRight object.
+
+ :param self: The object which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('users.change_listright'), u"Vous n'avez pas le droit\
+ d'éditer des groupes de droits"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a ListRight object.
+
+ :param self: The object which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('users.delete_listright'), u"Vous n'avez pas le droit\
+ de supprimer des groupes de droits"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every ListRight objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('users.view_listright'), u"Vous n'avez pas le droit\
+ de voir les groupes de droits"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a ListRight object.
+
+ :param self: The targeted object.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ return user_request.has_perm('users.view_listright'), u"Vous n'avez pas le droit\
+ de voir les groupes de droits"
+
def __str__(self):
- return self.listright
+ return self.name
def ldap_sync(self):
"""Sychronise les groups ldap avec le model listright coté django"""
@@ -1001,8 +1185,8 @@ class ListRight(models.Model):
except LdapUserGroup.DoesNotExist:
group_ldap = LdapUserGroup(gid=self.gid)
group_ldap.name = self.listright
- group_ldap.members = [right.user.pseudo for right
- in Right.objects.filter(right=self)]
+ group_ldap.members = [user.pseudo for user
+ in self.user_set.all()]
group_ldap.save()
def ldap_del(self):
@@ -1059,6 +1243,11 @@ class Ban(models.Model):
date_end = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
state = models.IntegerField(choices=STATES, default=STATE_HARD)
+ class Meta:
+ permissions = (
+ ("view_ban", "Peut voir un objet ban quelqu'il soit"),
+ )
+
def notif_ban(self):
""" Prend en argument un objet ban, envoie un mail de notification """
general_options, _created = GeneralOption.objects.get_or_create()
@@ -1083,6 +1272,62 @@ class Ban(models.Model):
"""Ce ban est-il actif?"""
return self.date_end > DT_NOW
+ def get_instance(banid, *args, **kwargs):
+ return Ban.objects.get(pk=banid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a Ban object.
+
+ :param user_request: The user who wants to create a Ban object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('users.add_ban'), u"Vous n'avez pas le droit de\
+ créer des bannissements"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a Ban object.
+
+ :param self: The object which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('users.change_ban'), u"Vous n'avez pas le droit\
+ d'éditer des bannissements"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a Ban object.
+
+ :param self: The object which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('users.delete_ban'), u"Vous n'avez pas le droit\
+ de supprimer des bannissements"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every Ban objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('users.view_ban'), u"Vous n'avez pas le droit\
+ de voir tous les bannissements"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a Ban object.
+
+ :param self: The targeted object.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ if not user_request.has_perm('users.view_ban') and\
+ self.user != user_request:
+ return False, u"Vous n'avez pas le droit de voir les bannissements\
+ autre que les vôtres"
+ else:
+ return True, None
+
def __str__(self):
return str(self.user) + ' ' + str(self.raison)
@@ -1125,9 +1370,70 @@ class Whitelist(models.Model):
date_start = models.DateTimeField(auto_now_add=True)
date_end = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
+ class Meta:
+ permissions = (
+ ("view_whitelist", "Peut voir un objet whitelist"),
+ )
+
def is_active(self):
return self.date_end > DT_NOW
+ def get_instance(whitelistid, *args, **kwargs):
+ return Whitelist.objects.get(pk=whitelistid)
+
+ def can_create(user_request, *args, **kwargs):
+ """Check if an user can create a Whitelist object.
+
+ :param user_request: The user who wants to create a Whitelist object.
+ :return: a message and a boolean which is True if the user can create.
+ """
+ return user_request.has_perm('users.add_whitelist'), u"Vous n'avez pas le\
+ droit de créer des accès gracieux"
+
+ def can_edit(self, user_request, *args, **kwargs):
+ """Check if an user can edit a Whitelist object.
+
+ :param self: The object which is to be edited.
+ :param user_request: The user who requests to edit self.
+ :return: a message and a boolean which is True if edition is granted.
+ """
+ return user_request.has_perm('users.change_whitelist'), u"Vous n'avez pas le\
+ droit d'éditer des accès gracieux"
+
+ def can_delete(self, user_request, *args, **kwargs):
+ """Check if an user can delete a Whitelist object.
+
+ :param self: The object which is to be deleted.
+ :param user_request: The user who requests deletion.
+ :return: True if deletion is granted, and a message.
+ """
+ return user_request.has_perm('users.delete_whitelist'), u"Vous n'avez pas le\
+ droit de supprimer des accès gracieux"
+
+ def can_view_all(user_request, *args, **kwargs):
+ """Check if an user can access to the list of every Whitelist objects
+
+ :param user_request: The user who wants to view the list.
+ :return: True if the user can view the list and an explanation message.
+ """
+ return user_request.has_perm('users.view_whitelist'), u"Vous n'avez pas le\
+ droit de voir les accès gracieux"
+
+ def can_view(self, user_request, *args, **kwargs):
+ """Check if an user can view a Whitelist object.
+
+ :param self: The targeted object.
+ :param user_request: The user who ask for viewing the target.
+ :return: A boolean telling if the acces is granted and an explanation
+ text
+ """
+ if not user_request.has_perm('users.view_whitelist') and\
+ self.user != user_request:
+ return False, u"Vous n'avez pas le droit de voir les accès\
+ gracieux autre que les vôtres"
+ else:
+ return True, None
+
def __str__(self):
return str(self.user) + ' ' + str(self.raison)
diff --git a/users/templates/users/aff_bans.html b/users/templates/users/aff_bans.html
index dff72aa2..2e3957a1 100644
--- a/users/templates/users/aff_bans.html
+++ b/users/templates/users/aff_bans.html
@@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
-
+{% load acl %}
{% if ban_list.paginator %}
{% include "pagination.html" with list=ban_list %}
{% endif %}
@@ -47,9 +47,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ ban.date_start }}
{{ ban.date_end }}
- {% if is_bofh %}
+ {% can_edit ban %}
{% include 'buttons/edit.html' with href='users:edit-ban' id=ban.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='users:history' name='ban' id=ban.id %}
diff --git a/users/templates/users/aff_clubs.html b/users/templates/users/aff_clubs.html
index 0287e8a5..425e1f28 100644
--- a/users/templates/users/aff_clubs.html
+++ b/users/templates/users/aff_clubs.html
@@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% include "pagination.html" with list=clubs_list %}
{% endif %}
+{% load acl %}
+
@@ -38,7 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% for club in clubs_list %}
-
+ {% can_view club %}
+
{{ club.surname }}
{{ club.pseudo }}
{{ club.room }}
@@ -52,6 +55,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+ {% acl_end %}
{% endfor %}
diff --git a/users/templates/users/aff_listright.html b/users/templates/users/aff_listright.html
index 90d71bfe..080a7388 100644
--- a/users/templates/users/aff_listright.html
+++ b/users/templates/users/aff_listright.html
@@ -22,26 +22,78 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
-
+
Droit
Gid
+ Informations
Details
-
{% for listright in listright_list %}
-
- {{ listright.listright }}
+
+
+ {{ listright.name }}
+
{{ listright.gid }}
+
+
+
+ Utilisateurs ({{ listright.user_set.all|length }})
+
+
+ Ensemble des permissions ({{ listright.permissions.all|length }})
+
+
+
{{ listright.details }}
{% include 'buttons/edit.html' with href='users:edit-listright' id=listright.id %}
{% include 'buttons/history.html' with href='users:history' name='listright' id=listright.id %}
+
+
+
+
+
+
+ {% for perm in listright.permissions.all %}
+
+ {{perm.name}}
+
+ {% endfor %}
+
+
+
+
+
+
+ {% for user in listright.user_set.all %}
+
+ {{user}}
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
{% endfor %}
+
diff --git a/users/templates/users/aff_schools.html b/users/templates/users/aff_schools.html
index 5fcdeb72..64d92cd1 100644
--- a/users/templates/users/aff_schools.html
+++ b/users/templates/users/aff_schools.html
@@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
-
+{% load acl %}
@@ -33,7 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ school.name }}
+ {% can_edit school %}
{% include 'buttons/edit.html' with href='users:edit-school' id=school.id %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='users:history' name='school' id=school.id %}
diff --git a/users/templates/users/aff_serviceusers.html b/users/templates/users/aff_serviceusers.html
index 01e223aa..ddef0360 100644
--- a/users/templates/users/aff_serviceusers.html
+++ b/users/templates/users/aff_serviceusers.html
@@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
-
+{% load acl %}
@@ -37,9 +37,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ serviceuser.access_group }}
{{ serviceuser.comment }}
- {% include 'buttons/suppr.html' with href='users:del-serviceuser' id=serviceuser.id %}
- {% include 'buttons/edit.html' with href='users:edit-serviceuser' id=serviceuser.id %}
- {% include 'buttons/history.html' with href='users:history' name='serviceuser' id=serviceuser.id %}
+ {% can_delete serviceuser %}
+ {% include 'buttons/suppr.html' with href='users:del-serviceuser' id=serviceuser.id %}
+ {% acl_end %}
+ {% can_edit serviceuser %}
+ {% include 'buttons/edit.html' with href='users:edit-serviceuser' id=serviceuser.id %}
+ {% acl_end %}
+ {% include 'buttons/history.html' with href='users:history' name='serviceuser' id=serviceuser.id %}
{% endfor %}
diff --git a/users/templates/users/aff_whitelists.html b/users/templates/users/aff_whitelists.html
index d86d356b..099d4b39 100644
--- a/users/templates/users/aff_whitelists.html
+++ b/users/templates/users/aff_whitelists.html
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if white_list.paginator %}
{% include "pagination.html" with list=white_list %}
{% endif %}
-
+{% load acl %}
@@ -47,9 +47,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ whitelist.date_start }}
{{ whitelist.date_end }}
- {% if is_cableur %}
+ {% can_edit whitelist %}
{% include 'buttons/edit.html' with href='users:edit-whitelist' id=whitelist.id %}
- {% endif %}
+ {% acl_end %}
{% include 'buttons/history.html' with href='users:history' name='whitelist' id=whitelist.id %}
diff --git a/users/templates/users/del_right.html b/users/templates/users/del_right.html
deleted file mode 100644
index 1e31f320..00000000
--- a/users/templates/users/del_right.html
+++ /dev/null
@@ -1,75 +0,0 @@
-{% extends "users/sidebar.html" %}
-{% comment %}
-Re2o est un logiciel d'administration développé initiallement au rezometz. Il
-se veut agnostique au réseau considéré, de manière à être installable en
-quelques clics.
-
-Copyright © 2017 Gabriel Détraz
-Copyright © 2017 Goulven Kermarec
-Copyright © 2017 Augustin Lemesle
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 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 General Public License for more details.
-
-You should have received a copy of the GNU General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-{% endcomment %}
-
-{% load bootstrap3 %}
-
-{% block title %}Création et modification d'utilisateur{% endblock %}
-
-{% block content %}
-
-Gestion des droits
-
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/users/templates/users/index_listright.html b/users/templates/users/index_listright.html
index c2a81966..eb2c5370 100644
--- a/users/templates/users/index_listright.html
+++ b/users/templates/users/index_listright.html
@@ -24,12 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
Liste des droits
+ {% can_create ListRight %}
Ajouter un droit ou groupe
+ {% acl_end %}
Supprimer un ou plusieurs droits/groupes
{% include "users/aff_listright.html" with listright_list=listright_list %}
diff --git a/users/templates/users/index_schools.html b/users/templates/users/index_schools.html
index 6e7b16c3..ff2b8b8b 100644
--- a/users/templates/users/index_schools.html
+++ b/users/templates/users/index_schools.html
@@ -24,13 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
Liste des Établissements
Ensemble des établissements d'enseignement ou d'activité des utilisateurs crées
+ {% can_create School %}
Ajouter un établissement
+ {% acl_end %}
Supprimer un ou plusieurs établissements
{% include "users/aff_schools.html" with school_list=school_list %}
diff --git a/users/templates/users/index_serviceusers.html b/users/templates/users/index_serviceusers.html
index 61fd0a63..bb1c2d3c 100644
--- a/users/templates/users/index_serviceusers.html
+++ b/users/templates/users/index_serviceusers.html
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
+{% load acl %}
{% block title %}Utilisateurs{% endblock %}
@@ -31,7 +32,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Liste des service users LDAP
Les service users LDAP sont des utilisateurs spéciaux qui disposent d'un accès uniquement sur le ldap pour effectuer des opération d'authentification.
Il est recommandé de créer un service-user doté d'un login/mdp par service concerné
+ {% can_create ServiceUser %}
Ajouter un service user
+ {% acl_end %}
{% include "users/aff_serviceusers.html" with serviceusers_list=serviceusers_list %}
diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html
index f2bdd54e..0e5f30e4 100644
--- a/users/templates/users/profil.html
+++ b/users/templates/users/profil.html
@@ -24,31 +24,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %}
{% load bootstrap3 %}
-
+{% load acl %}
{% block title %}Profil{% endblock %}
{% block content %}
-{{ user.class_name }}
+{{ users.class_name }}
-
+
Editer
-
+
Changer le mot de passe
- {% if is_bureau %}
-
+ {% can_change User state %}
+
Changer le statut
-
-
- Ajouter un droit
+ {% acl_end %}
+ {% can_change User groups %}
+
+
+ Gérer les groupes
- {% endif %}
-
+ {% acl_end %}
+
Historique
@@ -59,58 +61,58 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Prénom
- {{ user.name }}
+ {{ users.name }}
Nom
- {{ user.surname }}
+ {{ users.surname }}
Pseudo
- {{ user.pseudo }}
+ {{ users.pseudo }}
E-mail
- {{ user.email }}
+ {{ users.email }}
Chambre
- {{ user.room }}
+ {{ users.room }}
Téléphone
- {{ user.telephone }}
+ {{ users.telephone }}
École
- {{ user.school }}
+ {{ users.school }}
Commentaire
- {{ user.comment }}
+ {{ users.comment }}
Date d'inscription
- {{ user.registered }}
+ {{ users.registered }}
Dernière connexion
- {{ user.last_login }}
+ {{ users.last_login }}
Fin d'adhésion
- {% if user.end_adhesion != None %}
- {{ user.end_adhesion }}
+ {% if users.end_adhesion != None %}
+ {{ users.end_adhesion }}
{% else %}
Non adhérent
{% endif %}
Accès gracieux
- {% if user.end_whitelist != None %}
- {{ user.end_whitelist }}
+ {% if users.end_whitelist != None %}
+ {{ users.end_whitelist }}
{% else %}
Aucun
{% endif %}
Bannissement
- {% if user.end_ban != None %}
- {{ user.end_ban }}
+ {% if users.end_ban != None %}
+ {{ users.end_ban }}
{% else %}
Non banni
{% endif %}
Statut
- {% if user.state == 0 %}
+ {% if users.state == 0 %}
Actif
- {% elif user.state == 1 %}
+ {% elif users.state == 1 %}
Désactivé
{% else %}
Archivé
@@ -118,31 +120,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Accès internet
- {% if user.has_access == True %}
- Actif (jusqu'au {{ user.end_access }})
+ {% if users.has_access == True %}
+ Actif (jusqu'au {{ users.end_access }})
{% else %}
Désactivé
{% endif %}
- Droits
- {% if list_droits %}
- {% for droit in list_droits %}{{ droit.right }}{% if list_droits|length != forloop.counter %} - {% endif %} {% endfor %}
+ Groupes
+ {% if users.groups.all %}
+ {{ users.groups.all|join:", "}}
{% else %}
Aucun
{% endif %}
- {% if user_solde %}
+ {% if user_solde %}
Solde
- {{ user.solde }} €
+ {{ users.solde }} €
- {% endif %}
- {% if user.shell %}
- Shell
- {{ user.shell }}
- {% endif %}
+ {% endif %}
+ {% if users.shell %}
+ Shell
+ {{ users.shell }}
+ {% endif %}
- {% if user.is_class_club %}
-
+ {% if users.is_class_club %}
+
Gérer admin et membres
@@ -155,7 +157,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Pseudo
- {% for admin in user.club.administrators.all %}
+ {% for admin in users.club.administrators.all %}
{{ admin.surname }}
{{ admin.name }}
@@ -172,7 +174,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Pseudo
- {% for admin in user.club.members.all %}
+ {% for admin in users.club.members.all %}
{{ admin.surname }}
{{ admin.name }}
@@ -182,28 +184,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
Machines
-
+
{% if machines_list %}
{% include "machines/aff_machines.html" with machines_list=machines_list %}
{% else %}
Aucune machine
{% endif %}
Cotisations
- {% if is_cableur %}{% endif%}
+
{% if facture_list %}
{% include "cotisations/aff_cotisations.html" with facture_list=facture_list %}
{% else %}
Aucune facture
{% endif %}
Bannissements
- {% if is_bofh %}{% endif %}
+ {% can_create Ban %}{% acl_end %}
{% if ban_list %}
{% include "users/aff_bans.html" with ban_list=ban_list %}
{% else %}
Aucun bannissement
{% endif %}
Accès à titre gracieux :
- {% if is_cableur %}{% endif %}
+ {% can_create Whitelist %}{% acl_end %}
{% if white_list %}
{% include "users/aff_whitelists.html" with white_list=white_list %}
{% else %}
diff --git a/users/templates/users/sidebar.html b/users/templates/users/sidebar.html
index d6b31acd..27f6b2b1 100644
--- a/users/templates/users/sidebar.html
+++ b/users/templates/users/sidebar.html
@@ -22,57 +22,67 @@ You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
-
+{% load acl %}
{% block sidebar %}
- {% if is_cableur %}
+ {% can_create Club %}
Créer un club/association
- {% endif %}
+ {% acl_end %}
+ {% can_create User %}
Créer un adhérent
+ {% acl_end %}
+ {% can_view_all Club %}
Clubs et assos
- {% if is_cableur %}
+ {% acl_end %}
+ {% can_view_all Adherent %}
Adherents
+ {% acl_end %}
+ {% can_view_all Ban %}
Bannissements
+ {% acl_end %}
+ {% can_view_all Whitelist %}
Accès à titre gracieux
+ {% acl_end %}
+ {% can_view_all School %}
Établissements
+ {% acl_end %}
+ {% can_view_all ListRight %}
- Droits
+ Groupes de droits
+ {% acl_end %}
+ {% can_view_all ServiceUser %}
Gérer les service users
- {% if is_bureau %}
-
-
- Retirer un droit
-
+ {% acl_end %}
+ {% can_change User state %}
Archiver en masse
- {% endif %}
- {% endif %}
+ {% acl_end %}
{% endblock %}
diff --git a/users/urls.py b/users/urls.py
index e669a0d9..425f9618 100644
--- a/users/urls.py
+++ b/users/urls.py
@@ -27,6 +27,7 @@ from __future__ import unicode_literals
from django.conf.urls import url
+import re2o
from . import views
urlpatterns = [
@@ -39,7 +40,9 @@ urlpatterns = [
name='edit-club-admin-members'
),
url(r'^state/(?P[0-9]+)$', views.state, name='state'),
+ url(r'^groups/(?P[0-9]+)$', views.groups, name='groups'),
url(r'^password/(?P[0-9]+)$', views.password, name='password'),
+ url(r'^del_group/(?P[0-9]+)/(?P[0-9]+)$', views.del_group, name='del-group'),
url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'),
url(
r'^edit_serviceuser/(?P[0-9]+)$',
@@ -63,8 +66,6 @@ urlpatterns = [
views.edit_whitelist,
name='edit-whitelist'
),
- url(r'^add_right/(?P[0-9]+)$', views.add_right, name='add-right'),
- url(r'^del_right/$', views.del_right, name='del-right'),
url(r'^add_school/$', views.add_school, name='add-school'),
url(
r'^edit_school/(?P[0-9]+)$',
@@ -94,34 +95,10 @@ urlpatterns = [
url(r'^reset_password/$', views.reset_password, name='reset-password'),
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
url(
- r'^history/(?Puser)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(
- r'^history/(?Pban)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(
- r'^history/(?Pwhitelist)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(
- r'^history/(?Pschool)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(
- r'^history/(?Plistright)/(?P[0-9]+)$',
- views.history,
- name='history'
- ),
- url(
- r'^history/(?Pserviceuser)/(?P[0-9]+)$',
- views.history,
- name='history'
+ r'^history/(?P\w+)/(?P[0-9]+)$',
+ re2o.views.history,
+ name='history',
+ kwargs={'application':'users'},
),
url(r'^$', views.index, name='index'),
url(r'^index_clubs/$', views.index_clubs, name='index-clubs'),
diff --git a/users/views.py b/users/views.py
index e0c71157..600c472e 100644
--- a/users/views.py
+++ b/users/views.py
@@ -55,7 +55,6 @@ from reversion import revisions as reversion
from users.serializers import MailSerializer
from users.models import (
User,
- Right,
Ban,
Whitelist,
School,
@@ -63,19 +62,15 @@ from users.models import (
Request,
ServiceUser,
Adherent,
- Club
+ Club,
)
from users.forms import (
- DelRightForm,
BanForm,
WhitelistForm,
DelSchoolForm,
DelListRightForm,
NewListRightForm,
- FullAdherentForm,
StateForm,
- FullClubForm,
- RightForm,
SchoolForm,
EditServiceUserForm,
ServiceUserForm,
@@ -85,14 +80,27 @@ from users.forms import (
MassArchiveForm,
PassForm,
ResetPasswordForm,
- ClubAdminandMembersForm
+ ClubAdminandMembersForm,
+ GroupForm
)
from cotisations.models import Facture
from machines.models import Machine
from preferences.models import OptionalUser, GeneralOption
from re2o.views import form
-from re2o.utils import all_has_access, SortTable
+from re2o.utils import (
+ all_has_access,
+ SortTable,
+)
+from re2o.acl import (
+ can_create,
+ can_edit,
+ can_delete_set,
+ can_delete,
+ can_view,
+ can_view_all,
+ can_change
+)
def password_change_action(u_form, user, request, req=False):
""" Fonction qui effectue le changeemnt de mdp bdd"""
@@ -109,18 +117,12 @@ def password_change_action(u_form, user, request, req=False):
kwargs={'userid':str(user.id)}
))
-
@login_required
+@can_create(Adherent)
def new_user(request):
""" Vue de création d'un nouvel utilisateur,
envoie un mail pour le mot de passe"""
- if not User.can_create(request.user):
- messages.error(request, "Vous ne pouvez pas accéder à ce menu")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- user = AdherentForm(request.POST or None)
+ user = AdherentForm(request.POST or None, user=request.user)
if user.is_valid():
user = user.save(commit=False)
with transaction.atomic(), reversion.create_revision():
@@ -138,11 +140,11 @@ def new_user(request):
@login_required
-@permission_required('cableur')
+@can_create(Club)
def new_club(request):
""" Vue de création d'un nouveau club,
envoie un mail pour le mot de passe"""
- club = ClubForm(request.POST or None)
+ club = ClubForm(request.POST or None, user=request.user)
if club.is_valid():
club = club.save(commit=False)
with transaction.atomic(), reversion.create_revision():
@@ -160,20 +162,10 @@ def new_club(request):
@login_required
-def edit_club_admin_members(request, clubid):
+@can_edit(Club)
+def edit_club_admin_members(request, club_instance, clubid):
"""Vue d'edition de la liste des users administrateurs et
membres d'un club"""
- try:
- club_instance = Club.objects.get(pk=clubid)
- except Club.DoesNotExist:
- messages.error(request, "Club inexistant")
- return redirect(reverse('users:index'))
- if not club_instance.can_edit(request.user):
- messages.error(request, "Vous ne pouvez pas accéder à ce menu")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
club = ClubAdminandMembersForm(request.POST or None, instance=club_instance)
if club.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -190,41 +182,24 @@ def edit_club_admin_members(request, clubid):
return form({'userform': club}, 'users/user.html', request)
-def select_user_edit_form(request, user):
- """Fonction de choix du bon formulaire, en fonction de:
- - droit
- - type d'object
- """
- if not request.user.has_perms(('cableur',)):
- if user.is_class_adherent:
- user = AdherentForm(request.POST or None, instance=user.adherent)
- elif user.is_class_club:
- user = ClubForm(request.POST or None, instance=user.club)
- else:
- if user.is_class_adherent:
- user = FullAdherentForm(request.POST or None, instance=user.adherent)
- elif user.is_class_club:
- user = FullClubForm(request.POST or None, instance=user.club)
- return user
-
-
@login_required
-def edit_info(request, userid):
+@can_edit(User)
+def edit_info(request, user, userid):
""" Edite un utilisateur à partir de son id,
si l'id est différent de request.user, vérifie la
possession du droit cableur """
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
- if not user.can_edit(request.user):
- messages.error(request, "Vous ne pouvez pas accéder à ce menu")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- user = select_user_edit_form(request, user)
+ if user.is_class_adherent:
+ user = AdherentForm(
+ request.POST or None,
+ instance=user.adherent,
+ user=request.user
+ )
+ elif user.is_class_club:
+ user = ClubForm(
+ request.POST or None,
+ instance=user.club,
+ user=request.user
+ )
if user.is_valid():
with transaction.atomic(), reversion.create_revision():
user.save()
@@ -241,15 +216,10 @@ def edit_info(request, userid):
@login_required
-@permission_required('bureau')
-def state(request, userid):
+@can_edit(User, 'state')
+def state(request, user, userid):
""" Changer l'etat actif/desactivé/archivé d'un user,
need droit bureau """
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
state = StateForm(request.POST or None, instance=user)
if state.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -273,29 +243,30 @@ def state(request, userid):
@login_required
-def password(request, userid):
+@can_edit(User, 'groups')
+def groups(request, user, userid):
+ group = GroupForm(request.POST or None, instance=user)
+ if group.is_valid():
+ with transaction.atomic(), reversion.create_revision():
+ reversion.set_user(request.user)
+ reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
+ field for field in group.changed_data
+ ))
+ group.save()
+ messages.success(request, "Groupes changés avec succès")
+ return redirect(reverse(
+ 'users:profil',
+ kwargs={'userid':str(userid)}
+ ))
+ return form({'userform': group}, 'users/user.html', request)
+
+
+@login_required
+@can_edit(User, 'password')
+def password(request, user, userid):
""" Reinitialisation d'un mot de passe à partir de l'userid,
pour self par défaut, pour tous sans droit si droit cableur,
pour tous si droit bureau """
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users'))
- if not user.can_edit(request.user):
- messages.error(request, "Vous ne pouvez pas accéder à ce menu")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- if not request.user.has_perms(('bureau',)) and user != request.user\
- and Right.objects.filter(user=user):
- messages.error(request, "Il faut les droits bureau pour modifier le\
- mot de passe d'un membre actif")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
u_form = PassForm(request.POST or None)
if u_form.is_valid():
return password_change_action(u_form, user, request)
@@ -303,7 +274,17 @@ def password(request, userid):
@login_required
-@permission_required('infra')
+@can_edit(User, 'groups')
+def del_group(request, user, userid, listrightid):
+ with transaction.atomic(), reversion.create_revision():
+ user.groups.remove(ListRight.objects.get(id=listrightid))
+ user.save()
+ messages.success(request, "Droit supprimé à %s" % user)
+ return redirect(reverse('users:index-listright'))
+
+
+@login_required
+@can_create(ServiceUser)
def new_serviceuser(request):
""" Vue de création d'un nouvel utilisateur service"""
user = ServiceUserForm(request.POST or None)
@@ -323,16 +304,9 @@ def new_serviceuser(request):
@login_required
-@permission_required('infra')
-def edit_serviceuser(request, userid):
- """ Edite un utilisateur à partir de son id,
- si l'id est différent de request.user,
- vérifie la possession du droit cableur """
- try:
- user = ServiceUser.objects.get(pk=userid)
- except ServiceUser.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
+@can_edit(ServiceUser)
+def edit_serviceuser(request, user, userid):
+ """ Edit a ServiceUser """
user = EditServiceUserForm(request.POST or None, instance=user)
if user.is_valid():
user_object = user.save(commit=False)
@@ -350,14 +324,9 @@ def edit_serviceuser(request, userid):
@login_required
-@permission_required('infra')
-def del_serviceuser(request, userid):
+@can_delete(ServiceUser)
+def del_serviceuser(request, user, userid):
"""Suppression d'un ou plusieurs serviceusers"""
- try:
- user = ServiceUser.objects.get(pk=userid)
- except ServiceUser.DoesNotExist:
- messages.error(request, u"Utilisateur inexistant")
- return redirect(reverse('users:index'))
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
user.delete()
@@ -372,65 +341,12 @@ def del_serviceuser(request, userid):
@login_required
-@permission_required('bureau')
-def add_right(request, userid):
- """ Ajout d'un droit à un user, need droit bureau """
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
- right = RightForm(request.POST or None)
- if right.is_valid():
- right = right.save(commit=False)
- right.user = user
- try:
- with transaction.atomic(), reversion.create_revision():
- reversion.set_user(request.user)
- reversion.set_comment("Ajout du droit %s" % right.right)
- right.save()
- messages.success(request, "Droit ajouté")
- except IntegrityError:
- pass
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(userid)}
- ))
- return form({'userform': right}, 'users/user.html', request)
-
-
-@login_required
-@permission_required('bureau')
-def del_right(request):
- """ Supprimer un droit à un user, need droit bureau """
- user_right_list = dict()
- for right in ListRight.objects.all():
- user_right_list[right] = DelRightForm(right, request.POST or None)
- for _keys, right_item in user_right_list.items():
- if right_item.is_valid():
- right_del = right_item.cleaned_data['rights']
- with transaction.atomic(), reversion.create_revision():
- reversion.set_user(request.user)
- reversion.set_comment("Retrait des droit %s" % ','.join(
- str(deleted_right) for deleted_right in right_del
- ))
- right_del.delete()
- messages.success(request, "Droit retiré avec succès")
- return redirect(reverse('users:index'))
- return form({'userform': user_right_list}, 'users/del_right.html', request)
-
-
-@login_required
-@permission_required('bofh')
-def add_ban(request, userid):
+@can_create(Ban)
+@can_edit(User)
+def add_ban(request, user, userid):
""" Ajouter un banissement, nécessite au moins le droit bofh
(a fortiori bureau)
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
ban_instance = Ban(user=user)
ban = BanForm(request.POST or None, instance=ban_instance)
if ban.is_valid():
@@ -450,18 +366,12 @@ def add_ban(request, userid):
)
return form({'userform': ban}, 'users/user.html', request)
-
@login_required
-@permission_required('bofh')
-def edit_ban(request, banid):
+@can_edit(Ban)
+def edit_ban(request, ban_instance, banid):
""" Editer un bannissement, nécessite au moins le droit bofh
(a fortiori bureau)
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
- try:
- ban_instance = Ban.objects.get(pk=banid)
- except Ban.DoesNotExist:
- messages.error(request, "Entrée inexistante")
- return redirect(reverse('users:index'))
ban = BanForm(request.POST or None, instance=ban_instance)
if ban.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -476,17 +386,13 @@ def edit_ban(request, banid):
@login_required
-@permission_required('cableur')
-def add_whitelist(request, userid):
+@can_create(Whitelist)
+@can_edit(User)
+def add_whitelist(request, user, userid):
""" Accorder un accès gracieux, temporaire ou permanent.
Need droit cableur
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
raison obligatoire"""
- try:
- user = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
whitelist_instance = Whitelist(user=user)
whitelist = WhitelistForm(
request.POST or None,
@@ -511,17 +417,12 @@ def add_whitelist(request, userid):
@login_required
-@permission_required('cableur')
-def edit_whitelist(request, whitelistid):
+@can_edit(Whitelist)
+def edit_whitelist(request, whitelist_instance, whitelistid):
""" Editer un accès gracieux, temporaire ou permanent.
Need droit cableur
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
raison obligatoire"""
- try:
- whitelist_instance = Whitelist.objects.get(pk=whitelistid)
- except Whitelist.DoesNotExist:
- messages.error(request, "Entrée inexistante")
- return redirect(reverse('users:index'))
whitelist = WhitelistForm(
request.POST or None,
instance=whitelist_instance
@@ -539,7 +440,7 @@ def edit_whitelist(request, whitelistid):
@login_required
-@permission_required('cableur')
+@can_create(School)
def add_school(request):
""" Ajouter un établissement d'enseignement à la base de donnée,
need cableur"""
@@ -555,15 +456,10 @@ def add_school(request):
@login_required
-@permission_required('cableur')
-def edit_school(request, schoolid):
+@can_edit(School)
+def edit_school(request, school_instance, schoolid):
""" Editer un établissement d'enseignement à partir du schoolid dans
la base de donnée, need cableur"""
- try:
- school_instance = School.objects.get(pk=schoolid)
- except School.DoesNotExist:
- messages.error(request, u"Entrée inexistante")
- return redirect(reverse('users:index'))
school = SchoolForm(request.POST or None, instance=school_instance)
if school.is_valid():
with transaction.atomic(), reversion.create_revision():
@@ -578,13 +474,13 @@ def edit_school(request, schoolid):
@login_required
-@permission_required('cableur')
-def del_school(request):
+@can_delete_set(School)
+def del_school(request, instances):
""" Supprimer un établissement d'enseignement à la base de donnée,
need cableur
Objet protégé, possible seulement si aucun user n'est affecté à
l'établissement """
- school = DelSchoolForm(request.POST or None)
+ school = DelSchoolForm(request.POST or None, instances=instances)
if school.is_valid():
school_dels = school.cleaned_data['schools']
for school_del in school_dels:
@@ -603,7 +499,7 @@ def del_school(request):
@login_required
-@permission_required('bureau')
+@can_create(ListRight)
def add_listright(request):
""" Ajouter un droit/groupe, nécessite droit bureau.
Obligation de fournir un gid pour la synchro ldap, unique """
@@ -619,15 +515,10 @@ def add_listright(request):
@login_required
-@permission_required('bureau')
-def edit_listright(request, listrightid):
+@can_edit(ListRight)
+def edit_listright(request, listright_instance, listrightid):
""" Editer un groupe/droit, necessite droit bureau,
à partir du listright id """
- try:
- listright_instance = ListRight.objects.get(pk=listrightid)
- except ListRight.DoesNotExist:
- messages.error(request, u"Entrée inexistante")
- return redirect(reverse('users:index'))
listright = ListRightForm(
request.POST or None,
instance=listright_instance
@@ -645,11 +536,11 @@ def edit_listright(request, listrightid):
@login_required
-@permission_required('bureau')
-def del_listright(request):
+@can_delete_set(ListRight)
+def del_listright(request, instances):
""" Supprimer un ou plusieurs groupe, possible si il est vide, need droit
bureau """
- listright = DelListRightForm(request.POST or None)
+ listright = DelListRightForm(request.POST or None, instances=instances)
if listright.is_valid():
listright_dels = listright.cleaned_data['listrights']
for listright_del in listright_dels:
@@ -661,14 +552,15 @@ def del_listright(request):
except ProtectedError:
messages.error(
request,
- "L'établissement %s est affecté à au moins un user, \
+ "Le groupe %s est affecté à au moins un user, \
vous ne pouvez pas le supprimer" % listright_del)
return redirect(reverse('users:index-listright'))
return form({'userform': listright}, 'users/user.html', request)
@login_required
-@permission_required('bureau')
+@can_view_all(User)
+@can_change(User, 'state')
def mass_archive(request):
""" Permet l'archivage massif"""
to_archive_date = MassArchiveForm(request.POST or None)
@@ -698,7 +590,7 @@ def mass_archive(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Adherent)
def index(request):
""" Affiche l'ensemble des adherents, need droit cableur """
options, _created = GeneralOption.objects.get_or_create()
@@ -724,16 +616,12 @@ def index(request):
@login_required
+@can_view_all(Club)
def index_clubs(request):
""" Affiche l'ensemble des clubs, need droit cableur """
options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number
- if not request.user.has_perms(('cableur',)):
- clubs_list = Club.objects.filter(
- Q(administrators=request.user.adherent) | Q(members=request.user.adherent)
- ).distinct().select_related('room')
- else:
- clubs_list = Club.objects.select_related('room')
+ clubs_list = Club.objects.select_related('room')
clubs_list = SortTable.sort(
clubs_list,
request.GET.get('col'),
@@ -754,7 +642,7 @@ def index_clubs(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Ban)
def index_ban(request):
""" Affiche l'ensemble des ban, need droit cableur """
options, _created = GeneralOption.objects.get_or_create()
@@ -780,7 +668,7 @@ def index_ban(request):
@login_required
-@permission_required('cableur')
+@can_view_all(Whitelist)
def index_white(request):
""" Affiche l'ensemble des whitelist, need droit cableur """
options, _created = GeneralOption.objects.get_or_create()
@@ -810,7 +698,7 @@ def index_white(request):
@login_required
-@permission_required('cableur')
+@can_view_all(School)
def index_school(request):
""" Affiche l'ensemble des établissement, need droit cableur """
school_list = School.objects.order_by('name')
@@ -822,10 +710,10 @@ def index_school(request):
@login_required
-@permission_required('cableur')
+@can_view_all(ListRight)
def index_listright(request):
""" Affiche l'ensemble des droits , need droit cableur """
- listright_list = ListRight.objects.order_by('listright')
+ listright_list = ListRight.objects.order_by('unix_name')
return render(
request,
'users/index_listright.html',
@@ -834,7 +722,7 @@ def index_listright(request):
@login_required
-@permission_required('cableur')
+@can_view_all(ServiceUser)
def index_serviceusers(request):
""" Affiche les users de services (pour les accès ldap)"""
serviceusers_list = ServiceUser.objects.order_by('pseudo')
@@ -845,95 +733,6 @@ def index_serviceusers(request):
)
-@login_required
-def history(request, object_name, object_id):
- """ Affichage de l'historique : (acl, argument)
- user : self or cableur, userid,
- ban : self or cableur, banid,
- whitelist : self or cableur, whitelistid,
- school : cableur, schoolid,
- listright : cableur, listrightid """
- if object_name == 'user':
- try:
- object_instance = User.objects.get(pk=object_id)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
- if not object_instance.can_view(request.user):
- messages.error(request, "Vous ne pouvez pas afficher ce menu")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object_name == 'serviceuser' and request.user.has_perms(('cableur',)):
- try:
- object_instance = ServiceUser.objects.get(pk=object_id)
- except ServiceUser.DoesNotExist:
- messages.error(request, "User service inexistant")
- return redirect(reverse('users:index'))
- elif object_name == 'ban':
- try:
- object_instance = Ban.objects.get(pk=object_id)
- except Ban.DoesNotExist:
- messages.error(request, "Bannissement inexistant")
- return redirect(reverse('users:index'))
- if not request.user.has_perms(('cableur',)) and\
- object_instance.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher les bans\
- d'un autre user que vous sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object_name == 'whitelist':
- try:
- object_instance = Whitelist.objects.get(pk=object_id)
- except Whitelist.DoesNotExist:
- messages.error(request, "Whitelist inexistant")
- return redirect(reverse('users:index'))
- if not request.user.has_perms(('cableur',)) and\
- object_instance.user != request.user:
- messages.error(request, "Vous ne pouvez pas afficher les\
- whitelist d'un autre user que vous sans droit cableur")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
- elif object_name == 'school' and request.user.has_perms(('cableur',)):
- try:
- object_instance = School.objects.get(pk=object_id)
- except School.DoesNotExist:
- messages.error(request, "Ecole inexistante")
- return redirect(reverse('users:index'))
- elif object_name == 'listright' and request.user.has_perms(('cableur',)):
- try:
- object_instance = ListRight.objects.get(pk=object_id)
- except ListRight.DoesNotExist:
- messages.error(request, "Droit inexistant")
- return redirect(reverse('users:index'))
- else:
- messages.error(request, "Objet inconnu")
- return redirect(reverse('users:index'))
- options, _created = GeneralOption.objects.get_or_create()
- pagination_number = options.pagination_number
- reversions = Version.objects.get_for_object(object_instance)
- paginator = Paginator(reversions, pagination_number)
- page = request.GET.get('page')
- try:
- reversions = paginator.page(page)
- except PageNotAnInteger:
- # If page is not an integer, deliver first page.
- reversions = paginator.page(1)
- except EmptyPage:
- # If page is out of range (e.g. 9999), deliver last page of results.
- reversions = paginator.page(paginator.num_pages)
- return render(
- request,
- 're2o/history.html',
- {'reversions': reversions, 'object': object_instance}
- )
-
-
@login_required
def mon_profil(request):
""" Lien vers profil, renvoie request.id à la fonction """
@@ -944,19 +743,9 @@ def mon_profil(request):
@login_required
-def profil(request, userid):
+@can_view(User)
+def profil(request, users, userid):
""" Affiche un profil, self or cableur, prend un userid en argument """
- try:
- users = User.objects.get(pk=userid)
- except User.DoesNotExist:
- messages.error(request, "Utilisateur inexistant")
- return redirect(reverse('users:index'))
- if not users.can_view(request.user):
- messages.error(request, "Vous ne pouvez pas accéder à ce menu")
- return redirect(reverse(
- 'users:profil',
- kwargs={'userid':str(request.user.id)}
- ))
machines = Machine.objects.filter(user=users).select_related('user')\
.prefetch_related('interface_set__domain__extension')\
.prefetch_related('interface_set__ipv4__ip_type__extension')\
@@ -989,19 +778,17 @@ def profil(request, userid):
request.GET.get('order'),
SortTable.USERS_INDEX_WHITE
)
- list_droits = Right.objects.filter(user=users)
options, _created = OptionalUser.objects.get_or_create()
user_solde = options.user_solde
return render(
request,
'users/profil.html',
{
- 'user': users,
+ 'users': users,
'machines_list': machines,
'facture_list': factures,
'ban_list': bans,
'white_list': whitelists,
- 'list_droits': list_droits,
'user_solde': user_solde,
}
)