mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-23 03:43:12 +00:00
commit
0423442556
117 changed files with 5513 additions and 1919 deletions
|
@ -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 *
|
||||
|
|
40
cotisations/acl.py
Normal file
40
cotisations/acl.py
Normal file
|
@ -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.
|
||||
|
||||
"""cotisations.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('cotisations')
|
||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
|
@ -43,6 +43,8 @@ from django.forms import ModelForm, Form
|
|||
from django.core.validators import MinValueValidator
|
||||
from .models import Article, Paiement, Facture, Banque
|
||||
|
||||
from re2o.field_permissions import FieldPermissionFormMixin
|
||||
|
||||
|
||||
class NewFactureForm(ModelForm):
|
||||
"""Creation d'une facture, moyen de paiement, banque et numero
|
||||
|
@ -141,27 +143,18 @@ class NewFactureFormPdf(Form):
|
|||
)
|
||||
|
||||
|
||||
class EditFactureForm(NewFactureForm):
|
||||
class EditFactureForm(FieldPermissionFormMixin, NewFactureForm):
|
||||
"""Edition d'une facture : moyen de paiement, banque, user parent"""
|
||||
class Meta(NewFactureForm.Meta):
|
||||
fields = ['paiement', 'banque', 'cheque', 'user']
|
||||
model = Facture
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EditFactureForm, self).__init__(*args, **kwargs)
|
||||
self.fields['user'].label = 'Adherent'
|
||||
self.fields['user'].empty_label = "Séléctionner\
|
||||
l'adhérent propriétaire"
|
||||
|
||||
|
||||
class TrezEditFactureForm(EditFactureForm):
|
||||
"""Vue pour édition controle trésorier"""
|
||||
class Meta(EditFactureForm.Meta):
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TrezEditFactureForm, self).__init__(*args, **kwargs)
|
||||
self.fields['valid'].label = 'Validité de la facture'
|
||||
self.fields['control'].label = 'Contrôle de la facture'
|
||||
|
||||
|
||||
class ArticleForm(ModelForm):
|
||||
|
@ -180,11 +173,19 @@ class DelArticleForm(Form):
|
|||
"""Suppression d'un ou plusieurs articles en vente. Choix
|
||||
parmis les modèles"""
|
||||
articles = forms.ModelMultipleChoiceField(
|
||||
queryset=Article.objects.all(),
|
||||
queryset=Article.objects.none(),
|
||||
label="Articles actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelArticleForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['articles'].queryset = instances
|
||||
else:
|
||||
self.fields['articles'].queryset = Article.objects.all()
|
||||
|
||||
|
||||
class PaiementForm(ModelForm):
|
||||
"""Creation d'un moyen de paiement, champ text moyen et type
|
||||
|
@ -204,11 +205,19 @@ class DelPaiementForm(Form):
|
|||
"""Suppression d'un ou plusieurs moyens de paiements, selection
|
||||
parmis les models"""
|
||||
paiements = forms.ModelMultipleChoiceField(
|
||||
queryset=Paiement.objects.all(),
|
||||
queryset=Paiement.objects.none(),
|
||||
label="Moyens de paiement actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelPaiementForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['paiements'].queryset = instances
|
||||
else:
|
||||
self.fields['paiements'].queryset = Paiement.objects.all()
|
||||
|
||||
|
||||
class BanqueForm(ModelForm):
|
||||
"""Creation d'une banque, field name"""
|
||||
|
@ -225,7 +234,15 @@ class BanqueForm(ModelForm):
|
|||
class DelBanqueForm(Form):
|
||||
"""Selection d'une ou plusieurs banques, pour suppression"""
|
||||
banques = forms.ModelMultipleChoiceField(
|
||||
queryset=Banque.objects.all(),
|
||||
queryset=Banque.objects.none(),
|
||||
label="Banques actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelBanqueForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['banques'].queryset = instances
|
||||
else:
|
||||
self.fields['banques'].queryset = Banque.objects.all()
|
||||
|
|
39
cotisations/migrations/0028_auto_20171231_0007.py
Normal file
39
cotisations/migrations/0028_auto_20171231_0007.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-30 23:07
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0027_auto_20171029_1156'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='article',
|
||||
options={'permissions': (('view_article', 'Peut voir un objet article'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='banque',
|
||||
options={'permissions': (('view_banque', 'Peut voir un objet banque'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='cotisation',
|
||||
options={'permissions': (('view_cotisation', 'Peut voir un objet cotisation'), ('change_all_cotisation', 'Superdroit, peut modifier toutes les cotisations'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='facture',
|
||||
options={'permissions': (('change_facture_control', "Peut changer l'etat de controle"), ('change_facture_pdf', 'Peut éditer une facture pdf'), ('view_facture', 'Peut voir un objet facture'), ('change_all_facture', 'Superdroit, peut modifier toutes les factures'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='paiement',
|
||||
options={'permissions': (('view_paiement', 'Peut voir un objet paiement'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='vente',
|
||||
options={'permissions': (('view_vente', 'Peut voir un objet vente'), ('change_all_vente', 'Superdroit, peut modifier toutes les ventes'))},
|
||||
),
|
||||
]
|
|
@ -56,8 +56,10 @@ from django.db.models import Max
|
|||
from django.utils import timezone
|
||||
from machines.models import regen
|
||||
|
||||
from re2o.field_permissions import FieldPermissionModelMixin
|
||||
|
||||
class Facture(models.Model):
|
||||
|
||||
class Facture(FieldPermissionModelMixin, models.Model):
|
||||
""" Définition du modèle des factures. Une facture regroupe une ou
|
||||
plusieurs ventes, rattachée à un user, et reliée à un moyen de paiement
|
||||
et si il y a lieu un numero pour les chèques. Possède les valeurs
|
||||
|
@ -76,6 +78,15 @@ class Facture(models.Model):
|
|||
valid = models.BooleanField(default=True)
|
||||
control = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
abstract = False
|
||||
permissions = (
|
||||
("change_facture_control", "Peut changer l'etat de controle"),
|
||||
("change_facture_pdf", "Peut éditer une facture pdf"),
|
||||
("view_facture", "Peut voir un objet facture"),
|
||||
("change_all_facture", "Superdroit, peut modifier toutes les factures"),
|
||||
)
|
||||
|
||||
def prix(self):
|
||||
"""Renvoie le prix brut sans les quantités. Méthode
|
||||
dépréciée"""
|
||||
|
@ -103,6 +114,59 @@ class Facture(models.Model):
|
|||
).values_list('name', flat=True))
|
||||
return name
|
||||
|
||||
def get_instance(factureid, *args, **kwargs):
|
||||
return Facture.objects.get(pk=factureid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_facture'), u"Vous n'avez pas le\
|
||||
droit de créer des factures"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.change_facture'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer les factures"
|
||||
elif not user_request.has_perm('cotisations.change_all_facture') and\
|
||||
(self.control or not self.valid):
|
||||
return False, u"Vous n'avez pas le droit d'éditer une facture\
|
||||
controlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.delete_facture'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une facture"
|
||||
if self.control or not self.valid:
|
||||
return False, u"Vous ne pouvez pas supprimer une facture\
|
||||
contrôlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_facture'):
|
||||
return False, u"Vous n'avez pas le droit de voir les factures"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_facture') and\
|
||||
self.user != user_request:
|
||||
return False, u"Vous ne pouvez pas afficher l'historique d'une\
|
||||
facture d'un autre user que vous sans droit cableur"
|
||||
elif not self.valid:
|
||||
return False, u"La facture est invalidée et ne peut être affichée"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
@staticmethod
|
||||
def can_change_control(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_facture_control'), "Vous ne pouvez pas éditer le controle sans droit trésorier"
|
||||
|
||||
@staticmethod
|
||||
def can_change_pdf(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_facture_pdf'), "Vous ne pouvez pas éditer une facture sans droit trésorier"
|
||||
|
||||
field_permissions = {
|
||||
'control': can_change_control,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user) + ' ' + str(self.date)
|
||||
|
||||
|
@ -149,6 +213,12 @@ class Vente(models.Model):
|
|||
max_length=255
|
||||
)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_vente", "Peut voir un objet vente"),
|
||||
("change_all_vente", "Superdroit, peut modifier toutes les ventes"),
|
||||
)
|
||||
|
||||
def prix_total(self):
|
||||
"""Renvoie le prix_total de self (nombre*prix)"""
|
||||
return self.prix*self.number
|
||||
|
@ -201,6 +271,46 @@ class Vente(models.Model):
|
|||
self.update_cotisation()
|
||||
super(Vente, self).save(*args, **kwargs)
|
||||
|
||||
def get_instance(venteid, *args, **kwargs):
|
||||
return Vente.objects.get(pk=venteid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_vente'), u"Vous n'avez pas le\
|
||||
droit de créer des ventes"
|
||||
return True, None
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.change_vente'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer les ventes"
|
||||
elif not user_request.has_perm('cotisations.change_all_vente') and\
|
||||
(self.facture.control or not self.facture.valid):
|
||||
return False, u"Vous n'avez pas le droit d'éditer une vente\
|
||||
controlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.delete_vente'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une vente"
|
||||
if self.facture.control or not self.facture.valid:
|
||||
return False, u"Vous ne pouvez pas supprimer une vente\
|
||||
contrôlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_vente'):
|
||||
return False, u"Vous n'avez pas le droit de voir les ventes"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_vente') and\
|
||||
self.facture.user != user_request:
|
||||
return False, u"Vous ne pouvez pas afficher l'historique d'une\
|
||||
facture d'un autre user que vous sans droit cableur"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name) + ' ' + str(self.facture)
|
||||
|
||||
|
@ -269,6 +379,11 @@ class Article(models.Model):
|
|||
|
||||
unique_together = ('name', 'type_user')
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_article", "Peut voir un objet article"),
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if self.name.lower() == "solde":
|
||||
raise ValidationError("Solde est un nom d'article invalide")
|
||||
|
@ -277,6 +392,29 @@ class Article(models.Model):
|
|||
"La durée est obligatoire si il s'agit d'une cotisation"
|
||||
)
|
||||
|
||||
def get_instance(articleid, *args, **kwargs):
|
||||
return Article.objects.get(pk=articleid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_article'), u"Vous n'avez pas le\
|
||||
droit d'ajouter des articles"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_article'), u"Vous n'avez pas le\
|
||||
droit d'éditer des articles"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.delete_article'), u"Vous n'avez pas le\
|
||||
droit de supprimer des articles"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_article'), u"Vous n'avez pas le\
|
||||
droit de voir des articles"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_article'), u"Vous n'avez pas le\
|
||||
droit de voir des articles"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -287,6 +425,34 @@ class Banque(models.Model):
|
|||
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_banque", "Peut voir un objet banque"),
|
||||
)
|
||||
|
||||
def get_instance(banqueid, *args, **kwargs):
|
||||
return Banque.objects.get(pk=banqueid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_banque'), u"Vous n'avez pas le\
|
||||
droit d'ajouter des banques"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_banque'), u"Vous n'avez pas le\
|
||||
droit d'éditer des banques"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.delete_banque'), u"Vous n'avez pas le\
|
||||
droit de supprimer des banques"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_banque'), u"Vous n'avez pas le\
|
||||
droit de voir des banques"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_banque'), u"Vous n'avez pas le\
|
||||
droit de voir des banques"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -302,6 +468,34 @@ class Paiement(models.Model):
|
|||
moyen = models.CharField(max_length=255)
|
||||
type_paiement = models.IntegerField(choices=PAYMENT_TYPES, default=0)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_paiement", "Peut voir un objet paiement"),
|
||||
)
|
||||
|
||||
def get_instance(paiementid, *args, **kwargs):
|
||||
return Paiement.objects.get(pk=paiementid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_paiement'), u"Vous n'avez pas le\
|
||||
droit d'ajouter des paiements"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_paiement'), u"Vous n'avez pas le\
|
||||
droit d'éditer des paiements"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.delete_paiement'), u"Vous n'avez pas le\
|
||||
droit de supprimer des paiements"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_paiement'), u"Vous n'avez pas le\
|
||||
droit de voir des paiements"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_paiement'), u"Vous n'avez pas le\
|
||||
droit de voir des paiements"
|
||||
|
||||
def __str__(self):
|
||||
return self.moyen
|
||||
|
||||
|
@ -334,6 +528,52 @@ class Cotisation(models.Model):
|
|||
date_start = models.DateTimeField()
|
||||
date_end = models.DateTimeField()
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_cotisation", "Peut voir un objet cotisation"),
|
||||
("change_all_cotisation", "Superdroit, peut modifier toutes les cotisations"),
|
||||
)
|
||||
|
||||
def get_instance(cotisationid, *args, **kwargs):
|
||||
return Cotisations.objects.get(pk=cotisationid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_cotisation'), u"Vous n'avez pas le\
|
||||
droit de créer des cotisations"
|
||||
return True, None
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.change_cotisation'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer les cotisations"
|
||||
elif not user_request.has_perm('cotisations.change_all_cotisation') and\
|
||||
(self.vente.facture.control or not self.vente.facture.valid):
|
||||
return False, u"Vous n'avez pas le droit d'éditer une cotisation\
|
||||
controlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.delete_cotisation'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une cotisations"
|
||||
if self.vente.facture.control or not self.vente.facture.valid:
|
||||
return False, u"Vous ne pouvez pas supprimer une cotisations\
|
||||
contrôlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_cotisation'):
|
||||
return False, u"Vous n'avez pas le droit de voir les cotisations"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_cotisation') and\
|
||||
self.vente.facture.user != user_request:
|
||||
return False, u"Vous ne pouvez pas afficher l'historique d'une\
|
||||
cotisation d'un autre user que vous sans droit cableur"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.vente)
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -41,11 +43,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ article.duration }}</td>
|
||||
<td>{{ article.type_user }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_trez %}
|
||||
{% can_edit article %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-article' article.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'article' article.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,11 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ banque.name }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_trez %}
|
||||
{% can_edit banque %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-banque' banque.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'banque' banque.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
|
|
|
@ -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.,
|
|||
<td>{{ facture.paiement }}</td>
|
||||
<td>{{ facture.date }}</td>
|
||||
<td>{{ facture.id }}</td>
|
||||
{% if is_cableur %}
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" id="editionfacture" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
|
@ -55,17 +56,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="editionfacture">
|
||||
{% if facture.valid and not facture.control or is_trez %}
|
||||
{% can_edit facture %}
|
||||
<li><a href="{% url 'cotisations:edit-facture' facture.id %}"><i class="glyphicon glyphicon-bitcoin"></i> Modifier</a></li>
|
||||
<li><a href="{% url 'cotisations:del-facture' facture.id %}"><i class="glyphicon glyphicon-trash"></i> Supprimer</a></li>
|
||||
<li><a href="{% url 'cotisations:history' 'facture' facture.id %}"><i class="glyphicon glyphicon-time"></i> Historique</a></li>
|
||||
{% else %}
|
||||
{% acl_else %}
|
||||
<li>Facture controlée</li>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% can_delete facture %}
|
||||
<li><a href="{% url 'cotisations:del-facture' facture.id %}"><i class="glyphicon glyphicon-trash"></i> Supprimer</a></li>
|
||||
{% acl_end %}
|
||||
<li><a href="{% url 'cotisations:history' 'facture' facture.id %}"><i class="glyphicon glyphicon-time"></i> Historique</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if facture.valid %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:facture-pdf' facture.id %}">
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,11 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ paiement.moyen }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_trez %}
|
||||
{% can_edit paiement %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-paiement' paiement.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'paiement' paiement.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des types d'articles</h2>
|
||||
{% if is_trez %}
|
||||
{% can_create Article %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-article' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type d'articles</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types d'articles</a>
|
||||
{% endif %}
|
||||
{% include "cotisations/aff_article.html" with article_list=article_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des banques</h2>
|
||||
{% can_create Banque %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-banque' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une banque</a>
|
||||
{% if is_trez %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer une ou plusieurs banques</a>
|
||||
{% endif %}
|
||||
{% include "cotisations/aff_banque.html" with banque_list=banque_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des types de paiements</h2>
|
||||
{% if is_trez %}
|
||||
{% can_create Paiement %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-paiement' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type de paiement</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-paiement' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types de paiements</a>
|
||||
{% endif %}
|
||||
{% include "cotisations/aff_paiement.html" with paiement_list=paiement_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<a class="list-group-item list-group-item-success" href="{% url "cotisations:new-facture-pdf" %}">
|
||||
<i class="glyphicon glyphicon-plus"></i>
|
||||
Créer une facture
|
||||
|
@ -34,21 +35,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<i class="glyphicon glyphicon-eye-open"></i>
|
||||
Contrôler les factures
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% can_view_all Facture %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Factures
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Article %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-article" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Articles en vente
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Banque %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-banque" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Banques
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Paiement %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-paiement" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Moyens de paiement
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -24,6 +24,7 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.conf.urls import url
|
||||
|
||||
import re2o
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -99,20 +100,11 @@ urlpatterns = [
|
|||
views.index_paiement,
|
||||
name='index-paiement'
|
||||
),
|
||||
url(r'^history/(?P<object_name>facture)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(r'^history/(?P<object_name>article)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(r'^history/(?P<object_name>paiement)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>banque)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
url(
|
||||
r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||
re2o.views.history,
|
||||
name='history',
|
||||
kwargs={'application':'cotisations'},
|
||||
),
|
||||
url(r'^control/$',
|
||||
views.control,
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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 *
|
||||
|
|
40
logs/acl.py
Normal file
40
logs/acl.py
Normal file
|
@ -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."
|
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
|
||||
{% load logs_extra %}
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
|
@ -47,14 +48,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ revision.user }}</td>
|
||||
<td>{{ revision.date_created }}</td>
|
||||
<td>{{ revision.comment }}</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
|
||||
{% load logs_extra %}
|
||||
|
||||
{% load acl %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -51,14 +51,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
</i>)
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'whitelist' %}
|
||||
<tr class="success">
|
||||
|
@ -74,14 +74,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
</i>)
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history%}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'user' %}
|
||||
<tr>
|
||||
|
@ -93,14 +93,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
(<i>{{ v.comment }}</i>)
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'vente' %}
|
||||
<tr>
|
||||
|
@ -112,14 +112,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
(<i>+{{ v.version.object.duration }} mois</i>)
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'interface' %}
|
||||
<tr>
|
||||
|
@ -131,14 +131,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
(<i>{{ v.comment }}</i>)
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -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 %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:index" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
Résumé
|
||||
|
@ -50,5 +51,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<i class="glyphicon glyphicon-stats"></i>
|
||||
Utilisateurs
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 *
|
||||
|
|
40
machines/acl.py
Normal file
40
machines/acl.py
Normal file
|
@ -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."
|
|
@ -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,11 +427,19 @@ 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"""
|
||||
|
@ -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
|
||||
|
|
79
machines/migrations/0070_auto_20171231_1947.py
Normal file
79
machines/migrations/0070_auto_20171231_1947.py
Normal file
|
@ -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'),)},
|
||||
),
|
||||
]
|
19
machines/migrations/0071_auto_20171231_2100.py
Normal file
19
machines/migrations/0071_auto_20171231_2100.py
Normal file
|
@ -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'),)},
|
||||
),
|
||||
]
|
1125
machines/models.py
1125
machines/models.py
File diff suppressed because it is too large
Load diff
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,7 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ alias }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -45,9 +47,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ extension.origin_v6 }}</td>
|
||||
{% endif %}
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ type.vlan }}</td>
|
||||
<td>{{ type.ouverture_ports }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table">
|
||||
<table class="table" id="machines_table">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
|
@ -50,30 +52,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</a>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for interface in machine.interface_set.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if interface.domain.related_domain.all %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{{ interface.domain }} <span class="caret"></span>
|
||||
{{ interface.domain }}
|
||||
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseDomain_{{interface.id}}" aria-expanded="true" aria-controls="collapseDomain_{{interface.id}}">
|
||||
Afficher les alias
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="editioninterface">
|
||||
{% for al in interface.domain.related_domain.all %}
|
||||
<li>
|
||||
<a href="http://{{ al }}">
|
||||
{{ al }}
|
||||
<i class="glyphicon glyphicon-share-alt"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ interface.domain }}
|
||||
{% endif %}
|
||||
|
@ -97,27 +92,53 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<i class="glyphicon glyphicon-edit"></i> <span class="caret"></span>
|
||||
</button>
|
||||
{% 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 %}
|
||||
{% acl_end %}
|
||||
<ul class="dropdown-menu" aria-labelledby="editioninterface">
|
||||
{% can_edit interface %}
|
||||
<li>
|
||||
<a href="{% url 'machines:edit-interface' interface.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i> Editer
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
{% can_create Domain interface.id %}
|
||||
<li>
|
||||
<a href="{% url 'machines:index-alias' interface.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i> Gerer les alias
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
{% can_create OuverturePortList %}
|
||||
<li>
|
||||
<a href="{% url 'machines:port-config' interface.id%}">
|
||||
<i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% if interface.domain.related_domain.all %}
|
||||
<tr>
|
||||
<td colspan=5 style="border-top: none; padding: 1px;">
|
||||
<div class="collapse in" id="collapseDomain_{{interface.id}}">
|
||||
<ul class="list-group" style="margin-bottom: 0px;">
|
||||
{% for al in interface.domain.related_domain.all %}
|
||||
<li class="list-group-item col-xs-6 col-sm-4 col-md-3" style="border: none;">
|
||||
<a href="http://{{ al }}">
|
||||
{{ al }}
|
||||
<i class="glyphicon glyphicon-share-alt"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="8"></td>
|
||||
|
@ -126,6 +147,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
$("#machines_table").ready( function() {
|
||||
var alias_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.domain.related_domain.all %}$("#collapseDomain_{{interface.id}}"), {% endif %}{% endfor %}{% endfor %}];
|
||||
for (var i=0 ; i<alias_div.length ; i++) {
|
||||
alias_div[i].collapse('hide');
|
||||
}
|
||||
} );
|
||||
</script>
|
||||
|
||||
{% if machines_list.paginator %}
|
||||
{% include "pagination.html" with list=machines_list %}
|
||||
{% endif %}
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -35,9 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ type.type }}</td>
|
||||
<td>{{ type.ip_type }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -38,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ mx.priority }}</td>
|
||||
<td>{{ mx.name }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -41,9 +43,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ nas.port_access_mode }}</td>
|
||||
<td>{{ nas.autocapture_mac }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ ns.zone }}</td>
|
||||
<td>{{ ns.ns }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -40,9 +42,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ service.regular_time_regen }}</td>
|
||||
<td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -44,9 +46,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ soa.expire }}</td>
|
||||
<td>{{ soa.ttl }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ srv.port }}</td>
|
||||
<td>{{ srv.target }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ txt.zone }}</td>
|
||||
<td>{{ txt.dns_entry }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -39,9 +41,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ vlan.comment }}</td>
|
||||
<td>{% for range in vlan.iptype_set.all %}{{ range }}, {% endfor%}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des extensions</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Extension %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-extension' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une extension</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-extension' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer une ou plusieurs extensions</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_extension.html" with extension_list=extension_list %}
|
||||
|
||||
<h2>Liste des enregistrements SOA</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create SOA %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SOA</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SOA</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_soa.html" with soa_list=soa_list %}
|
||||
<h2>Liste des enregistrements MX</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Mx %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement MX</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-mx' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement MX</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_mx.html" with mx_list=mx_list %}
|
||||
<h2>Liste des enregistrements NS</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Ns %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-ns' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement NS</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-ns' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement NS</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_ns.html" with ns_list=ns_list %}
|
||||
<h2>Liste des enregistrements TXT</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Txt %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-txt' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement TXT</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement TXT</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_txt.html" with txt_list=txt_list %}
|
||||
<h2>Liste des enregistrements SRV</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Srv %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-srv' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SRV</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-srv' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SRV</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_srv.html" with srv_list=srv_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des types d'ip</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create IpType %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-iptype' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type d'ip</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-iptype' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types d'ip</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_iptype.html" with iptype_list=iptype_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des types de machines</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create MachineType %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-machinetype' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type de machine</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-machinetype' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types de machines</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_machinetype.html" with machinetype_list=machinetype_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des nas</h2>
|
||||
<h5>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</h5>
|
||||
{% if is_infra %}
|
||||
{% can_create Nas %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-nas' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type de nas</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-nas' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types nas</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_nas.html" with nas_list=nas_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Configuration de ports{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des configurations de ports</h2>
|
||||
{% can_create OuverturePortList %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-portlist' %}"><i class="glyphicon glyphicon-plus"></i>Ajouter une configuration</a>
|
||||
{% acl_end %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -44,8 +48,12 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des services</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Service %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-service' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un service</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-service' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs service</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_service.html" with service_list=service_list %}
|
||||
<h2>Etat des serveurs</h2>
|
||||
{% include "machines/aff_servers.html" with servers_list=servers_list %}
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Liste des vlans</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Vlan %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-vlan' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un vlan</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-vlan' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs vlan</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_vlan.html" with vlan_list=vlan_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Machines
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all MachineType %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-machinetype" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Types de machines
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Extension %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-extension" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Extensions et zones
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all IpType %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-iptype" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Plages d'IP
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Vlan %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-vlan" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Vlans
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Nas %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-nas" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Gestion des nas
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Service %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-service" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Services (dhcp, dns...)
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if is_cableur %}
|
||||
{% acl_end %}
|
||||
{% can_view_all OuverturePortList %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Ouverture de ports
|
||||
</a>
|
||||
{%endif%}
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -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<interfaceid>[0-9]+)$', views.add_alias, name='add-alias'),
|
||||
url(r'^edit_alias/(?P<aliasid>[0-9]+)$', views.edit_alias, name='edit-alias'),
|
||||
url(r'^edit_alias/(?P<domainid>[0-9]+)$', views.edit_alias, name='edit-alias'),
|
||||
url(r'^del_alias/(?P<interfaceid>[0-9]+)$', views.del_alias, name='del-alias'),
|
||||
url(r'^index_alias/(?P<interfaceid>[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<nasid>[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/(?P<object>machine)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>interface)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>machinetype)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>extension)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>soa)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>mx)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>ns)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>txt)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>srv)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>iptype)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>alias)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>vlan)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>nas)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>service)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(
|
||||
r'history/(?P<object_name>\w+)/(?P<object_id>[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<pk>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
|
||||
url(r'^del_portlist/(?P<pk>[0-9]+)$', views.del_portlist, name='del-portlist'),
|
||||
url(r'^edit_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
|
||||
url(r'^del_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.del_portlist, name='del-portlist'),
|
||||
url(r'^add_portlist/$', views.add_portlist, name='add-portlist'),
|
||||
url(r'^port_config/(?P<pk>[0-9]+)$', views.configure_ports, name='port-config'),
|
||||
url(r'^port_config/(?P<interfaceid>[0-9]+)$', views.configure_ports, name='port-config'),
|
||||
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .acl import *
|
40
preferences/acl.py
Normal file
40
preferences/acl.py
Normal file
|
@ -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."
|
|
@ -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()
|
||||
|
|
43
preferences/migrations/0025_auto_20171231_2142.py
Normal file
43
preferences/migrations/0025_auto_20171231_2142.py
Normal file
|
@ -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'),)},
|
||||
),
|
||||
]
|
16
preferences/migrations/0027_merge_20180106_2019.py
Normal file
16
preferences/migrations/0027_merge_20180106_2019.py
Normal file
|
@ -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 = [
|
||||
]
|
|
@ -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"
|
||||
|
|
|
@ -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 %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -40,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ service.description }}</td>
|
||||
<td>{{ service.image }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -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 %}
|
||||
<h4>Préférences utilisateur</h4>
|
||||
{% if is_bureau %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalUser' %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
{% endif %}
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
|
@ -58,12 +57,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tr>
|
||||
</table>
|
||||
<h4>Préférences machines</h4>
|
||||
{% if is_bureau %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalMachine' %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
{% endif %}
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
|
@ -81,12 +78,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tr>
|
||||
</table>
|
||||
<h4>Préférences topologie</h4>
|
||||
{% if is_bureau %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalTopologie' %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
{% endif %}
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
|
@ -104,12 +99,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tr>
|
||||
</table>
|
||||
<h4>Préférences generales</h4>
|
||||
{% if is_bureau %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'GeneralOption' %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
{% endif %}
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
|
@ -137,12 +130,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
</table>
|
||||
<h4>Données de l'association</h4>
|
||||
{% if is_bureau %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'AssoOption' %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
{% endif %}
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
|
@ -171,12 +162,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tr>
|
||||
</table>
|
||||
<h4>Messages personalisé dans les mails</h4>
|
||||
{% if is_bureau %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'MailMessageOption' %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
{% endif %}
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
|
@ -190,10 +179,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tr>
|
||||
</table>
|
||||
<h2>Liste des services page d'accueil</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-services' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un service</a>
|
||||
{% can_create Service%}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-service' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un service</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'preferences:del-services' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs service</a>
|
||||
{% endif %}
|
||||
{% include "preferences/aff_service.html" with service_list=service_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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<servicesid>[0-9]+)$',
|
||||
views.edit_services,
|
||||
name='edit-services'
|
||||
r'^edit_service/(?P<serviceid>[0-9]+)$',
|
||||
views.edit_service,
|
||||
name='edit-service'
|
||||
),
|
||||
url(r'^del_services/$', views.del_services, name='del-services'),
|
||||
url(
|
||||
r'^history/(?P<object_name>service)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||
re2o.views.history,
|
||||
name='history',
|
||||
kwargs={'application':'preferences'},
|
||||
),
|
||||
url(r'^$', views.display_options, name='display-options'),
|
||||
]
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
235
re2o/acl.py
Normal file
235
re2o/acl.py
Normal file
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
79
re2o/field_permissions.py
Normal file
79
re2o/field_permissions.py
Normal file
|
@ -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
|
||||
|
|
@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% block title %}Accueil{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Bienvenue sur {{ site_name }} !</h1>
|
||||
<h1>Bienvenue sur {{ request.get_host }} !</h1>
|
||||
|
||||
<div class="row">
|
||||
{% for service_list in services_urls %}
|
||||
|
|
410
re2o/templatetags/acl.py
Normal file
410
re2o/templatetags/acl.py
Normal file
|
@ -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**:
|
||||
{% <acl_name> <obj> [arg1 [arg2 [...]]]%}
|
||||
<template stuff>
|
||||
[{% acl_else %}
|
||||
<template stuff>]
|
||||
{% acl_end %}
|
||||
|
||||
where <acl_name> is one of the templatetag names available
|
||||
(can_xxx or cannot_xxx)
|
||||
|
||||
**Example**:
|
||||
{% can_create Machine targeted_user %}
|
||||
<p>I'm authorized to create new machines.models.for this guy \\o/</p>
|
||||
{% acl_else %}
|
||||
<p>Why can't I create a little machine for this guy ? :(</p>
|
||||
{% acl_end %}
|
||||
|
||||
{% can_edit user %}
|
||||
<p>Oh I can edit myself oO</p>
|
||||
{% acl_else %}
|
||||
<p>Sniff can't edit my own infos ...</p>
|
||||
{% 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)
|
|
@ -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
|
||||
|
|
114
re2o/views.py
114
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}
|
||||
)
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
39
search/acl.py
Normal file
39
search/acl.py
Normal file
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{# Load the tag library #}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head prefix="og: http://ogp.me/ns#">
|
||||
|
@ -73,13 +73,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<div class="collapse navbar-collapse" id="myNavbar">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{% url "users:mon-profil" %}">Mon profil</a></li>
|
||||
{% if is_cableur %}
|
||||
{% can_view_app users %}
|
||||
|
||||
<li><a href="{% url "users:index" %}">Adhérents</a></li>
|
||||
{% acl_end %}
|
||||
{% can_view_app machines %}
|
||||
<li><a href="{% url "machines:index" %}">Machines</a></li>
|
||||
{% acl_end %}
|
||||
{% can_view_app cotisations %}
|
||||
<li><a href="{% url "cotisations:index" %}">Cotisations</a></li>
|
||||
{% acl_end %}
|
||||
{% can_view_app topologie %}
|
||||
<li><a href="{% url "topologie:index" %}">Topologie</a></li>
|
||||
{% acl_end %}
|
||||
{% can_view_app logs %}
|
||||
<li><a href="{% url "logs:index" %}">Statistiques</a></li>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</ul>
|
||||
<div class="col-sm-3 col-md-3 navbar-right">
|
||||
<form action="{% url "search:search"%}" class="navbar-form" role="search">
|
||||
|
@ -105,7 +114,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
{% if is_cableur %}
|
||||
{% can_view_app preferences %}
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="{% url 'preferences:display-options' %}">
|
||||
|
@ -113,7 +122,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -205,7 +214,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
<footer class="navbar">
|
||||
<div class="containerfluid text-center">
|
||||
<p>Re2o 2016 - Gabriel Détraz, <a href="https://gitlab.rezometz.org/lhark">Goulven Kermarec</a>, Augustin Lemesle, Maël Kervella</p>
|
||||
<p>Re2o 2016-2018 - Gabriel Détraz, <a href="https://gitlab.rezometz.org/lhark">Goulven Kermarec</a>, Augustin Lemesle, Maël Kervella, Hugo Levy-Falk</p>
|
||||
</div>
|
||||
</footer>
|
||||
{# Read the documentation for more information #}
|
||||
|
|
|
@ -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 *
|
||||
|
|
40
topologie/acl.py
Normal file
40
topologie/acl.py
Normal file
|
@ -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."
|
39
topologie/migrations/0033_auto_20171231_1743.py
Normal file
39
topologie/migrations/0033_auto_20171231_1743.py
Normal file
|
@ -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'),)},
|
||||
),
|
||||
]
|
|
@ -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,6 +145,36 @@ 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,6 +366,42 @@ 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"""
|
||||
|
@ -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
|
||||
|
|
|
@ -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.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'room' room.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit room %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-room' room.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete room %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-room' room.id %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -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.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'constructor_switch' constructor_switch.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit constructor_switch %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-constructor-switch' constructor_switch.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete constructor_switch %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-constructor-switch' constructor_switch.id %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -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.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'model_switch' model_switch.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit model_switch %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-model-switch' model_switch.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete model_switch %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-model-switch' model_switch.id %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -60,14 +62,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'port' port.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit port %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-port' port.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete port %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-port' port.pk %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -46,14 +48,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit stack %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete stack %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
@ -67,14 +71,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit stack %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete stack %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -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.,
|
|||
<td>{{switch.details}}</td>
|
||||
<td class="text-right">
|
||||
{% include 'buttons/history.html' with href='topologie:history' name='switch' id=switch.pk%}
|
||||
{% if is_infra %}
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -39,14 +41,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% if is_infra %}
|
||||
{% can_edit stack %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete stack %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Switchs</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Switch %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-switch' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un switch</a>
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include "topologie/aff_switch.html" with switch_list=switch_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Modèles de switches</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create ModelSwitch %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-model-switch' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un modèle</a>
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include "topologie/aff_model_switch.html" with model_switch_list=model_switch_list %}
|
||||
<h2>Constructeurs de switches</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create ConstructorSwitch %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-constructor-switch' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un constructeur</a>
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include "topologie/aff_constructor_switch.html" with constructor_switch_list=constructor_switch_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Switch {{ nom_switch }}</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:edit-switch' id_switch %}"><i class="glyphicon glyphicon-edit"></i> Editer</a>
|
||||
{% can_create Port %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-port' id_switch %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un port</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:create-ports' id_switch %}"><i class="glyphicon glyphicon-plus"></i> Ajouter des ports</a>
|
||||
{% endif %}
|
||||
{% include "topologie/aff_port.html" with port_list=port_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Chambres</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Room %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-room' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une chambre</a>
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include "topologie/aff_chambres.html" with room_list=room_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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 %}
|
||||
<h2>Stacks</h2>
|
||||
{% if is_infra %}
|
||||
{% can_create Stack %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-stack' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une stack</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include "topologie/aff_stacks.html" with stack_list=stack_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -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<switch_id>[0-9]+)$',
|
||||
views.index_port,
|
||||
name='index-port'),
|
||||
url(r'^history/(?P<object_name>switch)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>port)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>room)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>stack)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>model_switch)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>constructor_switch)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(
|
||||
r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||
re2o.views.history,
|
||||
name='history',
|
||||
kwargs={'application':'topologie'},
|
||||
),
|
||||
url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'),
|
||||
url(r'^new_port/(?P<switch_id>[0-9]+)$', views.new_port, name='new-port'),
|
||||
url(r'^del_port/(?P<port_id>[0-9]+)$', views.del_port, name='del-port'),
|
||||
|
|
|
@ -66,6 +66,13 @@ from topologie.forms import (
|
|||
)
|
||||
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:
|
||||
|
@ -299,21 +241,11 @@ def new_port(request, switch_id):
|
|||
|
||||
|
||||
@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():
|
||||
|
@ -417,14 +335,10 @@ def del_stack(request, stack_id):
|
|||
|
||||
|
||||
@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:
|
||||
|
@ -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():
|
||||
|
@ -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():
|
||||
|
|
|
@ -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 *
|
||||
|
|
40
users/acl.py
Normal file
40
users/acl.py
Normal file
|
@ -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."
|
|
@ -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)
|
||||
|
|
131
users/forms.py
131
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
|
||||
|
|
32
users/management/commands/email.py
Normal file
32
users/management/commands/email.py
Normal file
|
@ -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
|
||||
|
31
users/migrations/0061_auto_20171230_2033.py
Normal file
31
users/migrations/0061_auto_20171230_2033.py
Normal file
|
@ -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'),
|
||||
),
|
||||
]
|
45
users/migrations/0062_auto_20171231_0056.py
Normal file
45
users/migrations/0062_auto_20171231_0056.py
Normal file
|
@ -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),
|
||||
]
|
29
users/migrations/0063_auto_20171231_0140.py
Normal file
29
users/migrations/0063_auto_20171231_0140.py
Normal file
|
@ -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),
|
||||
]
|
41
users/migrations/0064_auto_20171231_0150.py
Normal file
41
users/migrations/0064_auto_20171231_0150.py
Normal file
|
@ -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'),
|
||||
),
|
||||
|
||||
]
|
39
users/migrations/0065_auto_20171231_2053.py
Normal file
39
users/migrations/0065_auto_20171231_2053.py
Normal file
|
@ -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'),)},
|
||||
),
|
||||
]
|
254
users/migrations/0066_grouppermissions.py
Normal file
254
users/migrations/0066_grouppermissions.py
Normal file
|
@ -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),
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue