8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-26 14:42:25 +00:00

Merge branch 'acl' into 'master'

Acl

See merge request federez/re2o!55
This commit is contained in:
Gabriel Detraz 2018-01-07 02:38:41 +01:00
commit 0423442556
117 changed files with 5513 additions and 1919 deletions

View file

@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .acl import *

40
cotisations/acl.py Normal file
View 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."

View file

@ -43,6 +43,8 @@ from django.forms import ModelForm, Form
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from .models import Article, Paiement, Facture, Banque from .models import Article, Paiement, Facture, Banque
from re2o.field_permissions import FieldPermissionFormMixin
class NewFactureForm(ModelForm): class NewFactureForm(ModelForm):
"""Creation d'une facture, moyen de paiement, banque et numero """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""" """Edition d'une facture : moyen de paiement, banque, user parent"""
class Meta(NewFactureForm.Meta): class Meta(NewFactureForm.Meta):
fields = ['paiement', 'banque', 'cheque', 'user'] model = Facture
fields = '__all__'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(EditFactureForm, self).__init__(*args, **kwargs) super(EditFactureForm, self).__init__(*args, **kwargs)
self.fields['user'].label = 'Adherent' self.fields['user'].label = 'Adherent'
self.fields['user'].empty_label = "Séléctionner\ self.fields['user'].empty_label = "Séléctionner\
l'adhérent propriétaire" 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['valid'].label = 'Validité de la facture'
self.fields['control'].label = 'Contrôle de la facture'
class ArticleForm(ModelForm): class ArticleForm(ModelForm):
@ -180,11 +173,19 @@ class DelArticleForm(Form):
"""Suppression d'un ou plusieurs articles en vente. Choix """Suppression d'un ou plusieurs articles en vente. Choix
parmis les modèles""" parmis les modèles"""
articles = forms.ModelMultipleChoiceField( articles = forms.ModelMultipleChoiceField(
queryset=Article.objects.all(), queryset=Article.objects.none(),
label="Articles actuels", label="Articles actuels",
widget=forms.CheckboxSelectMultiple 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): class PaiementForm(ModelForm):
"""Creation d'un moyen de paiement, champ text moyen et type """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 """Suppression d'un ou plusieurs moyens de paiements, selection
parmis les models""" parmis les models"""
paiements = forms.ModelMultipleChoiceField( paiements = forms.ModelMultipleChoiceField(
queryset=Paiement.objects.all(), queryset=Paiement.objects.none(),
label="Moyens de paiement actuels", label="Moyens de paiement actuels",
widget=forms.CheckboxSelectMultiple 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): class BanqueForm(ModelForm):
"""Creation d'une banque, field name""" """Creation d'une banque, field name"""
@ -225,7 +234,15 @@ class BanqueForm(ModelForm):
class DelBanqueForm(Form): class DelBanqueForm(Form):
"""Selection d'une ou plusieurs banques, pour suppression""" """Selection d'une ou plusieurs banques, pour suppression"""
banques = forms.ModelMultipleChoiceField( banques = forms.ModelMultipleChoiceField(
queryset=Banque.objects.all(), queryset=Banque.objects.none(),
label="Banques actuelles", label="Banques actuelles",
widget=forms.CheckboxSelectMultiple 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()

View 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'))},
),
]

View file

@ -56,8 +56,10 @@ from django.db.models import Max
from django.utils import timezone from django.utils import timezone
from machines.models import regen 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 """ 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 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 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) valid = models.BooleanField(default=True)
control = models.BooleanField(default=False) 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): def prix(self):
"""Renvoie le prix brut sans les quantités. Méthode """Renvoie le prix brut sans les quantités. Méthode
dépréciée""" dépréciée"""
@ -103,6 +114,59 @@ class Facture(models.Model):
).values_list('name', flat=True)) ).values_list('name', flat=True))
return name 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): def __str__(self):
return str(self.user) + ' ' + str(self.date) return str(self.user) + ' ' + str(self.date)
@ -149,6 +213,12 @@ class Vente(models.Model):
max_length=255 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): def prix_total(self):
"""Renvoie le prix_total de self (nombre*prix)""" """Renvoie le prix_total de self (nombre*prix)"""
return self.prix*self.number return self.prix*self.number
@ -201,6 +271,46 @@ class Vente(models.Model):
self.update_cotisation() self.update_cotisation()
super(Vente, self).save(*args, **kwargs) 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): def __str__(self):
return str(self.name) + ' ' + str(self.facture) return str(self.name) + ' ' + str(self.facture)
@ -269,6 +379,11 @@ class Article(models.Model):
unique_together = ('name', 'type_user') unique_together = ('name', 'type_user')
class Meta:
permissions = (
("view_article", "Peut voir un objet article"),
)
def clean(self): def clean(self):
if self.name.lower() == "solde": if self.name.lower() == "solde":
raise ValidationError("Solde est un nom d'article invalide") 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" "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): def __str__(self):
return self.name return self.name
@ -287,6 +425,34 @@ class Banque(models.Model):
name = models.CharField(max_length=255) 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): def __str__(self):
return self.name return self.name
@ -302,6 +468,34 @@ class Paiement(models.Model):
moyen = models.CharField(max_length=255) moyen = models.CharField(max_length=255)
type_paiement = models.IntegerField(choices=PAYMENT_TYPES, default=0) 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): def __str__(self):
return self.moyen return self.moyen
@ -334,6 +528,52 @@ class Cotisation(models.Model):
date_start = models.DateTimeField() date_start = models.DateTimeField()
date_end = 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): def __str__(self):
return str(self.vente) return str(self.vente)

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -41,11 +43,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ article.duration }}</td> <td>{{ article.duration }}</td>
<td>{{ article.type_user }}</td> <td>{{ article.type_user }}</td>
<td class="text-right"> <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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-article' article.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% endif %} {% acl_end %}
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'article' article.id %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'article' article.id %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </a>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -33,11 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
<td>{{ banque.name }}</td> <td>{{ banque.name }}</td>
<td class="text-right"> <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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-banque' banque.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% endif %} {% acl_end %}
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'banque' banque.id %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'banque' banque.id %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </a>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% if facture_list.paginator %} {% if facture_list.paginator %}
{% include "pagination.html" with list=facture_list %} {% include "pagination.html" with list=facture_list %}
{% endif %} {% endif %}
@ -47,7 +49,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ facture.paiement }}</td> <td>{{ facture.paiement }}</td>
<td>{{ facture.date }}</td> <td>{{ facture.date }}</td>
<td>{{ facture.id }}</td> <td>{{ facture.id }}</td>
{% if is_cableur %}
<td> <td>
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="editionfacture" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <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> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu" aria-labelledby="editionfacture"> <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: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> {% acl_else %}
<li><a href="{% url 'cotisations:history' 'facture' facture.id %}"><i class="glyphicon glyphicon-time"></i> Historique</a></li>
{% else %}
<li>Facture controlée</li> <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> </ul>
</div> </div>
</td> </td>
{% endif %}
<td> <td>
{% if facture.valid %} {% if facture.valid %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:facture-pdf' facture.id %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:facture-pdf' facture.id %}">

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -33,11 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
<td>{{ paiement.moyen }}</td> <td>{{ paiement.moyen }}</td>
<td class="text-right"> <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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-paiement' paiement.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% endif %} {% acl_end %}
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'paiement' paiement.id %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'paiement' paiement.id %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </a>

View file

@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Articles{% endblock %} {% block title %}Articles{% endblock %}
{% block content %} {% block content %}
<h2>Liste des types d'articles</h2> <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> <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> <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 %} {% include "cotisations/aff_article.html" with article_list=article_list %}
<br /> <br />
<br /> <br />

View file

@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Banques{% endblock %} {% block title %}Banques{% endblock %}
{% block content %} {% block content %}
<h2>Liste des banques</h2> <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> <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> <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 %} {% include "cotisations/aff_banque.html" with banque_list=banque_list %}
<br /> <br />
<br /> <br />

View file

@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Paiements{% endblock %} {% block title %}Paiements{% endblock %}
{% block content %} {% block content %}
<h2>Liste des types de paiements</h2> <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> <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> <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 %} {% include "cotisations/aff_paiement.html" with paiement_list=paiement_list %}
<br /> <br />
<br /> <br />

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% block sidebar %} {% block sidebar %}
{% if is_trez %} {% can_change Facture pdf %}
<a class="list-group-item list-group-item-success" href="{% url "cotisations:new-facture-pdf" %}"> <a class="list-group-item list-group-item-success" href="{% url "cotisations:new-facture-pdf" %}">
<i class="glyphicon glyphicon-plus"></i> <i class="glyphicon glyphicon-plus"></i>
Créer une facture 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> <i class="glyphicon glyphicon-eye-open"></i>
Contrôler les factures Contrôler les factures
</a> </a>
{% endif %} {% acl_end %}
{% can_view_all Facture %}
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index" %}"> <a class="list-group-item list-group-item-info" href="{% url "cotisations:index" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Factures Factures
</a> </a>
{% acl_end %}
{% can_view_all Article %}
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-article" %}"> <a class="list-group-item list-group-item-info" href="{% url "cotisations:index-article" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Articles en vente Articles en vente
</a> </a>
{% acl_end %}
{% can_view_all Banque %}
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-banque" %}"> <a class="list-group-item list-group-item-info" href="{% url "cotisations:index-banque" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Banques Banques
</a> </a>
{% acl_end %}
{% can_view_all Paiement %}
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-paiement" %}"> <a class="list-group-item list-group-item-info" href="{% url "cotisations:index-paiement" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Moyens de paiement Moyens de paiement
</a> </a>
{% acl_end %}
{% endblock %} {% endblock %}

View file

@ -24,6 +24,7 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
import re2o
from . import views from . import views
urlpatterns = [ urlpatterns = [
@ -99,21 +100,12 @@ urlpatterns = [
views.index_paiement, views.index_paiement,
name='index-paiement' name='index-paiement'
), ),
url(r'^history/(?P<object_name>facture)/(?P<object_id>[0-9]+)$', url(
views.history, r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
name='history' re2o.views.history,
), name='history',
url(r'^history/(?P<object_name>article)/(?P<object_id>[0-9]+)$', kwargs={'application':'cotisations'},
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'^control/$', url(r'^control/$',
views.control, views.control,
name='control' name='control'

View file

@ -44,11 +44,19 @@ from re2o.settings import LOGO_PATH
from re2o import settings from re2o import settings
from re2o.views import form from re2o.views import form
from re2o.utils import SortTable 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 preferences.models import OptionalUser, AssoOption, GeneralOption
from .models import Facture, Article, Vente, Paiement, Banque from .models import Facture, Article, Vente, Paiement, Banque
from .forms import ( from .forms import (
NewFactureForm, NewFactureForm,
TrezEditFactureForm,
EditFactureForm, EditFactureForm,
ArticleForm, ArticleForm,
DelArticleForm, DelArticleForm,
@ -64,9 +72,11 @@ from .forms import (
from .tex import render_invoice from .tex import render_invoice
@login_required @login_required
@permission_required('cableur') @can_create(Facture)
def new_facture(request, userid): @can_edit(User)
def new_facture(request, user, userid):
"""Creation d'une facture pour un user. Renvoie la liste des articles """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 et crée des factures dans un formset. Utilise un peu de js coté template
pour ajouter des articles. pour ajouter des articles.
@ -74,11 +84,6 @@ def new_facture(request, userid):
enfin sauve la facture parente. enfin sauve la facture parente.
TODO : simplifier cette fonction, déplacer l'intelligence coté models TODO : simplifier cette fonction, déplacer l'intelligence coté models
Facture et Vente.""" 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) facture = Facture(user=user)
# Le template a besoin de connaitre les articles pour le js # Le template a besoin de connaitre les articles pour le js
article_list = Article.objects.filter( article_list = Article.objects.filter(
@ -163,7 +168,7 @@ def new_facture(request, userid):
@login_required @login_required
@permission_required('tresorier') @can_change(Facture, 'pdf')
def new_facture_pdf(request): def new_facture_pdf(request):
"""Permet de générer un pdf d'une facture. Réservée """Permet de générer un pdf d'une facture. Réservée
au trésorier, permet d'emettre des factures sans objet au trésorier, permet d'emettre des factures sans objet
@ -203,31 +208,13 @@ def new_facture_pdf(request):
@login_required @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, """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 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, de l'adhérent, etc. Réservée à self pour un user sans droits,
les droits cableurs permettent d'afficher toute facture""" 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_objects = Vente.objects.all().filter(facture=facture)
ventes = [] ventes = []
options, _created = AssoOption.objects.get_or_create() options, _created = AssoOption.objects.get_or_create()
@ -251,27 +238,12 @@ def facture_pdf(request, factureid):
@login_required @login_required
@permission_required('cableur') @can_edit(Facture)
def edit_facture(request, factureid): def edit_facture(request, facture, factureid):
"""Permet l'édition d'une facture. On peut y éditer les ventes """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 déjà effectuer, ou rendre une facture invalide (non payées, chèque
en bois etc). Mets à jour les durée de cotisation attenantes""" en bois etc). Mets à jour les durée de cotisation attenantes"""
try: facture_form = EditFactureForm(request.POST or None, instance=facture, user=request.user)
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)
ventes_objects = Vente.objects.filter(facture=facture) ventes_objects = Vente.objects.filter(facture=facture)
vente_form_set = modelformset_factory( vente_form_set = modelformset_factory(
Vente, Vente,
@ -297,19 +269,10 @@ def edit_facture(request, factureid):
@login_required @login_required
@permission_required('cableur') @can_delete(Facture)
def del_facture(request, factureid): def del_facture(request, facture, factureid):
"""Suppression d'une facture. Supprime en cascade les ventes """Suppression d'une facture. Supprime en cascade les ventes
et cotisations filles""" 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": if request.method == "POST":
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
facture.delete() facture.delete()
@ -323,14 +286,10 @@ def del_facture(request, factureid):
@login_required @login_required
@permission_required('cableur') @can_create(Facture)
def credit_solde(request, userid): @can_edit(User)
def credit_solde(request, user, userid):
""" Credit ou débit de solde """ """ 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) facture = CreditSoldeForm(request.POST or None)
if facture.is_valid(): if facture.is_valid():
facture_instance = facture.save(commit=False) facture_instance = facture.save(commit=False)
@ -355,7 +314,7 @@ def credit_solde(request, userid):
@login_required @login_required
@permission_required('tresorier') @can_create(Article)
def add_article(request): def add_article(request):
"""Ajoute un article. Champs : désignation, """Ajoute un article. Champs : désignation,
prix, est-ce une cotisation et si oui sa durée prix, est-ce une cotisation et si oui sa durée
@ -376,15 +335,10 @@ def add_article(request):
@login_required @login_required
@permission_required('tresorier') @can_edit(Article)
def edit_article(request, articleid): def edit_article(request, article_instance, articleid):
"""Edition d'un article (designation, prix, etc) """Edition d'un article (designation, prix, etc)
Réservé au trésorier""" 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) article = ArticleForm(request.POST or None, instance=article_instance)
if article.is_valid(): if article.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -401,10 +355,10 @@ def edit_article(request, articleid):
@login_required @login_required
@permission_required('tresorier') @can_delete_set(Article)
def del_article(request): def del_article(request, instances):
"""Suppression d'un article en vente""" """Suppression d'un article en vente"""
article = DelArticleForm(request.POST or None) article = DelArticleForm(request.POST or None, instances=instances)
if article.is_valid(): if article.is_valid():
article_del = article.cleaned_data['articles'] article_del = article.cleaned_data['articles']
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -416,7 +370,7 @@ def del_article(request):
@login_required @login_required
@permission_required('tresorier') @can_create(Paiement)
def add_paiement(request): def add_paiement(request):
"""Ajoute un moyen de paiement. Relié aux factures """Ajoute un moyen de paiement. Relié aux factures
via foreign key""" via foreign key"""
@ -432,14 +386,9 @@ def add_paiement(request):
@login_required @login_required
@permission_required('tresorier') @can_edit(Paiement)
def edit_paiement(request, paiementid): def edit_paiement(request, paiement_instance, paiementid):
"""Edition d'un moyen de paiement""" """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) paiement = PaiementForm(request.POST or None, instance=paiement_instance)
if paiement.is_valid(): if paiement.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -456,10 +405,10 @@ def edit_paiement(request, paiementid):
@login_required @login_required
@permission_required('tresorier') @can_delete_set(Paiement)
def del_paiement(request): def del_paiement(request, instances):
"""Suppression d'un moyen de paiement""" """Suppression d'un moyen de paiement"""
paiement = DelPaiementForm(request.POST or None) paiement = DelPaiementForm(request.POST or None, instances=instances)
if paiement.is_valid(): if paiement.is_valid():
paiement_dels = paiement.cleaned_data['paiements'] paiement_dels = paiement.cleaned_data['paiements']
for paiement_del in paiement_dels: for paiement_del in paiement_dels:
@ -483,7 +432,7 @@ def del_paiement(request):
@login_required @login_required
@permission_required('cableur') @can_create(Banque)
def add_banque(request): def add_banque(request):
"""Ajoute une banque à la liste des banques""" """Ajoute une banque à la liste des banques"""
banque = BanqueForm(request.POST or None) banque = BanqueForm(request.POST or None)
@ -498,14 +447,9 @@ def add_banque(request):
@login_required @login_required
@permission_required('tresorier') @can_edit(Banque)
def edit_banque(request, banqueid): def edit_banque(request, banque_instance, banqueid):
"""Edite le nom d'une banque""" """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) banque = BanqueForm(request.POST or None, instance=banque_instance)
if banque.is_valid(): if banque.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -522,10 +466,10 @@ def edit_banque(request, banqueid):
@login_required @login_required
@permission_required('tresorier') @can_delete_set(Banque)
def del_banque(request): def del_banque(request, instances):
"""Supprime une banque""" """Supprime une banque"""
banque = DelBanqueForm(request.POST or None) banque = DelBanqueForm(request.POST or None, instances=instances)
if banque.is_valid(): if banque.is_valid():
banque_dels = banque.cleaned_data['banques'] banque_dels = banque.cleaned_data['banques']
for banque_del in banque_dels: for banque_del in banque_dels:
@ -543,7 +487,8 @@ def del_banque(request):
@login_required @login_required
@permission_required('tresorier') @can_view_all(Facture)
@can_change(Facture, 'control')
def control(request): def control(request):
"""Pour le trésorier, vue pour controler en masse les """Pour le trésorier, vue pour controler en masse les
factures.Case à cocher, pratique""" factures.Case à cocher, pratique"""
@ -583,7 +528,7 @@ def control(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(Article)
def index_article(request): def index_article(request):
"""Affiche l'ensemble des articles en vente""" """Affiche l'ensemble des articles en vente"""
article_list = Article.objects.order_by('name') article_list = Article.objects.order_by('name')
@ -593,7 +538,7 @@ def index_article(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(Paiement)
def index_paiement(request): def index_paiement(request):
"""Affiche l'ensemble des moyens de paiement en vente""" """Affiche l'ensemble des moyens de paiement en vente"""
paiement_list = Paiement.objects.order_by('moyen') paiement_list = Paiement.objects.order_by('moyen')
@ -603,7 +548,7 @@ def index_paiement(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(Banque)
def index_banque(request): def index_banque(request):
"""Affiche l'ensemble des banques""" """Affiche l'ensemble des banques"""
banque_list = Banque.objects.order_by('name') banque_list = Banque.objects.order_by('name')
@ -613,7 +558,7 @@ def index_banque(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(Facture)
def index(request): def index(request):
"""Affiche l'ensemble des factures, pour les cableurs et +""" """Affiche l'ensemble des factures, pour les cableurs et +"""
options, _created = GeneralOption.objects.get_or_create() options, _created = GeneralOption.objects.get_or_create()
@ -639,60 +584,3 @@ def index(request):
return render(request, 'cotisations/index.html', { return render(request, 'cotisations/index.html', {
'facture_list': facture_list '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
})

View file

@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .acl import *

40
logs/acl.py Normal file
View 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."

View file

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %} {% endif %}
{% load logs_extra %} {% load logs_extra %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
@ -47,19 +48,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ revision.user }}</td> <td>{{ revision.user }}</td>
<td>{{ revision.date_created }}</td> <td>{{ revision.date_created }}</td>
<td>{{ revision.comment }}</td> <td>{{ revision.comment }}</td>
{% if is_bureau %} {% can_edit_history %}
<td> <td>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
Annuler Annuler
</a> </a>
</td> </td>
{% endif %} {% acl_end %}
</tr> </tr>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
</table> </table>
{% if revisions_list.paginator %} {% if revisions_list.paginator %}
{% include "pagination.html" with list=revisions_list %} {% include "pagination.html" with list=revisions_list %}
{% endif %} {% endif %}

View file

@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %} {% endif %}
{% load logs_extra %} {% load logs_extra %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -51,14 +51,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %} {% endif %}
</i>) </i>)
</td> </td>
{% if is_bureau %} {% can_edit_history %}
<td> <td>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
Annuler Annuler
</a> </a>
</td> </td>
{% endif %} {% acl_end %}
</tr> </tr>
{% elif v.version.content_type.model == 'whitelist' %} {% elif v.version.content_type.model == 'whitelist' %}
<tr class="success"> <tr class="success">
@ -74,14 +74,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %} {% endif %}
</i>) </i>)
</td> </td>
{% if is_bureau %} {% can_edit_history%}
<td> <td>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
Annuler Annuler
</a> </a>
</td> </td>
{% endif %} {% acl_end %}
</tr> </tr>
{% elif v.version.content_type.model == 'user' %} {% elif v.version.content_type.model == 'user' %}
<tr> <tr>
@ -93,14 +93,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
(<i>{{ v.comment }}</i>) (<i>{{ v.comment }}</i>)
{% endif %} {% endif %}
</td> </td>
{% if is_bureau %} {% can_edit_history %}
<td> <td>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
Annuler Annuler
</a> </a>
</td> </td>
{% endif %} {% acl_end %}
</tr> </tr>
{% elif v.version.content_type.model == 'vente' %} {% elif v.version.content_type.model == 'vente' %}
<tr> <tr>
@ -112,14 +112,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
(<i>+{{ v.version.object.duration }} mois</i>) (<i>+{{ v.version.object.duration }} mois</i>)
{% endif %} {% endif %}
</td> </td>
{% if is_bureau %} {% can_edit_history %}
<td> <td>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
Annuler Annuler
</a> </a>
</td> </td>
{% endif %} {% acl_end %}
</tr> </tr>
{% elif v.version.content_type.model == 'interface' %} {% elif v.version.content_type.model == 'interface' %}
<tr> <tr>
@ -131,14 +131,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
(<i>{{ v.comment }}</i>) (<i>{{ v.comment }}</i>)
{% endif %} {% endif %}
</td> </td>
{% if is_bureau %} {% can_edit_history %}
<td> <td>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
Annuler Annuler
</a> </a>
</td> </td>
{% endif %} {% acl_end %}
</tr> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% block sidebar %} {% block sidebar %}
{% if is_cableur %} {% can_view_app logs %}
<a class="list-group-item list-group-item-info" href="{% url "logs:index" %}"> <a class="list-group-item list-group-item-info" href="{% url "logs:index" %}">
<i class="glyphicon glyphicon-stats"></i> <i class="glyphicon glyphicon-stats"></i>
Résumé Résumé
@ -50,5 +51,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i class="glyphicon glyphicon-stats"></i> <i class="glyphicon glyphicon-stats"></i>
Utilisateurs Utilisateurs
</a> </a>
{% endif %} {% acl_end %}
{% endblock %} {% endblock %}

View file

@ -41,7 +41,7 @@ from django.urls import reverse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib import messages 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 django.db.models import Count
from reversion.models import Revision from reversion.models import Revision
@ -50,7 +50,6 @@ from reversion.models import Version, ContentType
from users.models import ( from users.models import (
User, User,
ServiceUser, ServiceUser,
Right,
School, School,
ListRight, ListRight,
ListShell, ListShell,
@ -93,7 +92,17 @@ from topologie.models import (
) )
from preferences.models import GeneralOption from preferences.models import GeneralOption
from re2o.views import form 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_assigned_interfaces_count
from re2o.utils import all_active_interfaces_count, SortTable from re2o.utils import all_active_interfaces_count, SortTable
@ -108,7 +117,7 @@ STATS_DICT = {
@login_required @login_required
@permission_required('cableur') @can_view_app('logs')
def index(request): def index(request):
"""Affiche les logs affinés, date reformatées, selectionne """Affiche les logs affinés, date reformatées, selectionne
les event importants (ajout de droits, ajout de ban/whitelist)""" les event importants (ajout de droits, ajout de ban/whitelist)"""
@ -167,7 +176,7 @@ def index(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(GeneralOption)
def stats_logs(request): def stats_logs(request):
"""Affiche l'ensemble des logs et des modifications sur les objets, """Affiche l'ensemble des logs et des modifications sur les objets,
classés par date croissante, en vrac""" classés par date croissante, en vrac"""
@ -197,7 +206,7 @@ def stats_logs(request):
@login_required @login_required
@permission_required('bureau') @can_edit_history
def revert_action(request, revision_id): def revert_action(request, revision_id):
""" Annule l'action en question """ """ Annule l'action en question """
try: try:
@ -215,7 +224,9 @@ def revert_action(request, revision_id):
@login_required @login_required
@permission_required('cableur') @can_view_all(IpList)
@can_view_all(Interface)
@can_view_all(User)
def stats_general(request): def stats_general(request):
"""Statistiques générales affinées sur les ip, activées, utilisées par """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, range, et les statistiques générales sur les users : users actifs,
@ -298,7 +309,10 @@ def stats_general(request):
@login_required @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): def stats_models(request):
"""Statistiques générales, affiche les comptages par models: """Statistiques générales, affiche les comptages par models:
nombre d'users, d'écoles, de droits, de bannissements, nombre d'users, d'écoles, de droits, de bannissements,
@ -310,7 +324,6 @@ def stats_models(request):
'clubs': [Club.PRETTY_NAME, Club.objects.count()], 'clubs': [Club.PRETTY_NAME, Club.objects.count()],
'serviceuser': [ServiceUser.PRETTY_NAME, 'serviceuser': [ServiceUser.PRETTY_NAME,
ServiceUser.objects.count()], ServiceUser.objects.count()],
'right': [Right.PRETTY_NAME, Right.objects.count()],
'school': [School.PRETTY_NAME, School.objects.count()], 'school': [School.PRETTY_NAME, School.objects.count()],
'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()], 'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()],
'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()], 'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()],
@ -340,7 +353,7 @@ def stats_models(request):
OuverturePortList.objects.count() OuverturePortList.objects.count()
], ],
'vlan': [Vlan.PRETTY_NAME, Vlan.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()], 'Mx': [Mx.PRETTY_NAME, Mx.objects.count()],
'Ns': [Ns.PRETTY_NAME, Ns.objects.count()], 'Ns': [Ns.PRETTY_NAME, Ns.objects.count()],
'nas': [Nas.PRETTY_NAME, Nas.objects.count()], 'nas': [Nas.PRETTY_NAME, Nas.objects.count()],
@ -368,7 +381,7 @@ def stats_models(request):
@login_required @login_required
@permission_required('cableur') @can_view_app('users')
def stats_users(request): def stats_users(request):
"""Affiche les statistiques base de données aggrégées par user : """Affiche les statistiques base de données aggrégées par user :
nombre de machines par user, d'etablissements par user, nombre de machines par user, d'etablissements par user,
@ -395,7 +408,7 @@ def stats_users(request):
num=Count('whitelist') num=Count('whitelist')
).order_by('-num')[:10], ).order_by('-num')[:10],
'Droits': User.objects.annotate( 'Droits': User.objects.annotate(
num=Count('right') num=Count('groups')
).order_by('-num')[:10], ).order_by('-num')[:10],
}, },
'Etablissement': { 'Etablissement': {
@ -422,7 +435,7 @@ def stats_users(request):
@login_required @login_required
@permission_required('cableur') @can_view_app('users')
def stats_actions(request): def stats_actions(request):
"""Vue qui affiche les statistiques de modifications d'objets par """Vue qui affiche les statistiques de modifications d'objets par
utilisateurs. utilisateurs.

View file

@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .acl import *

40
machines/acl.py Normal file
View 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."

View file

@ -38,6 +38,8 @@ from __future__ import unicode_literals
from django.forms import ModelForm, Form from django.forms import ModelForm, Form
from django import forms from django import forms
from re2o.field_permissions import FieldPermissionFormMixin
from .models import ( from .models import (
Domain, Domain,
Machine, Machine,
@ -58,7 +60,7 @@ from .models import (
) )
class EditMachineForm(ModelForm): class EditMachineForm(FieldPermissionFormMixin, ModelForm):
"""Formulaire d'édition d'une machine""" """Formulaire d'édition d'une machine"""
class Meta: class Meta:
model = Machine model = Machine
@ -117,10 +119,10 @@ class AddInterfaceForm(EditInterfaceForm):
fields = ['type', 'ipv4', 'mac_address', 'details'] fields = ['type', 'ipv4', 'mac_address', 'details']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
infra = kwargs.pop('infra') user = kwargs.pop('user')
super(AddInterfaceForm, self).__init__(*args, **kwargs) super(AddInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4" 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( self.fields['type'].queryset = MachineType.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False) ip_type__in=IpType.objects.filter(need_infra=False)
) )
@ -146,13 +148,14 @@ class BaseEditInterfaceForm(EditInterfaceForm):
fields = ['type', 'ipv4', 'mac_address', 'details'] fields = ['type', 'ipv4', 'mac_address', 'details']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
infra = kwargs.pop('infra') user = kwargs.pop('user')
super(BaseEditInterfaceForm, self).__init__(*args, **kwargs) super(BaseEditInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4" 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( self.fields['type'].queryset = MachineType.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False) ip_type__in=IpType.objects.filter(need_infra=False)
) )
if not IpType.can_use_all(user):
self.fields['ipv4'].queryset = IpList.objects.filter( self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True interface__isnull=True
).filter(ip_type__in=IpType.objects.filter(need_infra=False)) ).filter(ip_type__in=IpType.objects.filter(need_infra=False))
@ -177,9 +180,10 @@ class AliasForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
infra = kwargs.pop('infra') user = kwargs.pop('user')
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs) 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( self.fields['extension'].queryset = Extension.objects.filter(
need_infra=False need_infra=False
) )
@ -233,11 +237,19 @@ class MachineTypeForm(ModelForm):
class DelMachineTypeForm(Form): class DelMachineTypeForm(Form):
"""Suppression d'un ou plusieurs machinetype""" """Suppression d'un ou plusieurs machinetype"""
machinetypes = forms.ModelMultipleChoiceField( machinetypes = forms.ModelMultipleChoiceField(
queryset=MachineType.objects.all(), queryset=MachineType.objects.none(),
label="Types de machines actuelles", label="Types de machines actuelles",
widget=forms.CheckboxSelectMultiple 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): class IpTypeForm(ModelForm):
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de """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): class DelIpTypeForm(Form):
"""Suppression d'un ou plusieurs iptype""" """Suppression d'un ou plusieurs iptype"""
iptypes = forms.ModelMultipleChoiceField( iptypes = forms.ModelMultipleChoiceField(
queryset=IpType.objects.all(), queryset=IpType.objects.none(),
label="Types d'ip actuelles", label="Types d'ip actuelles",
widget=forms.CheckboxSelectMultiple 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): class ExtensionForm(ModelForm):
"""Formulaire d'ajout et edition d'une extension""" """Formulaire d'ajout et edition d'une extension"""
@ -288,11 +308,19 @@ class ExtensionForm(ModelForm):
class DelExtensionForm(Form): class DelExtensionForm(Form):
"""Suppression d'une ou plusieurs extensions""" """Suppression d'une ou plusieurs extensions"""
extensions = forms.ModelMultipleChoiceField( extensions = forms.ModelMultipleChoiceField(
queryset=Extension.objects.all(), queryset=Extension.objects.none(),
label="Extensions actuelles", label="Extensions actuelles",
widget=forms.CheckboxSelectMultiple 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): class SOAForm(ModelForm):
"""Ajout et edition d'un SOA""" """Ajout et edition d'un SOA"""
@ -308,11 +336,19 @@ class SOAForm(ModelForm):
class DelSOAForm(Form): class DelSOAForm(Form):
"""Suppression d'un ou plusieurs SOA""" """Suppression d'un ou plusieurs SOA"""
soa = forms.ModelMultipleChoiceField( soa = forms.ModelMultipleChoiceField(
queryset=SOA.objects.all(), queryset=SOA.objects.none(),
label="SOA actuels", label="SOA actuels",
widget=forms.CheckboxSelectMultiple 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): class MxForm(ModelForm):
"""Ajout et edition d'un MX""" """Ajout et edition d'un MX"""
@ -327,15 +363,22 @@ class MxForm(ModelForm):
interface_parent=None interface_parent=None
).select_related('extension') ).select_related('extension')
class DelMxForm(Form): class DelMxForm(Form):
"""Suppression d'un ou plusieurs MX""" """Suppression d'un ou plusieurs MX"""
mx = forms.ModelMultipleChoiceField( mx = forms.ModelMultipleChoiceField(
queryset=Mx.objects.all(), queryset=Mx.objects.none(),
label="MX actuels", label="MX actuels",
widget=forms.CheckboxSelectMultiple 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): class NsForm(ModelForm):
"""Ajout d'un NS pour une zone """Ajout d'un NS pour une zone
@ -356,11 +399,19 @@ class NsForm(ModelForm):
class DelNsForm(Form): class DelNsForm(Form):
"""Suppresion d'un ou plusieurs NS""" """Suppresion d'un ou plusieurs NS"""
ns = forms.ModelMultipleChoiceField( ns = forms.ModelMultipleChoiceField(
queryset=Ns.objects.all(), queryset=Ns.objects.none(),
label="Enregistrements NS actuels", label="Enregistrements NS actuels",
widget=forms.CheckboxSelectMultiple 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): class TxtForm(ModelForm):
"""Ajout d'un txt pour une zone""" """Ajout d'un txt pour une zone"""
@ -376,12 +427,20 @@ class TxtForm(ModelForm):
class DelTxtForm(Form): class DelTxtForm(Form):
"""Suppression d'un ou plusieurs TXT""" """Suppression d'un ou plusieurs TXT"""
txt = forms.ModelMultipleChoiceField( txt = forms.ModelMultipleChoiceField(
queryset=Txt.objects.all(), queryset=Txt.objects.none(),
label="Enregistrements Txt actuels", label="Enregistrements Txt actuels",
widget=forms.CheckboxSelectMultiple 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): class SrvForm(ModelForm):
"""Ajout d'un srv pour une zone""" """Ajout d'un srv pour une zone"""
class Meta: class Meta:
@ -396,11 +455,19 @@ class SrvForm(ModelForm):
class DelSrvForm(Form): class DelSrvForm(Form):
"""Suppression d'un ou plusieurs Srv""" """Suppression d'un ou plusieurs Srv"""
srv = forms.ModelMultipleChoiceField( srv = forms.ModelMultipleChoiceField(
queryset=Srv.objects.all(), queryset=Srv.objects.none(),
label="Enregistrements Srv actuels", label="Enregistrements Srv actuels",
widget=forms.CheckboxSelectMultiple 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): class NasForm(ModelForm):
"""Ajout d'un type de nas (machine d'authentification, """Ajout d'un type de nas (machine d'authentification,
@ -417,11 +484,19 @@ class NasForm(ModelForm):
class DelNasForm(Form): class DelNasForm(Form):
"""Suppression d'un ou plusieurs nas""" """Suppression d'un ou plusieurs nas"""
nas = forms.ModelMultipleChoiceField( nas = forms.ModelMultipleChoiceField(
queryset=Nas.objects.all(), queryset=Nas.objects.none(),
label="Enregistrements Nas actuels", label="Enregistrements Nas actuels",
widget=forms.CheckboxSelectMultiple 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): class ServiceForm(ModelForm):
"""Ajout et edition d'une classe de service : dns, dhcp, etc""" """Ajout et edition d'une classe de service : dns, dhcp, etc"""
@ -446,11 +521,19 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form): class DelServiceForm(Form):
"""Suppression d'un ou plusieurs service""" """Suppression d'un ou plusieurs service"""
service = forms.ModelMultipleChoiceField( service = forms.ModelMultipleChoiceField(
queryset=Service.objects.all(), queryset=Service.objects.none(),
label="Services actuels", label="Services actuels",
widget=forms.CheckboxSelectMultiple 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): class VlanForm(ModelForm):
"""Ajout d'un vlan : id, nom""" """Ajout d'un vlan : id, nom"""
@ -466,11 +549,19 @@ class VlanForm(ModelForm):
class DelVlanForm(Form): class DelVlanForm(Form):
"""Suppression d'un ou plusieurs vlans""" """Suppression d'un ou plusieurs vlans"""
vlan = forms.ModelMultipleChoiceField( vlan = forms.ModelMultipleChoiceField(
queryset=Vlan.objects.all(), queryset=Vlan.objects.none(),
label="Vlan actuels", label="Vlan actuels",
widget=forms.CheckboxSelectMultiple 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): class EditOuverturePortConfigForm(ModelForm):
"""Edition de la liste des profils d'ouverture de ports """Edition de la liste des profils d'ouverture de ports

View 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'),)},
),
]

View 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'),)},
),
]

File diff suppressed because it is too large Load diff

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -33,7 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
<td>{{ alias }}</td> <td>{{ alias }}</td>
<td class="text-right"> <td class="text-right">
{% can_edit alias %}
{% include 'buttons/edit.html' with href='machines:edit-alias' id=alias.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='alias' id=alias.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -45,9 +47,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ extension.origin_v6 }}</td> <td>{{ extension.origin_v6 }}</td>
{% endif %} {% endif %}
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_create Extension %}
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='extension' id=extension.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ type.vlan }}</td> <td>{{ type.vlan }}</td>
<td>{{ type.ouverture_ports }}</td> <td>{{ type.ouverture_ports }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit type %}
{% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='iptype' id=type.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% if machines_list.paginator %} {% if machines_list.paginator %}
{% include "pagination.html" with list=machines_list %} {% include "pagination.html" with list=machines_list %}
{% endif %} {% endif %}
<table class="table"> <table class="table" id="machines_table">
<colgroup> <colgroup>
<col> <col>
<col> <col>
@ -50,30 +52,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</a> </a>
</td> </td>
<td class="text-right"> <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' %} {% 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 %} {% 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 %} {% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %}
{% acl_end %}
</td> </td>
</tr> </tr>
{% for interface in machine.interface_set.all %} {% for interface in machine.interface_set.all %}
<tr> <tr>
<td> <td>
{% if interface.domain.related_domain.all %} {% if interface.domain.related_domain.all %}
<div class="dropdown"> {{ interface.domain }}
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <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}}">
{{ interface.domain }} <span class="caret"></span> Afficher les alias
</button> </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 %} {% else %}
{{ interface.domain }} {{ interface.domain }}
{% endif %} {% 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> <i class="glyphicon glyphicon-edit"></i> <span class="caret"></span>
</button> </button>
{% include 'buttons/history.html' with href='machines:history' name='interface' id=interface.id %} {% 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 %} {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %}
<ul class="dropdown-menu" aria-labelledby="editioninterface"> {% acl_end %}
<li> <ul class="dropdown-menu" aria-labelledby="editioninterface">
{% can_edit interface %}
<li>
<a href="{% url 'machines:edit-interface' interface.id %}"> <a href="{% url 'machines:edit-interface' interface.id %}">
<i class="glyphicon glyphicon-edit"></i> Editer <i class="glyphicon glyphicon-edit"></i> Editer
</a> </a>
</li> </li>
{% acl_end %}
{% can_create Domain interface.id %}
<li> <li>
<a href="{% url 'machines:index-alias' interface.id %}"> <a href="{% url 'machines:index-alias' interface.id %}">
<i class="glyphicon glyphicon-edit"></i> Gerer les alias <i class="glyphicon glyphicon-edit"></i> Gerer les alias
</a> </a>
</li> </li>
{% acl_end %}
{% can_create OuverturePortList %}
<li> <li>
<a href="{% url 'machines:port-config' interface.id%}"> <a href="{% url 'machines:port-config' interface.id%}">
<i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports <i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports
</a> </a>
</li> </li>
{% acl_end %}
</ul> </ul>
</div> </div>
</td> </td>
</tr> </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 %} {% endfor %}
<tr> <tr>
<td colspan="8"></td> <td colspan="8"></td>
@ -126,6 +147,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</tbody> </tbody>
</table> </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 %} {% if machines_list.paginator %}
{% include "pagination.html" with list=machines_list %} {% include "pagination.html" with list=machines_list %}
{% endif %} {% endif %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -35,9 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ type.type }}</td> <td>{{ type.type }}</td>
<td>{{ type.ip_type }}</td> <td>{{ type.ip_type }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit type %}
{% include 'buttons/edit.html' with href='machines:edit-machinetype' id=type.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='machinetype' id=type.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -38,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ mx.priority }}</td> <td>{{ mx.priority }}</td>
<td>{{ mx.name }}</td> <td>{{ mx.name }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit mx %}
{% include 'buttons/edit.html' with href='machines:edit-mx' id=mx.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='mx' id=mx.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <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.port_access_mode }}</td>
<td>{{ nas.autocapture_mac }}</td> <td>{{ nas.autocapture_mac }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit nas %}
{% include 'buttons/edit.html' with href='machines:edit-nas' id=nas.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='nas' id=nas.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ ns.zone }}</td> <td>{{ ns.zone }}</td>
<td>{{ ns.ns }}</td> <td>{{ ns.ns }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit ns %}
{% include 'buttons/edit.html' with href='machines:edit-ns' id=ns.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='ns' id=ns.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -40,9 +42,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ service.regular_time_regen }}</td> <td>{{ service.regular_time_regen }}</td>
<td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td> <td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit service %}
{% include 'buttons/edit.html' with href='machines:edit-service' id=service.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='service' id=service.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -44,9 +46,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ soa.expire }}</td> <td>{{ soa.expire }}</td>
<td>{{ soa.ttl }}</td> <td>{{ soa.ttl }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit soa %}
{% include 'buttons/edit.html' with href='machines:edit-soa' id=soa.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='soa' id=soa.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ srv.port }}</td> <td>{{ srv.port }}</td>
<td>{{ srv.target }}</td> <td>{{ srv.target }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit srv %}
{% include 'buttons/edit.html' with href='machines:edit-srv' id=srv.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='srv' id=srv.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ txt.zone }}</td> <td>{{ txt.zone }}</td>
<td>{{ txt.dns_entry }}</td> <td>{{ txt.dns_entry }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_edit txt %}
{% include 'buttons/edit.html' with href='machines:edit-txt' id=txt.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='txt' id=txt.id %}
</td> </td>
</tr> </tr>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -39,9 +41,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ vlan.comment }}</td> <td>{{ vlan.comment }}</td>
<td>{% for range in vlan.iptype_set.all %}{{ range }}, {% endfor%}</td> <td>{% for range in vlan.iptype_set.all %}{{ range }}, {% endfor%}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% can_create Vlan %}
{% include 'buttons/edit.html' with href='machines:edit-vlan' id=vlan.id %} {% 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 %} {% include 'buttons/history.html' with href='machines:history' name='vlan' id=vlan.id %}
</td> </td>
</tr> </tr>

View file

@ -25,45 +25,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Machines{% endblock %} {% block title %}Machines{% endblock %}
{% block content %} {% block content %}
<h2>Liste des extensions</h2> <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> <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> <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 %} {% include "machines/aff_extension.html" with extension_list=extension_list %}
<h2>Liste des enregistrements SOA</h2> <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> <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> <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 %} {% include "machines/aff_soa.html" with soa_list=soa_list %}
<h2>Liste des enregistrements MX</h2> <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> <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> <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 %} {% include "machines/aff_mx.html" with mx_list=mx_list %}
<h2>Liste des enregistrements NS</h2> <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> <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> <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 %} {% include "machines/aff_ns.html" with ns_list=ns_list %}
<h2>Liste des enregistrements TXT</h2> <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> <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> <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 %} {% include "machines/aff_txt.html" with txt_list=txt_list %}
<h2>Liste des enregistrements SRV</h2> <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> <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> <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 %} {% include "machines/aff_srv.html" with srv_list=srv_list %}
<br /> <br />
<br /> <br />

View file

@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Ip{% endblock %} {% block title %}Ip{% endblock %}
{% block content %} {% block content %}
<h2>Liste des types d'ip</h2> <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> <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> <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 %} {% include "machines/aff_iptype.html" with iptype_list=iptype_list %}
<br /> <br />
<br /> <br />

View file

@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Machines{% endblock %} {% block title %}Machines{% endblock %}
{% block content %} {% block content %}
<h2>Liste des types de machines</h2> <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> <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> <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 %} {% include "machines/aff_machinetype.html" with machinetype_list=machinetype_list %}
<br /> <br />
<br /> <br />

View file

@ -25,16 +25,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Machines{% endblock %} {% block title %}Machines{% endblock %}
{% block content %} {% block content %}
<h2>Liste des nas</h2> <h2>Liste des nas</h2>
<h5>La correpondance nas-machinetype relie le type de nas à un type de machine. <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> 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> <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> <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 %} {% include "machines/aff_nas.html" with nas_list=nas_list %}
<br /> <br />
<br /> <br />

View file

@ -2,11 +2,15 @@
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Configuration de ports{% endblock %} {% block title %}Configuration de ports{% endblock %}
{% block content %} {% block content %}
<h2>Liste des configurations de ports</h2> <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> <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"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -44,8 +48,12 @@
</div> </div>
{% endif %} {% endif %}
<td class="text-right"> <td class="text-right">
{% can_delete pl %}
{% include 'buttons/suppr.html' with href='machines:del-portlist' id=pl.id %} {% 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 %} {% include 'buttons/edit.html' with href='machines:edit-portlist' id=pl.id %}
{% acl_end %}
</td> </td>
</tr> </tr>
{%endfor%} {%endfor%}

View file

@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Machines{% endblock %} {% block title %}Machines{% endblock %}
{% block content %} {% block content %}
<h2>Liste des services</h2> <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> <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> <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 %} {% include "machines/aff_service.html" with service_list=service_list %}
<h2>Etat des serveurs</h2> <h2>Etat des serveurs</h2>
{% include "machines/aff_servers.html" with servers_list=servers_list %} {% include "machines/aff_servers.html" with servers_list=servers_list %}

View file

@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Machines{% endblock %} {% block title %}Machines{% endblock %}
{% block content %} {% block content %}
<h2>Liste des vlans</h2> <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> <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> <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 %} {% include "machines/aff_vlan.html" with vlan_list=vlan_list %}
<br /> <br />
<br /> <br />

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% block sidebar %} {% block sidebar %}
{% if is_cableur %} {% can_view_all Machine %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Machines Machines
</a> </a>
{% acl_end %}
{% can_view_all MachineType %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-machinetype" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-machinetype" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Types de machines Types de machines
</a> </a>
{% acl_end %}
{% can_view_all Extension %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-extension" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-extension" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Extensions et zones Extensions et zones
</a> </a>
{% acl_end %}
{% can_view_all IpType %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-iptype" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-iptype" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Plages d'IP Plages d'IP
</a> </a>
{% acl_end %}
{% can_view_all Vlan %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-vlan" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-vlan" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Vlans Vlans
</a> </a>
{% acl_end %}
{% can_view_all Nas %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-nas" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-nas" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Gestion des nas Gestion des nas
</a> </a>
{% acl_end %}
{% can_view_all Service %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-service" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-service" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Services (dhcp, dns...) Services (dhcp, dns...)
</a> </a>
{% endif %} {% acl_end %}
{% if is_cableur %} {% can_view_all OuverturePortList %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Ouverture de ports Ouverture de ports
</a> </a>
{%endif%} {% acl_end %}
{% endblock %} {% endblock %}

View file

@ -24,7 +24,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
import re2o
from . import views from . import views
urlpatterns = [ urlpatterns = [
@ -61,7 +61,7 @@ urlpatterns = [
url(r'^del_srv/$', views.del_srv, name='del-srv'), url(r'^del_srv/$', views.del_srv, name='del-srv'),
url(r'^index_extension/$', views.index_extension, name='index-extension'), 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'^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'^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'^index_alias/(?P<interfaceid>[0-9]+)$', views.index_alias, name='index-alias'),
url(r'^add_service/$', views.add_service, name='add-service'), 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'^edit_nas/(?P<nasid>[0-9]+)$', views.edit_nas, name='edit-nas'),
url(r'^del_nas/$', views.del_nas, name='del-nas'), url(r'^del_nas/$', views.del_nas, name='del-nas'),
url(r'^index_nas/$', views.index_nas, name='index-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(
url(r'^history/(?P<object>interface)/(?P<id>[0-9]+)$', views.history, name='history'), r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
url(r'^history/(?P<object>machinetype)/(?P<id>[0-9]+)$', views.history, name='history'), re2o.views.history,
url(r'^history/(?P<object>extension)/(?P<id>[0-9]+)$', views.history, name='history'), name='history',
url(r'^history/(?P<object>soa)/(?P<id>[0-9]+)$', views.history, name='history'), kwargs={'application':'machines'},
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'^$', views.index, name='index'), url(r'^$', views.index, name='index'),
url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'), url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'),
url(r'^rest/regen-achieved/$', views.regen_achieved, name='regen-achieved'), 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/service_servers/$', views.service_servers, name='service-servers'),
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'), url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),
url(r'index_portlist/$', views.index_portlist, name='index-portlist'), 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'^edit_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
url(r'^del_portlist/(?P<pk>[0-9]+)$', views.del_portlist, name='del-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'^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

View file

@ -0,0 +1,2 @@
from .acl import *

40
preferences/acl.py Normal file
View 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."

View file

@ -173,7 +173,15 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form): class DelServiceForm(Form):
"""Suppression de services sur la page d'accueil""" """Suppression de services sur la page d'accueil"""
services = forms.ModelMultipleChoiceField( services = forms.ModelMultipleChoiceField(
queryset=Service.objects.all(), queryset=Service.objects.none(),
label="Enregistrements service actuels", label="Enregistrements service actuels",
widget=forms.CheckboxSelectMultiple 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()

View 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'),)},
),
]

View 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 = [
]

View file

@ -26,7 +26,7 @@ Reglages généraux, machines, utilisateurs, mail, general pour l'application.
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models from django.db import models
from cotisations.models import Paiement import cotisations.models
class OptionalUser(models.Model): class OptionalUser(models.Model):
@ -47,10 +47,67 @@ class OptionalUser(models.Model):
help_text="Tous les users peuvent en créer d'autres", 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): def clean(self):
"""Creation du mode de paiement par solde""" """Creation du mode de paiement par solde"""
if self.user_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): class OptionalMachine(models.Model):
@ -63,6 +120,64 @@ class OptionalMachine(models.Model):
max_lambdauser_aliases = models.IntegerField(default=10) max_lambdauser_aliases = models.IntegerField(default=10)
ipv6 = models.BooleanField(default=False) 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): class OptionalTopologie(models.Model):
"""Reglages pour la topologie : mode d'accès radius, vlan où placer """Reglages pour la topologie : mode d'accès radius, vlan où placer
@ -96,6 +211,63 @@ class OptionalTopologie(models.Model):
null=True 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): class GeneralOption(models.Model):
"""Options générales : nombre de resultats par page, nom du site, """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") site_name = models.CharField(max_length=32, default="Re2o")
email_from = models.EmailField(default="www-data@serveur.net") 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): class Service(models.Model):
"""Liste des services affichés sur la page d'accueil : url, description, """Liste des services affichés sur la page d'accueil : url, description,
@ -123,6 +353,65 @@ class Service(models.Model):
description = models.TextField() description = models.TextField()
image = models.ImageField(upload_to='logo', blank=True) 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): def __str__(self):
return str(self.name) return str(self.name)
@ -148,6 +437,63 @@ class AssoOption(models.Model):
null=True 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): class MailMessageOption(models.Model):
"""Reglages, mail de bienvenue et autre""" """Reglages, mail de bienvenue et autre"""
@ -155,3 +501,61 @@ class MailMessageOption(models.Model):
welcome_mail_fr = models.TextField(default="") welcome_mail_fr = models.TextField(default="")
welcome_mail_en = 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"

View file

@ -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., with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -40,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ service.description }}</td> <td>{{ service.description }}</td>
<td>{{ service.image }}</td> <td>{{ service.image }}</td>
<td class="text-right"> <td class="text-right">
{% if is_admin %} {% can_edit service%}
{% include 'buttons/edit.html' with href='preferences:edit-services' id=service.id %} {% include 'buttons/edit.html' with href='preferences:edit-service' id=service.id %}
{% endif %} {% acl_end %}
{% include 'buttons/history.html' with href='preferences:history' name='service' id=service.id %} {% include 'buttons/history.html' with href='preferences:history' name='service' id=service.id %}
</td> </td>
</tr> </tr>

View file

@ -24,17 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Création et modification des préférences{% endblock %} {% block title %}Création et modification des préférences{% endblock %}
{% block content %} {% block content %}
<h4>Préférences utilisateur</h4> <h4>Préférences utilisateur</h4>
{% if is_bureau %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalUser' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalUser' %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
Editer Editer
</a> </a>
{% endif %}
<p> <p>
</p> </p>
<table class="table table-striped"> <table class="table table-striped">
@ -58,12 +57,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</tr> </tr>
</table> </table>
<h4>Préférences machines</h4> <h4>Préférences machines</h4>
{% if is_bureau %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalMachine' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalMachine' %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
Editer Editer
</a> </a>
{% endif %}
<p> <p>
</p> </p>
<table class="table table-striped"> <table class="table table-striped">
@ -81,12 +78,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</tr> </tr>
</table> </table>
<h4>Préférences topologie</h4> <h4>Préférences topologie</h4>
{% if is_bureau %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalTopologie' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalTopologie' %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
Editer Editer
</a> </a>
{% endif %}
<p> <p>
</p> </p>
<table class="table table-striped"> <table class="table table-striped">
@ -104,14 +99,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</tr> </tr>
</table> </table>
<h4>Préférences generales</h4> <h4>Préférences generales</h4>
{% if is_bureau %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'GeneralOption' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'GeneralOption' %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
Editer Editer
</a> </a>
{% endif %}
<p> <p>
</p> </p>
<table class="table table-striped"> <table class="table table-striped">
<tr> <tr>
<th>Nom du site web</th> <th>Nom du site web</th>
@ -137,12 +130,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
</table> </table>
<h4>Données de l'association</h4> <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' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'AssoOption' %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
Editer Editer
</a> </a>
{% endif %}
<p> <p>
</p> </p>
<table class="table table-striped"> <table class="table table-striped">
@ -164,19 +155,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ assooptions.telephone }}</td> <td>{{ assooptions.telephone }}</td>
<th>Pseudo d'usage</th> <th>Pseudo d'usage</th>
<td>{{ assooptions.pseudo }}</td> <td>{{ assooptions.pseudo }}</td>
</tr> </tr>
<tr> <tr>
<th>Objet utilisateur de l'association</th> <th>Objet utilisateur de l'association</th>
<td>{{ assooptions.utilisateur_asso }}</td> <td>{{ assooptions.utilisateur_asso }}</td>
</tr> </tr>
</table> </table>
<h4>Messages personalisé dans les mails</h4> <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' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'MailMessageOption' %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
Editer Editer
</a> </a>
{% endif %}
<p> <p>
</p> </p>
<table class="table table-striped"> <table class="table table-striped">
@ -190,10 +179,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</tr> </tr>
</table> </table>
<h2>Liste des services page d'accueil</h2> <h2>Liste des services page d'accueil</h2>
{% if is_infra %} {% can_create Service%}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-services' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un service</a> <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> <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 %} {% include "preferences/aff_service.html" with service_list=service_list %}
<br /> <br />
<br /> <br />

View file

@ -28,6 +28,7 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from . import views from . import views
import re2o
urlpatterns = [ urlpatterns = [
@ -61,17 +62,18 @@ urlpatterns = [
views.edit_options, views.edit_options,
name='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( url(
r'^edit_services/(?P<servicesid>[0-9]+)$', r'^edit_service/(?P<serviceid>[0-9]+)$',
views.edit_services, views.edit_service,
name='edit-services' name='edit-service'
), ),
url(r'^del_services/$', views.del_services, name='del-services'), url(r'^del_services/$', views.del_services, name='del-services'),
url( url(
r'^history/(?P<object_name>service)/(?P<object_id>[0-9]+)$', r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
views.history, re2o.views.history,
name='history' name='history',
kwargs={'application':'preferences'},
), ),
url(r'^$', views.display_options, name='display-options'), url(r'^$', views.display_options, name='display-options'),
] ]

View file

@ -42,6 +42,7 @@ from reversion.models import Version
from reversion import revisions as reversion from reversion import revisions as reversion
from re2o.views import form 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 .forms import ServiceForm, DelServiceForm
from .models import Service, OptionalUser, OptionalMachine, AssoOption from .models import Service, OptionalUser, OptionalMachine, AssoOption
from .models import MailMessageOption, GeneralOption, OptionalTopologie from .models import MailMessageOption, GeneralOption, OptionalTopologie
@ -50,7 +51,12 @@ from . import forms
@login_required @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): def display_options(request):
"""Vue pour affichage des options (en vrac) classé selon les models """Vue pour affichage des options (en vrac) classé selon les models
correspondants dans un tableau""" correspondants dans un tableau"""
@ -80,6 +86,11 @@ def edit_options(request, section):
form_instance = getattr(forms, 'Edit' + section + 'Form', None) form_instance = getattr(forms, 'Edit' + section + 'Form', None)
if model and form: if model and form:
options_instance, _created = model.objects.get_or_create() 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( options = form_instance(
request.POST or None, request.POST or None,
instance=options_instance instance=options_instance
@ -106,57 +117,52 @@ def edit_options(request, section):
@login_required @login_required
@permission_required('admin') @can_create(Service)
def add_services(request): def add_service(request):
"""Ajout d'un service de la page d'accueil""" """Ajout d'un service de la page d'accueil"""
services = ServiceForm(request.POST or None) service = ServiceForm(request.POST or None)
if services.is_valid(): if service.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
services.save() service.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "Ce service a été ajouté") messages.success(request, "Ce service a été ajouté")
return redirect(reverse('preferences:display-options')) return redirect(reverse('preferences:display-options'))
return form( return form(
{'preferenceform': services}, {'preferenceform': service},
'preferences/preferences.html', 'preferences/preferences.html',
request request
) )
@login_required @login_required
@permission_required('admin') @can_edit(Service)
def edit_services(request, servicesid): def edit_service(request, service_instance, serviceid):
"""Edition des services affichés sur la page d'accueil""" """Edition des services affichés sur la page d'accueil"""
try: service = ServiceForm(request.POST or None, instance=service_instance)
services_instance = Service.objects.get(pk=servicesid) if service.is_valid():
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():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
services.save() service.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment( reversion.set_comment(
"Champs modifié(s) : %s" % ', '.join( "Champs modifié(s) : %s" % ', '.join(
field for field in services.changed_data field for field in service.changed_data
) )
) )
messages.success(request, "Service modifié") messages.success(request, "Service modifié")
return redirect(reverse('preferences:display-options')) return redirect(reverse('preferences:display-options'))
return form( return form(
{'preferenceform': services}, {'preferenceform': service},
'preferences/preferences.html', 'preferences/preferences.html',
request request
) )
@login_required @login_required
@permission_required('admin') @can_delete_set(Service)
def del_services(request): def del_services(request, instances):
"""Suppression d'un service de la page d'accueil""" """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(): if services.is_valid():
services_dels = services.cleaned_data['services'] services_dels = services.cleaned_data['services']
for services_del in services_dels: for services_del in services_dels:
@ -164,7 +170,7 @@ def del_services(request):
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
services_del.delete() services_del.delete()
reversion.set_user(request.user) 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: except ProtectedError:
messages.error(request, "Erreur le service\ messages.error(request, "Erreur le service\
suivant %s ne peut être supprimé" % services_del) suivant %s ne peut être supprimé" % services_del)
@ -174,33 +180,3 @@ def del_services(request):
'preferences/preferences.html', 'preferences/preferences.html',
request 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
View 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

View file

@ -39,28 +39,10 @@ def context_user(request):
messages.warning(request, global_message) messages.warning(request, global_message)
if user.is_authenticated(): if user.is_authenticated():
interfaces = user.user_interfaces() 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: else:
interfaces = None interfaces = None
is_cableur = False
is_bureau = False
is_bofh = False
is_trez = False
is_infra = False
is_admin = False
return { return {
'request_user': user, '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, 'interfaces': interfaces,
'site_name': general_options.site_name, 'site_name': general_options.site_name,
'ipv6_enabled': machine_options.ipv6, 'ipv6_enabled': machine_options.ipv6,

79
re2o/field_permissions.py Normal file
View 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

View file

@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block title %}Accueil{% endblock %} {% block title %}Accueil{% endblock %}
{% block content %} {% block content %}
<h1>Bienvenue sur {{ site_name }} !</h1> <h1>Bienvenue sur {{ request.get_host }} !</h1>
<div class="row"> <div class="row">
{% for service_list in services_urls %} {% for service_list in services_urls %}

View file

@ -2,19 +2,19 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # 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 # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Maël Kervella # Copyright © 2017 Maël Kervella
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License along # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

410
re2o/templatetags/acl.py Normal file
View 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)

View file

@ -39,6 +39,9 @@ from __future__ import unicode_literals
from django.utils import timezone from django.utils import timezone
from django.db.models import Q 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 cotisations.models import Cotisation, Facture, Paiement, Vente
from machines.models import Domain, Interface, Machine from machines.models import Domain, Interface, Machine

View file

@ -26,10 +26,17 @@ les views
from __future__ import unicode_literals 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.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 Service
from preferences.models import OptionalUser, GeneralOption
import users, preferences, cotisations, topologie, machines
def form(ctx, template, request): def form(ctx, template, request):
"""Form générique, raccourci importé par les fonctions views du site""" """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()): for indice, serv in enumerate(Service.objects.all()):
services[indice % 3].append(serv) services[indice % 3].append(serv)
return form({'services_urls': services}, 're2o/index.html', request) 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}
)

View file

@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .acl import *

39
search/acl.py Normal file
View 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

View file

@ -120,7 +120,7 @@ def finish_results(results, col, order):
return results return results
def search_single_word(word, filters, is_cableur, user_id, def search_single_word(word, filters, user,
start, end, user_state, aff): start, end, user_state, aff):
""" Construct the correct filters to match differents fields of some models """ Construct the correct filters to match differents fields of some models
with the given query according to the given filters. 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 adherent__room__name__icontains=word
) )
) & Q(state__in=user_state) ) & Q(state__in=user_state)
if not is_cableur: if not User.can_view_all(user)[0]:
filter_users &= Q(id=user_id) filter_users &= Q(id=user.id)
filters['users'] |= filter_users filters['users'] |= filter_users
# Machines # Machines
@ -167,8 +167,8 @@ def search_single_word(word, filters, is_cableur, user_id,
) | Q( ) | Q(
interface__ipv4__ipv4__icontains=word interface__ipv4__ipv4__icontains=word
) )
if not is_cableur: if not Machine.can_view_all(user)[0]:
filter_machines &= Q(user__id=user_id) filter_machines &= Q(user__id=user.id)
filters['machines'] |= filter_machines filters['machines'] |= filter_machines
# Factures # Factures
@ -243,7 +243,7 @@ def search_single_word(word, filters, is_cableur, user_id,
filters['whitelists'] |= filter_whitelists filters['whitelists'] |= filter_whitelists
# Rooms # Rooms
if '5' in aff and is_cableur: if '5' in aff and Room.can_view_all(user):
filter_rooms = Q( filter_rooms = Q(
details__icontains=word details__icontains=word
) | Q( ) | Q(
@ -254,7 +254,7 @@ def search_single_word(word, filters, is_cableur, user_id,
filters['rooms'] |= filter_rooms filters['rooms'] |= filter_rooms
# Switch ports # Switch ports
if '6' in aff and is_cableur: if '6' in aff and User.can_view_all(user):
filter_ports = Q( filter_ports = Q(
room__name__icontains=word room__name__icontains=word
) | Q( ) | Q(
@ -275,7 +275,7 @@ def search_single_word(word, filters, is_cableur, user_id,
filters['ports'] |= filter_ports filters['ports'] |= filter_ports
# Switches # Switches
if '7' in aff and is_cableur: if '7' in aff and Switch.can_view_all(user):
filter_switches = Q( filter_switches = Q(
switch_interface__domain__name__icontains=word switch_interface__domain__name__icontains=word
) | Q( ) | Q(
@ -374,8 +374,7 @@ def get_results(query, request, params):
filters = search_single_word( filters = search_single_word(
word, word,
filters, filters,
request.user.has_perms(('cableur',)), request.user,
request.user.id,
start, start,
end, end,
user_state, user_state,

View file

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{# Load the tag library #} {# Load the tag library #}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head prefix="og: http://ogp.me/ns#"> <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"> <div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="{% url "users:mon-profil" %}">Mon profil</a></li> <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> <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> <li><a href="{% url "machines:index" %}">Machines</a></li>
{% acl_end %}
{% can_view_app cotisations %}
<li><a href="{% url "cotisations:index" %}">Cotisations</a></li> <li><a href="{% url "cotisations:index" %}">Cotisations</a></li>
{% acl_end %}
{% can_view_app topologie %}
<li><a href="{% url "topologie:index" %}">Topologie</a></li> <li><a href="{% url "topologie:index" %}">Topologie</a></li>
{% acl_end %}
{% can_view_app logs %}
<li><a href="{% url "logs:index" %}">Statistiques</a></li> <li><a href="{% url "logs:index" %}">Statistiques</a></li>
{% endif %} {% acl_end %}
</ul> </ul>
<div class="col-sm-3 col-md-3 navbar-right"> <div class="col-sm-3 col-md-3 navbar-right">
<form action="{% url "search:search"%}" class="navbar-form" role="search"> <form action="{% url "search:search"%}" class="navbar-form" role="search">
@ -103,9 +112,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<span class="glyphicon glyphicon-log-in"></span> Login <span class="glyphicon glyphicon-log-in"></span> Login
</a> </a>
{% endif %} {% endif %}
</li> </li>
</ul> </ul>
{% if is_cableur %} {% can_view_app preferences %}
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li> <li>
<a href="{% url 'preferences:display-options' %}"> <a href="{% url 'preferences:display-options' %}">
@ -113,7 +122,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</a> </a>
</li> </li>
</ul> </ul>
{% endif %} {% acl_end %}
</div> </div>
</div> </div>
</nav> </nav>
@ -205,7 +214,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<footer class="navbar"> <footer class="navbar">
<div class="containerfluid text-center"> <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> </div>
</footer> </footer>
{# Read the documentation for more information #} {# Read the documentation for more information #}

View file

@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .acl import *

40
topologie/acl.py Normal file
View 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."

View 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'),)},
),
]

View file

@ -60,6 +60,38 @@ class Stack(models.Model):
member_id_min = models.PositiveIntegerField() member_id_min = models.PositiveIntegerField()
member_id_max = 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): def __str__(self):
return " ".join([self.name, self.stack_id]) return " ".join([self.name, self.stack_id])
@ -113,7 +145,37 @@ class Switch(models.Model):
class Meta: class Meta:
unique_together = ('stack', 'stack_member_id') 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): def __str__(self):
return self.location + ' ' + str(self.switch_interface) return self.location + ' ' + str(self.switch_interface)
@ -167,6 +229,38 @@ class ModelSwitch(models.Model):
on_delete=models.PROTECT 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): def __str__(self):
return str(self.constructor) + ' ' + self.reference return str(self.constructor) + ' ' + self.reference
@ -176,6 +270,39 @@ class ConstructorSwitch(models.Model):
PRETTY_NAME = "Constructeur de switch" PRETTY_NAME = "Constructeur de switch"
name = models.CharField(max_length=255) 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): def __str__(self):
return self.name return self.name
@ -239,7 +366,43 @@ class Port(models.Model):
class Meta: class Meta:
unique_together = ('switch', 'port') 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): def make_port_related(self):
""" Synchronise le port distant sur self""" """ Synchronise le port distant sur self"""
related_port = self.related related_port = self.related
@ -293,6 +456,36 @@ class Room(models.Model):
class Meta: class Meta:
ordering = ['name'] 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): def __str__(self):
return self.name return self.name

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% if room_list.paginator %} {% if room_list.paginator %}
{% include "pagination.html" with list=room_list %} {% include "pagination.html" with list=room_list %}
{% endif %} {% 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 %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'room' room.pk %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-room' room.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% acl_end %}
{% can_delete room %}
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-room' room.id %}"> <a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-room' room.id %}">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% if constructor_switch_list.paginator %} {% if constructor_switch_list.paginator %}
{% include "pagination.html" with list=constructor_switch_list %} {% include "pagination.html" with list=constructor_switch_list %}
{% endif %} {% 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 %}"> <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> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <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> <i class="glyphicon glyphicon-edit"></i>
</a> </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 %}"> <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> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% if model_switch_list.paginator %} {% if model_switch_list.paginator %}
{% include "pagination.html" with list=model_switch_list %} {% include "pagination.html" with list=model_switch_list %}
{% endif %} {% 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 %}"> <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> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <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> <i class="glyphicon glyphicon-edit"></i>
</a> </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 %}"> <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> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <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 %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'port' port.pk %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-port' port.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% acl_end %}
{% can_delete port %}
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-port' port.pk %}"> <a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-port' port.pk %}">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <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 %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% acl_end %}
{% can_delete stack %}
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}"> <a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
{% endif %} {% endif %}
</tr> </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 %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% acl_end %}
{% can_delete stack %}
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}"> <a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
{% endfor %} {% endfor %}
</tbody> </tbody>

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
{% if switch_list.paginator %} {% if switch_list.paginator %}
{% include "pagination.html" with list=switch_list %} {% include "pagination.html" with list=switch_list %}
{% endif %} {% endif %}
@ -56,11 +58,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{switch.details}}</td> <td>{{switch.details}}</td>
<td class="text-right"> <td class="text-right">
{% include 'buttons/history.html' with href='topologie:history' name='switch' id=switch.pk%} {% 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 %} {% 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 %} {% 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'%} {% include 'buttons/add.html' with href='topologie:create-ports' id=switch.pk desc='Création de ports'%}
{% endif %} {% acl_end %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %} {% endcomment %}
{% load acl %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <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 %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </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 %}"> <a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
<i class="glyphicon glyphicon-edit"></i> <i class="glyphicon glyphicon-edit"></i>
</a> </a>
{% acl_end %}
{% can_delete stack %}
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}"> <a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
</a> </a>
{% endif %} {% acl_end %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Switchs{% endblock %} {% block title %}Switchs{% endblock %}
{% block content %} {% block content %}
<h2>Switchs</h2> <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> <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> <hr>
{% endif %} {% acl_end %}
{% include "topologie/aff_switch.html" with switch_list=switch_list %} {% include "topologie/aff_switch.html" with switch_list=switch_list %}
<br /> <br />
<br /> <br />

View file

@ -24,21 +24,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Modèles de switches{% endblock %} {% block title %}Modèles de switches{% endblock %}
{% block content %} {% block content %}
<h2>Modèles de switches</h2> <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> <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> <hr>
{% endif %} {% acl_end %}
{% include "topologie/aff_model_switch.html" with model_switch_list=model_switch_list %} {% include "topologie/aff_model_switch.html" with model_switch_list=model_switch_list %}
<h2>Constructeurs de switches</h2> <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> <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> <hr>
{% endif %} {% acl_end %}
{% include "topologie/aff_constructor_switch.html" with constructor_switch_list=constructor_switch_list %} {% include "topologie/aff_constructor_switch.html" with constructor_switch_list=constructor_switch_list %}
<br /> <br />
<br /> <br />

View file

@ -24,16 +24,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Ports du switch{% endblock %} {% block title %}Ports du switch{% endblock %}
{% block content %} {% block content %}
<h2>Switch {{ nom_switch }}</h2> <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> <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> <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> <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 %} {% include "topologie/aff_port.html" with port_list=port_list %}
<br /> <br />
<br /> <br />

View file

@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Chambres{% endblock %} {% block title %}Chambres{% endblock %}
{% block content %} {% block content %}
<h2>Chambres</h2> <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> <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> <hr>
{% endif %} {% acl_end %}
{% include "topologie/aff_chambres.html" with room_list=room_list %} {% include "topologie/aff_chambres.html" with room_list=room_list %}
<br /> <br />
<br /> <br />

View file

@ -24,14 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endcomment %} {% endcomment %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load acl %}
{% block title %}Stacks{% endblock %} {% block title %}Stacks{% endblock %}
{% block content %} {% block content %}
<h2>Stacks</h2> <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> <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 %} {% include "topologie/aff_stacks.html" with stack_list=stack_list %}
<br /> <br />
<br /> <br />

View file

@ -30,6 +30,7 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
import re2o
from . import views from . import views
urlpatterns = [ urlpatterns = [
@ -45,24 +46,12 @@ urlpatterns = [
url(r'^switch/(?P<switch_id>[0-9]+)$', url(r'^switch/(?P<switch_id>[0-9]+)$',
views.index_port, views.index_port,
name='index-port'), name='index-port'),
url(r'^history/(?P<object_name>switch)/(?P<object_id>[0-9]+)$', url(
views.history, r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
name='history'), re2o.views.history,
url(r'^history/(?P<object_name>port)/(?P<object_id>[0-9]+)$', name='history',
views.history, kwargs={'application':'topologie'},
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'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'), 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'^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'), url(r'^del_port/(?P<port_id>[0-9]+)$', views.del_port, name='del-port'),

View file

@ -60,12 +60,19 @@ from topologie.forms import (
AddPortForm, AddPortForm,
EditRoomForm, EditRoomForm,
StackForm, StackForm,
EditModelSwitchForm, EditModelSwitchForm,
EditConstructorSwitchForm, EditConstructorSwitchForm,
CreatePortsForm CreatePortsForm
) )
from users.views import form from users.views import form
from re2o.utils import SortTable from re2o.utils import SortTable
from re2o.acl import (
can_create,
can_edit,
can_delete,
can_view,
can_view_all,
)
from machines.forms import ( from machines.forms import (
DomainForm, DomainForm,
NewMachineForm, NewMachineForm,
@ -78,7 +85,7 @@ from preferences.models import AssoOption, GeneralOption
@login_required @login_required
@permission_required('cableur') @can_view_all(Switch)
def index(request): def index(request):
""" Vue d'affichage de tous les swicthes""" """ Vue d'affichage de tous les swicthes"""
switch_list = Switch.objects\ switch_list = Switch.objects\
@ -110,76 +117,10 @@ def index(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(Port)
def history(request, object_name, object_id): @can_view(Switch)
""" Vue générique pour afficher l'historique complet d'un objet""" def index_port(request, switch, switch_id):
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):
""" Affichage de l'ensemble des ports reliés à un switch particulier""" """ 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)\ port_list = Port.objects.filter(switch=switch)\
.select_related('room')\ .select_related('room')\
.select_related('machine_interface__domain__extension')\ .select_related('machine_interface__domain__extension')\
@ -202,7 +143,7 @@ def index_port(request, switch_id):
@login_required @login_required
@permission_required('cableur') @can_view_all(Room)
def index_room(request): def index_room(request):
""" Affichage de l'ensemble des chambres""" """ Affichage de l'ensemble des chambres"""
room_list = Room.objects room_list = Room.objects
@ -230,7 +171,7 @@ def index_room(request):
@login_required @login_required
@permission_required('infra') @can_view_all(Stack)
def index_stack(request): def index_stack(request):
"""Affichage de la liste des stacks (affiche l'ensemble des switches)""" """Affichage de la liste des stacks (affiche l'ensemble des switches)"""
stack_list = Stack.objects\ stack_list = Stack.objects\
@ -247,7 +188,8 @@ def index_stack(request):
@login_required @login_required
@permission_required('cableur') @can_view_all(ModelSwitch)
@can_view_all(ConstructorSwitch)
def index_model_switch(request): def index_model_switch(request):
""" Affichage de l'ensemble des modèles de switches""" """ Affichage de l'ensemble des modèles de switches"""
model_switch_list = ModelSwitch.objects model_switch_list = ModelSwitch.objects
@ -271,7 +213,7 @@ def index_model_switch(request):
@login_required @login_required
@permission_required('infra') @can_create(Port)
def new_port(request, switch_id): def new_port(request, switch_id):
""" Nouveau port""" """ Nouveau port"""
try: try:
@ -292,28 +234,18 @@ def new_port(request, switch_id):
except IntegrityError: except IntegrityError:
messages.error(request, "Ce port existe déjà") messages.error(request, "Ce port existe déjà")
return redirect(reverse( return redirect(reverse(
'topologie:index-port', 'topologie:index-port',
kwargs={'switch_id':switch_id} kwargs={'switch_id':switch_id}
)) ))
return form({'id_switch': switch_id,'topoform': port}, 'topologie/topo.html', request) return form({'id_switch': switch_id,'topoform': port}, 'topologie/topo.html', request)
@login_required @login_required
@permission_required('infra') @can_edit(Port)
def edit_port(request, port_id): def edit_port(request, port_object, port_id):
""" Edition d'un port. Permet de changer le switch parent et """ Edition d'un port. Permet de changer le switch parent et
l'affectation du port""" 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) port = EditPortForm(request.POST or None, instance=port_object)
if port.is_valid(): if port.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -331,14 +263,9 @@ def edit_port(request, port_id):
@login_required @login_required
@permission_required('infra') @can_delete(Port)
def del_port(request, port_id): def del_port(request, port, port_id):
""" Supprime le port""" """ 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": if request.method == "POST":
try: try:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -357,7 +284,7 @@ def del_port(request, port_id):
@login_required @login_required
@permission_required('infra') @can_create(Stack)
def new_stack(request): def new_stack(request):
"""Ajoute un nouveau stack : stack_id_min, max, et nombre de switches""" """Ajoute un nouveau stack : stack_id_min, max, et nombre de switches"""
stack = StackForm(request.POST or None) stack = StackForm(request.POST or None)
@ -371,14 +298,10 @@ def new_stack(request):
@login_required @login_required
@permission_required('infra') @can_edit(Stack)
def edit_stack(request, stack_id): def edit_stack(request, stack, stack_id):
"""Edition d'un stack (nombre de switches, nom...)""" """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) stack = StackForm(request.POST or None, instance=stack)
if stack.is_valid(): if stack.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -394,14 +317,9 @@ def edit_stack(request, stack_id):
@login_required @login_required
@permission_required('infra') @can_delete(Stack)
def del_stack(request, stack_id): def del_stack(request, stack, stack_id):
"""Supprime un stack""" """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": if request.method == "POST":
try: try:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -412,19 +330,15 @@ def del_stack(request, stack_id):
except ProtectedError: except ProtectedError:
messages.error(request, "La stack %s est affectée à un autre\ messages.error(request, "La stack %s est affectée à un autre\
objet, impossible de la supprimer" % stack) objet, impossible de la supprimer" % stack)
return redirect(reverse('topologie:index-stack')) return redirect(reverse('topologie:index-stack'))
return form({'objet': stack}, 'topologie/delete.html', request) return form({'objet': stack}, 'topologie/delete.html', request)
@login_required @login_required
@permission_required('infra') @can_edit(Stack)
def edit_switchs_stack(request, stack_id): def edit_switchs_stack(request, stack, stack_id):
"""Permet d'éditer la liste des switches dans une stack et l'ajouter""" """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": if request.method == "POST":
pass pass
else: else:
@ -434,16 +348,19 @@ def edit_switchs_stack(request, stack_id):
@login_required @login_required
@permission_required('infra') @can_create(Switch)
def new_switch(request): def new_switch(request):
""" Creation d'un switch. Cree en meme temps l'interface et la machine """ Creation d'un switch. Cree en meme temps l'interface et la machine
associée. Vue complexe. Appelle successivement les 4 models forms associée. Vue complexe. Appelle successivement les 4 models forms
adaptés : machine, interface, domain et switch""" adaptés : machine, interface, domain et switch"""
switch = NewSwitchForm(request.POST or None) switch = NewSwitchForm(request.POST or None)
machine = NewMachineForm(request.POST or None) machine = NewMachineForm(
request.POST or None,
user=request.user
)
interface = AddInterfaceForm( interface = AddInterfaceForm(
request.POST or None, request.POST or None,
infra=request.user.has_perms(('infra',)) user=request.user
) )
domain = DomainForm( domain = DomainForm(
request.POST or None, request.POST or None,
@ -492,7 +409,7 @@ def new_switch(request):
@login_required @login_required
@permission_required('infra') @can_create(Port)
def create_ports(request, switch_id): def create_ports(request, switch_id):
""" Création d'une liste de ports pour un switch.""" """ Création d'une liste de ports pour un switch."""
try: try:
@ -500,14 +417,14 @@ def create_ports(request, switch_id):
except Switch.DoesNotExist: except Switch.DoesNotExist:
messages.error(request, u"Switch inexistant") messages.error(request, u"Switch inexistant")
return redirect("/topologie/") return redirect("/topologie/")
s_begin = s_end = 0 s_begin = s_end = 0
nb_ports = switch.ports.count() nb_ports = switch.ports.count()
if nb_ports > 0: if nb_ports > 0:
ports = switch.ports.order_by('port').values('port') ports = switch.ports.order_by('port').values('port')
s_begin = ports.first().get('port') s_begin = ports.first().get('port')
s_end = ports.last().get('port') s_end = ports.last().get('port')
port_form = CreatePortsForm( port_form = CreatePortsForm(
request.POST or None, request.POST or None,
initial={'begin': s_begin, 'end': s_end} initial={'begin': s_begin, 'end': s_end}
@ -528,15 +445,11 @@ def create_ports(request, switch_id):
@login_required @login_required
@permission_required('infra') @can_edit(Switch)
def edit_switch(request, switch_id): def edit_switch(request, switch, switch_id):
""" Edition d'un switch. Permet de chambre nombre de ports, """ Edition d'un switch. Permet de chambre nombre de ports,
place dans le stack, interface et machine associée""" 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) switch_form = EditSwitchForm(request.POST or None, instance=switch)
machine_form = EditMachineForm( machine_form = EditMachineForm(
request.POST or None, request.POST or None,
@ -596,7 +509,7 @@ def edit_switch(request, switch_id):
@login_required @login_required
@permission_required('infra') @can_create(Room)
def new_room(request): def new_room(request):
"""Nouvelle chambre """ """Nouvelle chambre """
room = EditRoomForm(request.POST or None) room = EditRoomForm(request.POST or None)
@ -611,14 +524,10 @@ def new_room(request):
@login_required @login_required
@permission_required('infra') @can_edit(Room)
def edit_room(request, room_id): def edit_room(request, room, room_id):
""" Edition numero et details de la chambre""" """ 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) room = EditRoomForm(request.POST or None, instance=room)
if room.is_valid(): if room.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -633,14 +542,9 @@ def edit_room(request, room_id):
@login_required @login_required
@permission_required('infra') @can_delete(Room)
def del_room(request, room_id): def del_room(request, room, room_id):
""" Suppression d'un chambre""" """ 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": if request.method == "POST":
try: try:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -651,7 +555,7 @@ def del_room(request, room_id):
except ProtectedError: except ProtectedError:
messages.error(request, "La chambre %s est affectée à un autre objet,\ messages.error(request, "La chambre %s est affectée à un autre objet,\
impossible de la supprimer (switch ou user)" % room) impossible de la supprimer (switch ou user)" % room)
return redirect(reverse('topologie:index-room')) return redirect(reverse('topologie:index-room'))
return form({ return form({
'objet': room, 'objet': room,
'objet_name': 'Chambre' 'objet_name': 'Chambre'
@ -659,7 +563,7 @@ def del_room(request, room_id):
@login_required @login_required
@permission_required('infra') @can_create(ModelSwitch)
def new_model_switch(request): def new_model_switch(request):
"""Nouveau modèle de switch""" """Nouveau modèle de switch"""
model_switch = EditModelSwitchForm(request.POST or None) model_switch = EditModelSwitchForm(request.POST or None)
@ -674,14 +578,10 @@ def new_model_switch(request):
@login_required @login_required
@permission_required('infra') @can_edit(ModelSwitch)
def edit_model_switch(request, model_switch_id): def edit_model_switch(request, model_switch, model_switch_id):
""" Edition d'un modèle de switch""" """ 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) model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch)
if model_switch.is_valid(): if model_switch.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -696,14 +596,9 @@ def edit_model_switch(request, model_switch_id):
@login_required @login_required
@permission_required('infra') @can_delete(ModelSwitch)
def del_model_switch(request, model_switch_id): def del_model_switch(request, model_switch_id):
""" Suppression d'un modèle de switch""" """ 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": if request.method == "POST":
try: try:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -722,7 +617,7 @@ def del_model_switch(request, model_switch_id):
@login_required @login_required
@permission_required('infra') @can_create(ConstructorSwitch)
def new_constructor_switch(request): def new_constructor_switch(request):
"""Nouveau constructeur de switch""" """Nouveau constructeur de switch"""
constructor_switch = EditConstructorSwitchForm(request.POST or None) constructor_switch = EditConstructorSwitchForm(request.POST or None)
@ -737,14 +632,10 @@ def new_constructor_switch(request):
@login_required @login_required
@permission_required('infra') @can_edit(ConstructorSwitch)
def edit_constructor_switch(request, constructor_switch_id): def edit_constructor_switch(request, constructor_switch, constructor_switch_id):
""" Edition d'un constructeur de switch""" """ 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) constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch)
if constructor_switch.is_valid(): if constructor_switch.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -759,14 +650,9 @@ def edit_constructor_switch(request, constructor_switch_id):
@login_required @login_required
@permission_required('infra') @can_delete(ConstructorSwitch)
def del_constructor_switch(request, constructor_switch_id): def del_constructor_switch(request, constructor_switch_id):
""" Suppression d'un constructeur de switch""" """ 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": if request.method == "POST":
try: try:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():

View file

@ -21,3 +21,4 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .acl import *

40
users/acl.py Normal file
View 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."

View file

@ -32,7 +32,7 @@ from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from reversion.admin import VersionAdmin 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 Ban, Whitelist, Request, LdapUser, LdapServiceUser
from .models import LdapServiceUserGroup, LdapUserGroup from .models import LdapServiceUserGroup, LdapUserGroup
from .forms import UserChangeForm, UserCreationForm from .forms import UserChangeForm, UserCreationForm
@ -86,7 +86,7 @@ class SchoolAdmin(VersionAdmin):
class ListRightAdmin(VersionAdmin): class ListRightAdmin(VersionAdmin):
"""Gestion de la liste des droits existants """Gestion de la liste des droits existants
Ne permet pas l'edition du gid (primarykey pour ldap)""" Ne permet pas l'edition du gid (primarykey pour ldap)"""
list_display = ('listright',) list_display = ('unix_name',)
class ListShellAdmin(VersionAdmin): class ListShellAdmin(VersionAdmin):
@ -94,11 +94,6 @@ class ListShellAdmin(VersionAdmin):
pass pass
class RightAdmin(VersionAdmin):
"""Gestion de la liste des droits affectés"""
pass
class RequestAdmin(admin.ModelAdmin): class RequestAdmin(admin.ModelAdmin):
"""Gestion des request objet, ticket pour lien de reinit mot de passe""" """Gestion des request objet, ticket pour lien de reinit mot de passe"""
list_display = ('user', 'type', 'created_at', 'expires_at') 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(LdapServiceUser, LdapServiceUserAdmin)
admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin) admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin)
admin.site.register(School, SchoolAdmin) admin.site.register(School, SchoolAdmin)
admin.site.register(Right, RightAdmin)
admin.site.register(ListRight, ListRightAdmin) admin.site.register(ListRight, ListRightAdmin)
admin.site.register(ListShell, ListShellAdmin) admin.site.register(ListShell, ListShellAdmin)
admin.site.register(Ban, BanAdmin) admin.site.register(Ban, BanAdmin)

View file

@ -38,12 +38,15 @@ from django.forms import ModelForm, Form
from django.contrib.auth.forms import ReadOnlyPasswordHashField from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.validators import MinLengthValidator from django.core.validators import MinLengthValidator
from django.utils import timezone from django.utils import timezone
from django.contrib.auth.models import Group, Permission
from preferences.models import OptionalUser 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 .models import Ban, Adherent, Club
from re2o.utils import remove_user_room from re2o.utils import remove_user_room
from re2o.field_permissions import FieldPermissionFormMixin
NOW = timezone.now() NOW = timezone.now()
@ -253,7 +256,7 @@ class MassArchiveForm(forms.Form):
utilisateurs dont la fin d'accès se situe dans le futur !") 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é """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 pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis""" avec des label plus jolis"""
@ -278,6 +281,7 @@ class AdherentForm(ModelForm):
'school', 'school',
'comment', 'comment',
'room', 'room',
'shell',
'telephone', 'telephone',
] ]
@ -306,7 +310,7 @@ class AdherentForm(ModelForm):
return return
class ClubForm(ModelForm): class ClubForm(FieldPermissionFormMixin, ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé """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 pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis""" avec des label plus jolis"""
@ -330,6 +334,7 @@ class ClubForm(ModelForm):
'comment', 'comment',
'room', 'room',
'telephone', 'telephone',
'shell',
] ]
def clean_telephone(self): def clean_telephone(self):
@ -344,41 +349,6 @@ class ClubForm(ModelForm):
return telephone 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): class ClubAdminandMembersForm(ModelForm):
"""Permet d'éditer la liste des membres et des administrateurs """Permet d'éditer la liste des membres et des administrateurs
d'un club""" d'un club"""
@ -440,6 +410,23 @@ class StateForm(ModelForm):
super(StateForm, self).__init__(*args, prefix=prefix, **kwargs) 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): class SchoolForm(ModelForm):
"""Edition, creation d'un école""" """Edition, creation d'un école"""
class Meta: class Meta:
@ -455,14 +442,20 @@ class SchoolForm(ModelForm):
class ListRightForm(ModelForm): class ListRightForm(ModelForm):
"""Edition, d'un groupe , équivalent à un droit """Edition, d'un groupe , équivalent à un droit
Ne peremet pas d'editer le gid, car il sert de primary key""" 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: class Meta:
model = ListRight model = ListRight
fields = ['listright', 'details'] fields = ['name', 'unix_name', 'permissions', 'details']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ListRightForm, self).__init__(*args, prefix=prefix, **kwargs) 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): class NewListRightForm(ListRightForm):
@ -479,45 +472,35 @@ class NewListRightForm(ListRightForm):
class DelListRightForm(Form): class DelListRightForm(Form):
"""Suppression d'un ou plusieurs groupes""" """Suppression d'un ou plusieurs groupes"""
listrights = forms.ModelMultipleChoiceField( listrights = forms.ModelMultipleChoiceField(
queryset=ListRight.objects.all(), queryset=ListRight.objects.none(),
label="Droits actuels", label="Droits actuels",
widget=forms.CheckboxSelectMultiple 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): class DelSchoolForm(Form):
"""Suppression d'une ou plusieurs écoles""" """Suppression d'une ou plusieurs écoles"""
schools = forms.ModelMultipleChoiceField( schools = forms.ModelMultipleChoiceField(
queryset=School.objects.all(), queryset=School.objects.none(),
label="Etablissements actuels", label="Etablissements actuels",
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple
) )
class RightForm(ModelForm):
"""Assignation d'un droit à un user"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) instances = kwargs.pop('instances', None)
super(RightForm, self).__init__(*args, prefix=prefix, **kwargs) super(DelSchoolForm, self).__init__(*args, **kwargs)
self.fields['right'].label = 'Droit' if instances:
self.fields['right'].empty_label = "Choisir un nouveau droit" self.fields['schools'].queryset = instances
else:
class Meta: self.fields['schools'].queryset = School.objects.all()
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)
class BanForm(ModelForm): class BanForm(ModelForm):
@ -531,14 +514,6 @@ class BanForm(ModelForm):
model = Ban model = Ban
exclude = ['user'] 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): class WhitelistForm(ModelForm):
"""Creation, edition d'un objet whitelist""" """Creation, edition d'un objet whitelist"""
@ -550,11 +525,3 @@ class WhitelistForm(ModelForm):
class Meta: class Meta:
model = Whitelist model = Whitelist
exclude = ['user'] 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

View 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

View 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'),
),
]

View 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),
]

View 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),
]

View 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'),
),
]

View 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'),)},
),
]

Some files were not shown because too many files have changed in this diff Show more