8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-26 08:53:46 +00:00

Merge branch 'master' into massive_use_bft_tag

This commit is contained in:
Maël Kervella 2017-10-15 15:05:40 +00:00
commit 25d460cb91
19 changed files with 1719 additions and 619 deletions

View file

@ -19,7 +19,10 @@
# 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.
"""
Urls de l'application logs, pointe vers les fonctions de views.
Inclu dans le re2o.urls
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
@ -29,7 +32,9 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.index, name='index'), url(r'^$', views.index, name='index'),
url(r'^stats_logs$', views.stats_logs, name='stats-logs'), url(r'^stats_logs$', views.stats_logs, name='stats-logs'),
url(r'^revert_action/(?P<revision_id>[0-9]+)$', views.revert_action, name='revert-action'), url(r'^revert_action/(?P<revision_id>[0-9]+)$',
views.revert_action,
name='revert-action'),
url(r'^stats_general/$', views.stats_general, name='stats-general'), url(r'^stats_general/$', views.stats_general, name='stats-general'),
url(r'^stats_models/$', views.stats_models, name='stats-models'), url(r'^stats_models/$', views.stats_models, name='stats-models'),
url(r'^stats_users/$', views.stats_users, name='stats-users'), url(r'^stats_users/$', views.stats_users, name='stats-users'),

View file

@ -23,62 +23,67 @@
# App de gestion des statistiques pour re2o # App de gestion des statistiques pour re2o
# Gabriel Détraz # Gabriel Détraz
# Gplv2 # Gplv2
"""
Vues des logs et statistiques générales.
La vue index générale affiche une selection des dernières actions,
classées selon l'importance, avec date, et user formatés.
Stats_logs renvoie l'ensemble des logs.
Les autres vues sont thématiques, ensemble des statistiques et du
nombre d'objets par models, nombre d'actions par user, etc
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.http import HttpResponse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404
from django.template.context_processors import csrf
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.template import Context, RequestContext, loader
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, permission_required
from django.db.models import ProtectedError
from django.forms import ValidationError
from django.db import transaction
from django.db.models import Count from django.db.models import Count
from reversion.models import Revision from reversion.models import Revision
from reversion.models import Version, ContentType from reversion.models import Version, ContentType
from users.models import User, ServiceUser, Right, School, ListRight, ListShell, Ban, Whitelist from users.models import User, ServiceUser, Right, School, ListRight, ListShell
from users.models import all_has_access, all_whitelisted, all_baned, all_adherent from users.models import Ban, Whitelist
from cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation from cotisations.models import Facture, Vente, Article, Banque, Paiement
from machines.models import Machine, MachineType, IpType, Extension, Interface, Domain, IpList from cotisations.models import Cotisation
from machines.views import all_active_assigned_interfaces_count, all_active_interfaces_count from machines.models import Machine, MachineType, IpType, Extension, Interface
from machines.models import Domain, IpList
from topologie.models import Switch, Port, Room from topologie.models import Switch, Port, Room
from preferences.models import GeneralOption from preferences.models import GeneralOption
from re2o.views import form
from django.utils import timezone from re2o.utils import all_whitelisted, all_baned, all_has_access, all_adherent
from dateutil.relativedelta import relativedelta from re2o.utils import all_active_assigned_interfaces_count
from re2o.utils import all_active_interfaces_count
STATS_DICT = { STATS_DICT = {
0 : ["Tout", 36], 0: ["Tout", 36],
1 : ["1 mois", 1], 1: ["1 mois", 1],
2 : ["2 mois", 2], 2: ["2 mois", 2],
3 : ["6 mois", 6], 3: ["6 mois", 6],
4 : ["1 an", 12], 4: ["1 an", 12],
5 : ["2 an", 24], 5: ["2 an", 24],
} }
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render(request, template, c)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index(request): def index(request):
options, created = GeneralOption.objects.get_or_create() """Affiche les logs affinés, date reformatées, selectionne
les event importants (ajout de droits, ajout de ban/whitelist)"""
options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
# The types of content kept for display # The types of content kept for display
content_type_filter = ['ban', 'whitelist', 'vente', 'interface', 'user'] content_type_filter = ['ban', 'whitelist', 'vente', 'interface', 'user']
# Select only wanted versions # Select only wanted versions
versions = Version.objects.filter(content_type__in=ContentType.objects.filter(model__in=content_type_filter)).order_by('revision__date_created').reverse().select_related('revision') versions = Version.objects.filter(
content_type__in=ContentType.objects.filter(
model__in=content_type_filter
)
).order_by('revision__date_created').reverse().select_related('revision')
paginator = Paginator(versions, pagination_number) paginator = Paginator(versions, pagination_number)
page = request.GET.get('page') page = request.GET.get('page')
try: try:
@ -95,30 +100,38 @@ def index(request):
# Items to remove later because invalid # Items to remove later because invalid
to_remove = [] to_remove = []
# Parse every item (max = pagination_number) # Parse every item (max = pagination_number)
for i in range( len( versions.object_list ) ): for i in range(len(versions.object_list)):
if versions.object_list[i].object : if versions.object_list[i].object:
v = versions.object_list[i] version = versions.object_list[i]
versions.object_list[i] = { versions.object_list[i] = {
'rev_id' : v.revision.id, 'rev_id': version.revision.id,
'comment': v.revision.comment, 'comment': version.revision.comment,
'datetime': v.revision.date_created.strftime('%d/%m/%y %H:%M:%S'), 'datetime': version.revision.date_created.strftime(
'username': v.revision.user.get_username() if v.revision.user else '?', '%d/%m/%y %H:%M:%S'
'user_id': v.revision.user_id, ),
'version': v } 'username':
else : version.revision.user.get_username()
to_remove.insert(0,i) if version.revision.user else '?',
'user_id': version.revision.user_id,
'version': version}
else:
to_remove.insert(0, i)
# Remove all tagged invalid items # Remove all tagged invalid items
for i in to_remove : for i in to_remove:
versions.object_list.pop(i) versions.object_list.pop(i)
return render(request, 'logs/index.html', {'versions_list': versions}) return render(request, 'logs/index.html', {'versions_list': versions})
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def stats_logs(request): def stats_logs(request):
options, created = GeneralOption.objects.get_or_create() """Affiche l'ensemble des logs et des modifications sur les objets,
classés par date croissante, en vrac"""
options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
revisions = Revision.objects.all().order_by('date_created').reverse().select_related('user').prefetch_related('version_set__object') revisions = Revision.objects.all().order_by('date_created')\
.reverse().select_related('user')\
.prefetch_related('version_set__object')
paginator = Paginator(revisions, pagination_number) paginator = Paginator(revisions, pagination_number)
page = request.GET.get('page') page = request.GET.get('page')
try: try:
@ -129,7 +142,10 @@ def stats_logs(request):
except EmptyPage: except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results. # If page is out of range (e.g. 9999), deliver last page of results.
revisions = paginator.page(paginator.num_pages) revisions = paginator.page(paginator.num_pages)
return render(request, 'logs/stats_logs.html', {'revisions_list': revisions}) return render(request, 'logs/stats_logs.html', {
'revisions_list': revisions
})
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
@ -138,36 +154,64 @@ def revert_action(request, revision_id):
try: try:
revision = Revision.objects.get(id=revision_id) revision = Revision.objects.get(id=revision_id)
except Revision.DoesNotExist: except Revision.DoesNotExist:
messages.error(request, u"Revision inexistante" ) messages.error(request, u"Revision inexistante")
if request.method == "POST": if request.method == "POST":
revision.revert() revision.revert()
messages.success(request, "L'action a été supprimée") messages.success(request, "L'action a été supprimée")
return redirect("/logs/") return redirect("/logs/")
return form({'objet': revision, 'objet_name': revision.__class__.__name__ }, 'logs/delete.html', request) return form({
'objet': revision,
'objet_name': revision.__class__.__name__
}, 'logs/delete.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def stats_general(request): def stats_general(request):
all_active_users = User.objects.filter(state=User.STATE_ACTIVE) """Statistiques générales affinées sur les ip, activées, utilisées par
ip = dict() range, et les statistiques générales sur les users : users actifs,
cotisants, activés, archivés, etc"""
ip_dict = dict()
for ip_range in IpType.objects.all(): for ip_range in IpType.objects.all():
all_ip = IpList.objects.filter(ip_type=ip_range) all_ip = IpList.objects.filter(ip_type=ip_range)
used_ip = Interface.objects.filter(ipv4__in=all_ip).count() used_ip = Interface.objects.filter(ipv4__in=all_ip).count()
active_ip = all_active_assigned_interfaces_count().filter(ipv4__in=IpList.objects.filter(ip_type=ip_range)).count() active_ip = all_active_assigned_interfaces_count().filter(
ip[ip_range] = [ip_range, all_ip.count(), used_ip, active_ip, all_ip.count()-used_ip] ipv4__in=IpList.objects.filter(ip_type=ip_range)
).count()
ip_dict[ip_range] = [ip_range, all_ip.count(),
used_ip, active_ip, all_ip.count()-used_ip]
stats = [ stats = [
[["Categorie", "Nombre d'utilisateurs"], { [["Categorie", "Nombre d'utilisateurs"], {
'active_users' : ["Users actifs", User.objects.filter(state=User.STATE_ACTIVE).count()], 'active_users': [
'inactive_users' : ["Users désactivés", User.objects.filter(state=User.STATE_DISABLED).count()], "Users actifs",
'archive_users' : ["Users archivés", User.objects.filter(state=User.STATE_ARCHIVE).count()], User.objects.filter(state=User.STATE_ACTIVE).count()],
'adherent_users' : ["Adhérents à l'association", all_adherent().count()], 'inactive_users': [
'connexion_users' : ["Utilisateurs bénéficiant d'une connexion", all_has_access().count()], "Users désactivés",
'ban_users' : ["Utilisateurs bannis", all_baned().count()], User.objects.filter(state=User.STATE_DISABLED).count()],
'whitelisted_user' : ["Utilisateurs bénéficiant d'une connexion gracieuse", all_whitelisted().count()], 'archive_users': [
'actives_interfaces' : ["Interfaces actives (ayant accès au reseau)", all_active_interfaces_count().count()], "Users archivés",
'actives_assigned_interfaces' : ["Interfaces actives et assignées ipv4", all_active_assigned_interfaces_count().count()] User.objects.filter(state=User.STATE_ARCHIVE).count()],
'adherent_users': [
"Adhérents à l'association",
all_adherent().count()],
'connexion_users': [
"Utilisateurs bénéficiant d'une connexion",
all_has_access().count()],
'ban_users': [
"Utilisateurs bannis",
all_baned().count()],
'whitelisted_user': [
"Utilisateurs bénéficiant d'une connexion gracieuse",
all_whitelisted().count()],
'actives_interfaces': [
"Interfaces actives (ayant accès au reseau)",
all_active_interfaces_count().count()],
'actives_assigned_interfaces': [
"Interfaces actives et assignées ipv4",
all_active_assigned_interfaces_count().count()]
}], }],
[["Range d'ip", "Nombre d'ip totales", "Ip assignées", "Ip assignées à une machine active", "Ip non assignées"] ,ip] [["Range d'ip", "Nombre d'ip totales", "Ip assignées",
"Ip assignées à une machine active", "Ip non assignées"], ip_dict]
] ]
return render(request, 'logs/stats_general.html', {'stats_list': stats}) return render(request, 'logs/stats_general.html', {'stats_list': stats})
@ -175,84 +219,117 @@ def stats_general(request):
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def stats_models(request): def stats_models(request):
all_active_users = User.objects.filter(state=User.STATE_ACTIVE) """Statistiques générales, affiche les comptages par models:
nombre d'users, d'écoles, de droits, de bannissements,
de factures, de ventes, de banque, de machines, etc"""
stats = { stats = {
'Users' : { 'Users': {
'users' : [User.PRETTY_NAME, User.objects.count()], 'users': [User.PRETTY_NAME, User.objects.count()],
'serviceuser' : [ServiceUser.PRETTY_NAME, ServiceUser.objects.count()], 'serviceuser': [ServiceUser.PRETTY_NAME,
'right' : [Right.PRETTY_NAME, Right.objects.count()], ServiceUser.objects.count()],
'school' : [School.PRETTY_NAME, School.objects.count()], 'right': [Right.PRETTY_NAME, Right.objects.count()],
'listright' : [ListRight.PRETTY_NAME, ListRight.objects.count()], 'school': [School.PRETTY_NAME, School.objects.count()],
'listshell' : [ListShell.PRETTY_NAME, ListShell.objects.count()], 'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()],
'ban' : [Ban.PRETTY_NAME, Ban.objects.count()], 'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()],
'whitelist' : [Whitelist.PRETTY_NAME, Whitelist.objects.count()] 'ban': [Ban.PRETTY_NAME, Ban.objects.count()],
'whitelist': [Whitelist.PRETTY_NAME, Whitelist.objects.count()]
}, },
'Cotisations' : { 'Cotisations': {
'factures' : [Facture.PRETTY_NAME, Facture.objects.count()], 'factures': [Facture.PRETTY_NAME, Facture.objects.count()],
'vente' : [Vente.PRETTY_NAME, Vente.objects.count()], 'vente': [Vente.PRETTY_NAME, Vente.objects.count()],
'cotisation' : [Cotisation.PRETTY_NAME, Cotisation.objects.count()], 'cotisation': [Cotisation.PRETTY_NAME, Cotisation.objects.count()],
'article' : [Article.PRETTY_NAME, Article.objects.count()], 'article': [Article.PRETTY_NAME, Article.objects.count()],
'banque' : [Banque.PRETTY_NAME, Banque.objects.count()], 'banque': [Banque.PRETTY_NAME, Banque.objects.count()],
'cotisation' : [Cotisation.PRETTY_NAME, Cotisation.objects.count()],
}, },
'Machines' : { 'Machines': {
'machine' : [Machine.PRETTY_NAME, Machine.objects.count()], 'machine': [Machine.PRETTY_NAME, Machine.objects.count()],
'typemachine' : [MachineType.PRETTY_NAME, MachineType.objects.count()], 'typemachine': [MachineType.PRETTY_NAME,
'typeip' : [IpType.PRETTY_NAME, IpType.objects.count()], MachineType.objects.count()],
'extension' : [Extension.PRETTY_NAME, Extension.objects.count()], 'typeip': [IpType.PRETTY_NAME, IpType.objects.count()],
'interface' : [Interface.PRETTY_NAME, Interface.objects.count()], 'extension': [Extension.PRETTY_NAME, Extension.objects.count()],
'alias' : [Domain.PRETTY_NAME, Domain.objects.exclude(cname=None).count()], 'interface': [Interface.PRETTY_NAME, Interface.objects.count()],
'iplist' : [IpList.PRETTY_NAME, IpList.objects.count()], 'alias': [Domain.PRETTY_NAME,
Domain.objects.exclude(cname=None).count()],
'iplist': [IpList.PRETTY_NAME, IpList.objects.count()],
}, },
'Topologie' : { 'Topologie': {
'switch' : [Switch.PRETTY_NAME, Switch.objects.count()], 'switch': [Switch.PRETTY_NAME, Switch.objects.count()],
'port' : [Port.PRETTY_NAME, Port.objects.count()], 'port': [Port.PRETTY_NAME, Port.objects.count()],
'chambre' : [Room.PRETTY_NAME, Room.objects.count()], 'chambre': [Room.PRETTY_NAME, Room.objects.count()],
}, },
'Actions effectuées sur la base' : 'Actions effectuées sur la base':
{ {
'revision' : ["Nombre d'actions", Revision.objects.count()], 'revision': ["Nombre d'actions", Revision.objects.count()],
}, },
} }
return render(request, 'logs/stats_models.html', {'stats_list': stats}) return render(request, 'logs/stats_models.html', {'stats_list': stats})
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def stats_users(request): def stats_users(request):
"""Affiche les statistiques base de données aggrégées par user :
nombre de machines par user, d'etablissements par user,
de moyens de paiements par user, de banque par user,
de bannissement par user, etc"""
onglet = request.GET.get('onglet') onglet = request.GET.get('onglet')
try: try:
search_field = STATS_DICT[onglet] _search_field = STATS_DICT[onglet]
except: except KeyError:
search_field = STATS_DICT[0] _search_field = STATS_DICT[0]
onglet = 0 onglet = 0
start_date = timezone.now() + relativedelta(months=-search_field[1])
stats = { stats = {
'Utilisateur' : { 'Utilisateur': {
'Machines' : User.objects.annotate(num=Count('machine')).order_by('-num')[:10], 'Machines': User.objects.annotate(
'Facture' : User.objects.annotate(num=Count('facture')).order_by('-num')[:10], num=Count('machine')
'Bannissement' : User.objects.annotate(num=Count('ban')).order_by('-num')[:10], ).order_by('-num')[:10],
'Accès gracieux' : User.objects.annotate(num=Count('whitelist')).order_by('-num')[:10], 'Facture': User.objects.annotate(
'Droits' : User.objects.annotate(num=Count('right')).order_by('-num')[:10], num=Count('facture')
).order_by('-num')[:10],
'Bannissement': User.objects.annotate(
num=Count('ban')
).order_by('-num')[:10],
'Accès gracieux': User.objects.annotate(
num=Count('whitelist')
).order_by('-num')[:10],
'Droits': User.objects.annotate(
num=Count('right')
).order_by('-num')[:10],
}, },
'Etablissement' : { 'Etablissement': {
'Utilisateur' : School.objects.annotate(num=Count('user')).order_by('-num')[:10], 'Utilisateur': School.objects.annotate(
num=Count('user')
).order_by('-num')[:10],
}, },
'Moyen de paiement' : { 'Moyen de paiement': {
'Utilisateur' : Paiement.objects.annotate(num=Count('facture')).order_by('-num')[:10], 'Utilisateur': Paiement.objects.annotate(
num=Count('facture')
).order_by('-num')[:10],
}, },
'Banque' : { 'Banque': {
'Utilisateur' : Banque.objects.annotate(num=Count('facture')).order_by('-num')[:10], 'Utilisateur': Banque.objects.annotate(
num=Count('facture')
).order_by('-num')[:10],
}, },
} }
return render(request, 'logs/stats_users.html', {'stats_list': stats, 'stats_dict' : STATS_DICT, 'active_field': onglet}) return render(request, 'logs/stats_users.html', {
'stats_list': stats,
'stats_dict': STATS_DICT,
'active_field': onglet
})
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def stats_actions(request): def stats_actions(request):
onglet = request.GET.get('onglet') """Vue qui affiche les statistiques de modifications d'objets par
utilisateurs.
Affiche le nombre de modifications aggrégées par utilisateurs"""
stats = { stats = {
'Utilisateur' : { 'Utilisateur': {
'Action' : User.objects.annotate(num=Count('revision')).order_by('-num')[:40], 'Action': User.objects.annotate(
num=Count('revision')
).order_by('-num')[:40],
}, },
} }
return render(request, 'logs/stats_users.html', {'stats_list': stats}) return render(request, 'logs/stats_users.html', {'stats_list': stats})

View file

@ -53,30 +53,10 @@ from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasF
from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text, OuverturePortList, OuverturePort from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text, OuverturePortList, OuverturePort
from users.models import User from users.models import User
from users.models import all_has_access
from preferences.models import GeneralOption, OptionalMachine from preferences.models import GeneralOption, OptionalMachine
from re2o.templatetags.massive_bootstrap_form import hidden_id, input_id from re2o.templatetags.massive_bootstrap_form import hidden_id, input_id
from re2o.utils import all_active_assigned_interfaces, all_has_access
def all_active_interfaces(): from re2o.views import form
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
return Interface.objects.filter(machine__in=Machine.objects.filter(user__in=all_has_access()).filter(active=True)).select_related('domain').select_related('machine').select_related('type').select_related('ipv4').select_related('domain__extension').select_related('ipv4__ip_type').distinct()
def all_active_assigned_interfaces():
""" Renvoie l'ensemble des machines qui ont une ipv4 assignées et disposant de l'accès internet"""
return all_active_interfaces().filter(ipv4__isnull=False)
def all_active_interfaces_count():
""" Version light seulement pour compter"""
return Interface.objects.filter(machine__in=Machine.objects.filter(user__in=all_has_access()).filter(active=True))
def all_active_assigned_interfaces_count():
""" Version light seulement pour compter"""
return all_active_interfaces_count().filter(ipv4__isnull=False)
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render(request, template, c)
def f_type_id( is_type_tt ): def f_type_id( is_type_tt ):
""" The id that will be used in HTML to store the value of the field """ The id that will be used in HTML to store the value of the field

View file

@ -20,35 +20,53 @@
# 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.
"""
Classes admin pour les models de preferences
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import OptionalUser, OptionalMachine, OptionalTopologie, GeneralOption, Service, AssoOption, MailMessageOption from .models import OptionalUser, OptionalMachine, OptionalTopologie
from .models import GeneralOption, Service, AssoOption, MailMessageOption
class OptionalUserAdmin(VersionAdmin): class OptionalUserAdmin(VersionAdmin):
"""Class admin options user"""
pass pass
class OptionalTopologieAdmin(VersionAdmin): class OptionalTopologieAdmin(VersionAdmin):
"""Class admin options topologie"""
pass pass
class OptionalMachineAdmin(VersionAdmin): class OptionalMachineAdmin(VersionAdmin):
"""Class admin options machines"""
pass pass
class GeneralOptionAdmin(VersionAdmin): class GeneralOptionAdmin(VersionAdmin):
"""Class admin options générales"""
pass pass
class ServiceAdmin(VersionAdmin): class ServiceAdmin(VersionAdmin):
"""Class admin gestion des services de la page d'accueil"""
pass pass
class AssoOptionAdmin(VersionAdmin): class AssoOptionAdmin(VersionAdmin):
"""Class admin options de l'asso"""
pass pass
class MailMessageOptionAdmin(VersionAdmin): class MailMessageOptionAdmin(VersionAdmin):
"""Class admin options mail"""
pass pass
admin.site.register(OptionalUser, OptionalUserAdmin) admin.site.register(OptionalUser, OptionalUserAdmin)
admin.site.register(OptionalMachine, OptionalMachineAdmin) admin.site.register(OptionalMachine, OptionalMachineAdmin)
admin.site.register(OptionalTopologie, OptionalTopologieAdmin) admin.site.register(OptionalTopologie, OptionalTopologieAdmin)

View file

@ -19,71 +19,116 @@
# 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.
"""
Formulaire d'edition des réglages : user, machine, topologie, asso...
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.forms import ModelForm, Form, ValidationError from django.forms import ModelForm, Form
from django import forms from django import forms
from .models import OptionalUser, OptionalMachine, OptionalTopologie, GeneralOption, AssoOption, MailMessageOption, Service from .models import OptionalUser, OptionalMachine, OptionalTopologie
from django.db.models import Q from .models import GeneralOption, AssoOption, MailMessageOption, Service
class EditOptionalUserForm(ModelForm): class EditOptionalUserForm(ModelForm):
"""Formulaire d'édition des options de l'user. (solde, telephone..)"""
class Meta: class Meta:
model = OptionalUser model = OptionalUser
fields = '__all__' fields = '__all__'
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(EditOptionalUserForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditOptionalUserForm, self).__init__(
self.fields['is_tel_mandatory'].label = 'Exiger un numéro de téléphone' *args,
self.fields['user_solde'].label = 'Activation du solde pour les utilisateurs' prefix=prefix,
**kwargs
)
self.fields['is_tel_mandatory'].label = 'Exiger un numéro de\
téléphone'
self.fields['user_solde'].label = 'Activation du solde pour\
les utilisateurs'
class EditOptionalMachineForm(ModelForm): class EditOptionalMachineForm(ModelForm):
"""Options machines (max de machines, etc)"""
class Meta: class Meta:
model = OptionalMachine model = OptionalMachine
fields = '__all__' fields = '__all__'
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(EditOptionalMachineForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditOptionalMachineForm, self).__init__(
self.fields['password_machine'].label = "Possibilité d'attribuer un mot de passe par interface" *args,
self.fields['max_lambdauser_interfaces'].label = "Maximum d'interfaces autorisées pour un user normal" prefix=prefix,
self.fields['max_lambdauser_aliases'].label = "Maximum d'alias dns autorisés pour un user normal" **kwargs
)
self.fields['password_machine'].label = "Possibilité d'attribuer\
un mot de passe par interface"
self.fields['max_lambdauser_interfaces'].label = "Maximum\
d'interfaces autorisées pour un user normal"
self.fields['max_lambdauser_aliases'].label = "Maximum d'alias\
dns autorisés pour un user normal"
class EditOptionalTopologieForm(ModelForm): class EditOptionalTopologieForm(ModelForm):
"""Options de topologie, formulaire d'edition (vlan par default etc)"""
class Meta: class Meta:
model = OptionalTopologie model = OptionalTopologie
fields = '__all__' fields = '__all__'
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(EditOptionalTopologieForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditOptionalTopologieForm, self).__init__(
self.fields['vlan_decision_ok'].label = "Vlan où placer les machines après acceptation RADIUS" *args,
self.fields['vlan_decision_nok'].label = "Vlan où placer les machines après rejet RADIUS" prefix=prefix,
**kwargs
)
self.fields['vlan_decision_ok'].label = "Vlan où placer les\
machines après acceptation RADIUS"
self.fields['vlan_decision_nok'].label = "Vlan où placer les\
machines après rejet RADIUS"
class EditGeneralOptionForm(ModelForm): class EditGeneralOptionForm(ModelForm):
"""Options générales (affichages de résultats de recherche, etc)"""
class Meta: class Meta:
model = GeneralOption model = GeneralOption
fields = '__all__' fields = '__all__'
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(EditGeneralOptionForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditGeneralOptionForm, self).__init__(
self.fields['search_display_page'].label = 'Resultats affichés dans une recherche' *args,
self.fields['pagination_number'].label = 'Items par page, taille normale (ex users)' prefix=prefix,
self.fields['pagination_large_number'].label = 'Items par page, taille élevée (machines)' **kwargs
self.fields['req_expire_hrs'].label = 'Temps avant expiration du lien de reinitialisation de mot de passe (en heures)' )
self.fields['search_display_page'].label = 'Resultats\
affichés dans une recherche'
self.fields['pagination_number'].label = 'Items par page,\
taille normale (ex users)'
self.fields['pagination_large_number'].label = 'Items par page,\
taille élevée (machines)'
self.fields['req_expire_hrs'].label = 'Temps avant expiration du lien\
de reinitialisation de mot de passe (en heures)'
self.fields['site_name'].label = 'Nom du site web' self.fields['site_name'].label = 'Nom du site web'
self.fields['email_from'].label = 'Adresse mail d\'expedition automatique' self.fields['email_from'].label = "Adresse mail d\
'expedition automatique"
class EditAssoOptionForm(ModelForm): class EditAssoOptionForm(ModelForm):
"""Options de l'asso (addresse, telephone, etc)"""
class Meta: class Meta:
model = AssoOption model = AssoOption
fields = '__all__' fields = '__all__'
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(EditAssoOptionForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditAssoOptionForm, self).__init__(
*args,
prefix=prefix,
**kwargs
)
self.fields['name'].label = 'Nom de l\'asso' self.fields['name'].label = 'Nom de l\'asso'
self.fields['siret'].label = 'SIRET' self.fields['siret'].label = 'SIRET'
self.fields['adresse1'].label = 'Adresse (ligne 1)' self.fields['adresse1'].label = 'Adresse (ligne 1)'
@ -91,20 +136,31 @@ class EditAssoOptionForm(ModelForm):
self.fields['contact'].label = 'Email de contact' self.fields['contact'].label = 'Email de contact'
self.fields['telephone'].label = 'Numéro de téléphone' self.fields['telephone'].label = 'Numéro de téléphone'
self.fields['pseudo'].label = 'Pseudo d\'usage' self.fields['pseudo'].label = 'Pseudo d\'usage'
self.fields['utilisateur_asso'].label = 'Compte utilisé pour faire les modifications depuis /admin' self.fields['utilisateur_asso'].label = 'Compte utilisé pour\
faire les modifications depuis /admin'
class EditMailMessageOptionForm(ModelForm): class EditMailMessageOptionForm(ModelForm):
"""Formulaire d'edition des messages de bienvenue personnalisés"""
class Meta: class Meta:
model = MailMessageOption model = MailMessageOption
fields = '__all__' fields = '__all__'
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(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditMailMessageOptionForm, self).__init__(
self.fields['welcome_mail_fr'].label = 'Message dans le mail de bienvenue en français' *args,
self.fields['welcome_mail_en'].label = 'Message dans le mail de bienvenue en anglais' prefix=prefix,
**kwargs
)
self.fields['welcome_mail_fr'].label = 'Message dans le\
mail de bienvenue en français'
self.fields['welcome_mail_en'].label = 'Message dans le\
mail de bienvenue en anglais'
class ServiceForm(ModelForm): class ServiceForm(ModelForm):
"""Edition, ajout de services sur la page d'accueil"""
class Meta: class Meta:
model = Service model = Service
fields = '__all__' fields = '__all__'
@ -113,6 +169,11 @@ class ServiceForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs) super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelServiceForm(Form):
services = forms.ModelMultipleChoiceField(queryset=Service.objects.all(), label="Enregistrements service actuels", widget=forms.CheckboxSelectMultiple)
class DelServiceForm(Form):
"""Suppression de services sur la page d'accueil"""
services = forms.ModelMultipleChoiceField(
queryset=Service.objects.all(),
label="Enregistrements service actuels",
widget=forms.CheckboxSelectMultiple
)

View file

@ -20,26 +20,38 @@
# 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.
"""
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 from cotisations.models import Paiement
from machines.models import Vlan
class OptionalUser(models.Model): class OptionalUser(models.Model):
"""Options pour l'user : obligation ou nom du telephone,
activation ou non du solde, autorisation du negatif, fingerprint etc"""
PRETTY_NAME = "Options utilisateur" PRETTY_NAME = "Options utilisateur"
is_tel_mandatory = models.BooleanField(default=True) is_tel_mandatory = models.BooleanField(default=True)
user_solde = models.BooleanField(default=False) user_solde = models.BooleanField(default=False)
solde_negatif = models.DecimalField(max_digits=5, decimal_places=2, default=0) solde_negatif = models.DecimalField(
max_digits=5,
decimal_places=2,
default=0
)
gpg_fingerprint = models.BooleanField(default=True) gpg_fingerprint = models.BooleanField(default=True)
def clean(self): def clean(self):
"""Creation du mode de paiement par solde"""
if self.user_solde: if self.user_solde:
Paiement.objects.get_or_create(moyen="Solde") Paiement.objects.get_or_create(moyen="Solde")
class OptionalMachine(models.Model): class OptionalMachine(models.Model):
"""Options pour les machines : maximum de machines ou d'alias par user
sans droit, activation de l'ipv6"""
PRETTY_NAME = "Options machines" PRETTY_NAME = "Options machines"
password_machine = models.BooleanField(default=False) password_machine = models.BooleanField(default=False)
@ -47,21 +59,43 @@ 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 OptionalTopologie(models.Model): class OptionalTopologie(models.Model):
"""Reglages pour la topologie : mode d'accès radius, vlan où placer
les machines en accept ou reject"""
PRETTY_NAME = "Options topologie" PRETTY_NAME = "Options topologie"
MACHINE = 'MACHINE' MACHINE = 'MACHINE'
DEFINED = 'DEFINED' DEFINED = 'DEFINED'
CHOICE_RADIUS = ( CHOICE_RADIUS = (
(MACHINE, 'Sur le vlan de la plage ip machine'), (MACHINE, 'Sur le vlan de la plage ip machine'),
(DEFINED, 'Prédéfini dans "Vlan où placer les machines après acceptation RADIUS"'), (DEFINED, 'Prédéfini dans "Vlan où placer les machines\
après acceptation RADIUS"'),
)
radius_general_policy = models.CharField(
max_length=32,
choices=CHOICE_RADIUS,
default='DEFINED'
)
vlan_decision_ok = models.OneToOneField(
'machines.Vlan',
on_delete=models.PROTECT,
related_name='decision_ok',
blank=True,
null=True
)
vlan_decision_nok = models.OneToOneField(
'machines.Vlan',
on_delete=models.PROTECT,
related_name='decision_nok',
blank=True,
null=True
) )
radius_general_policy = models.CharField(max_length=32, choices=CHOICE_RADIUS, default='DEFINED')
vlan_decision_ok = models.OneToOneField('machines.Vlan', on_delete=models.PROTECT, related_name='decision_ok', blank=True, null=True)
vlan_decision_nok = models.OneToOneField('machines.Vlan', on_delete=models.PROTECT, related_name='decision_nok', blank=True, null=True)
class GeneralOption(models.Model): class GeneralOption(models.Model):
"""Options générales : nombre de resultats par page, nom du site,
temps les liens sont valides"""
PRETTY_NAME = "Options générales" PRETTY_NAME = "Options générales"
search_display_page = models.IntegerField(default=15) search_display_page = models.IntegerField(default=15)
@ -71,7 +105,10 @@ 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 Service(models.Model): class Service(models.Model):
"""Liste des services affichés sur la page d'accueil : url, description,
image et nom"""
name = models.CharField(max_length=32) name = models.CharField(max_length=32)
url = models.URLField() url = models.URLField()
description = models.TextField() description = models.TextField()
@ -80,21 +117,32 @@ class Service(models.Model):
def __str__(self): def __str__(self):
return str(self.name) return str(self.name)
class AssoOption(models.Model): class AssoOption(models.Model):
"""Options générales de l'asso : siret, addresse, nom, etc"""
PRETTY_NAME = "Options de l'association" PRETTY_NAME = "Options de l'association"
name = models.CharField(default="Association réseau école machin", max_length=256) name = models.CharField(
default="Association réseau école machin",
max_length=256
)
siret = models.CharField(default="00000000000000", max_length=32) siret = models.CharField(default="00000000000000", max_length=32)
adresse1 = models.CharField(default="1 Rue de exemple", max_length=128) adresse1 = models.CharField(default="1 Rue de exemple", max_length=128)
adresse2 = models.CharField(default="94230 Cachan", max_length=128) adresse2 = models.CharField(default="94230 Cachan", max_length=128)
contact = models.EmailField(default="contact@example.org") contact = models.EmailField(default="contact@example.org")
telephone = models.CharField(max_length=15, default="0000000000") telephone = models.CharField(max_length=15, default="0000000000")
pseudo = models.CharField(default="Asso", max_length=32) pseudo = models.CharField(default="Asso", max_length=32)
utilisateur_asso = models.OneToOneField('users.User', on_delete=models.PROTECT, blank=True, null=True) utilisateur_asso = models.OneToOneField(
'users.User',
on_delete=models.PROTECT,
blank=True,
null=True
)
class MailMessageOption(models.Model): class MailMessageOption(models.Model):
"""Reglages, mail de bienvenue et autre"""
PRETTY_NAME = "Options de corps de mail" PRETTY_NAME = "Options de corps de mail"
welcome_mail_fr = models.TextField(default="") welcome_mail_fr = models.TextField(default="")
welcome_mail_en = models.TextField(default="") welcome_mail_en = models.TextField(default="")

View file

@ -19,6 +19,9 @@
# 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.
"""
Urls de l'application preferences, pointant vers les fonctions de views
"""
from __future__ import unicode_literals from __future__ import unicode_literals
@ -28,15 +31,47 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^edit_options/(?P<section>OptionalUser)$', views.edit_options, name='edit-options'), url(
url(r'^edit_options/(?P<section>OptionalMachine)$', views.edit_options, name='edit-options'), r'^edit_options/(?P<section>OptionalUser)$',
url(r'^edit_options/(?P<section>OptionalTopologie)$', views.edit_options, name='edit-options'), views.edit_options,
url(r'^edit_options/(?P<section>GeneralOption)$', views.edit_options, name='edit-options'), name='edit-options'
url(r'^edit_options/(?P<section>AssoOption)$', views.edit_options, name='edit-options'), ),
url(r'^edit_options/(?P<section>MailMessageOption)$', views.edit_options, name='edit-options'), url(
r'^edit_options/(?P<section>OptionalMachine)$',
views.edit_options,
name='edit-options'
),
url(
r'^edit_options/(?P<section>OptionalTopologie)$',
views.edit_options,
name='edit-options'
),
url(
r'^edit_options/(?P<section>GeneralOption)$',
views.edit_options,
name='edit-options'
),
url(
r'^edit_options/(?P<section>AssoOption)$',
views.edit_options,
name='edit-options'
),
url(
r'^edit_options/(?P<section>MailMessageOption)$',
views.edit_options,
name='edit-options'
),
url(r'^add_services/$', views.add_services, name='add-services'), url(r'^add_services/$', views.add_services, name='add-services'),
url(r'^edit_services/(?P<servicesid>[0-9]+)$', views.edit_services, name='edit-services'), url(
r'^edit_services/(?P<servicesid>[0-9]+)$',
views.edit_services,
name='edit-services'
),
url(r'^del_services/$', views.del_services, name='del-services'), url(r'^del_services/$', views.del_services, name='del-services'),
url(r'^history/(?P<object>service)/(?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.display_options, name='display-options'), url(r'^$', views.display_options, name='display-options'),
] ]

View file

@ -23,48 +23,53 @@
# App de gestion des machines pour re2o # App de gestion des machines pour re2o
# Gabriel Détraz, Augustin Lemesle # Gabriel Détraz, Augustin Lemesle
# Gplv2 # Gplv2
"""
Vue d'affichage, et de modification des réglages (réglages machine,
topologie, users, service...)
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.shortcuts import render from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404, render, redirect
from django.template.context_processors import csrf
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.template import Context, RequestContext, loader
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, permission_required
from django.db.models import Max, ProtectedError from django.db.models import ProtectedError
from django.db import IntegrityError
from django.core.mail import send_mail
from django.utils import timezone
from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from reversion.models import Version from reversion.models import Version
from reversion import revisions as reversion from reversion import revisions as reversion
from re2o.views import form
from .forms import ServiceForm, DelServiceForm from .forms import ServiceForm, DelServiceForm
from .models import Service, OptionalUser, OptionalMachine, AssoOption, MailMessageOption, GeneralOption, OptionalTopologie from .models import Service, OptionalUser, OptionalMachine, AssoOption
from .models import MailMessageOption, GeneralOption, OptionalTopologie
from . import models from . import models
from . import forms from . import forms
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render(request, template, c)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def display_options(request): def display_options(request):
useroptions, created = OptionalUser.objects.get_or_create() """Vue pour affichage des options (en vrac) classé selon les models
machineoptions, created = OptionalMachine.objects.get_or_create() correspondants dans un tableau"""
topologieoptions, created = OptionalTopologie.objects.get_or_create() useroptions, _created = OptionalUser.objects.get_or_create()
generaloptions, created = GeneralOption.objects.get_or_create() machineoptions, _created = OptionalMachine.objects.get_or_create()
assooptions, created = AssoOption.objects.get_or_create() topologieoptions, _created = OptionalTopologie.objects.get_or_create()
mailmessageoptions, created = MailMessageOption.objects.get_or_create() generaloptions, _created = GeneralOption.objects.get_or_create()
assooptions, _created = AssoOption.objects.get_or_create()
mailmessageoptions, _created = MailMessageOption.objects.get_or_create()
service_list = Service.objects.all() service_list = Service.objects.all()
return form({'useroptions': useroptions, 'machineoptions': machineoptions, 'topologieoptions': topologieoptions, 'generaloptions': generaloptions, 'assooptions' : assooptions, 'mailmessageoptions' : mailmessageoptions, 'service_list':service_list}, 'preferences/display_preferences.html', request) return form({
'useroptions': useroptions,
'machineoptions': machineoptions,
'topologieoptions': topologieoptions,
'generaloptions': generaloptions,
'assooptions': assooptions,
'mailmessageoptions': mailmessageoptions,
'service_list': service_list
}, 'preferences/display_preferences.html', request)
@login_required @login_required
@permission_required('admin') @permission_required('admin')
@ -73,23 +78,36 @@ def edit_options(request, section):
model = getattr(models, section, None) model = getattr(models, section, None)
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()
options = form_instance(request.POST or None, instance=options_instance) options = form_instance(
request.POST or None,
instance=options_instance
)
if options.is_valid(): if options.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
options.save() options.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in options.changed_data)) reversion.set_comment(
"Champs modifié(s) : %s" % ', '.join(
field for field in options.changed_data
)
)
messages.success(request, "Préférences modifiées") messages.success(request, "Préférences modifiées")
return redirect("/preferences/") return redirect("/preferences/")
return form({'options': options}, 'preferences/edit_preferences.html', request) return form(
{'options': options},
'preferences/edit_preferences.html',
request
)
else: else:
messages.error(request, "Objet inconnu") messages.error(request, "Objet inconnu")
return redirect("/preferences/") return redirect("/preferences/")
@login_required @login_required
@permission_required('admin') @permission_required('admin')
def add_services(request): def add_services(request):
"""Ajout d'un service de la page d'accueil"""
services = ServiceForm(request.POST or None) services = ServiceForm(request.POST or None)
if services.is_valid(): if services.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -98,29 +116,45 @@ def add_services(request):
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "Cet enregistrement ns a été ajouté") messages.success(request, "Cet enregistrement ns a été ajouté")
return redirect("/preferences/") return redirect("/preferences/")
return form({'preferenceform': services}, 'preferences/preferences.html', request) return form(
{'preferenceform': services},
'preferences/preferences.html',
request
)
@login_required @login_required
@permission_required('admin') @permission_required('admin')
def edit_services(request, servicesid): def edit_services(request, servicesid):
"""Edition des services affichés sur la page d'accueil"""
try: try:
services_instance = Service.objects.get(pk=servicesid) services_instance = Service.objects.get(pk=servicesid)
except Service.DoesNotExist: except Service.DoesNotExist:
messages.error(request, u"Entrée inexistante" ) messages.error(request, u"Entrée inexistante")
return redirect("/preferences/") return redirect("/preferences/")
services = ServiceForm(request.POST or None, instance=services_instance) services = ServiceForm(request.POST or None, instance=services_instance)
if services.is_valid(): if services.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
services.save() services.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in services.changed_data)) reversion.set_comment(
"Champs modifié(s) : %s" % ', '.join(
field for field in services.changed_data
)
)
messages.success(request, "Service modifié") messages.success(request, "Service modifié")
return redirect("/preferences/") return redirect("/preferences/")
return form({'preferenceform': services}, 'preferences/preferences.html', request) return form(
{'preferenceform': services},
'preferences/preferences.html',
request
)
@login_required @login_required
@permission_required('admin') @permission_required('admin')
def del_services(request): def del_services(request):
"""Suppression d'un service de la page d'accueil"""
services = DelServiceForm(request.POST or None) services = DelServiceForm(request.POST or None)
if services.is_valid(): if services.is_valid():
services_dels = services.cleaned_data['services'] services_dels = services.cleaned_data['services']
@ -131,20 +165,28 @@ def del_services(request):
reversion.set_user(request.user) reversion.set_user(request.user)
messages.success(request, "Le services a été supprimée") messages.success(request, "Le services a été supprimée")
except ProtectedError: except ProtectedError:
messages.error(request, "Erreur le service suivant %s ne peut être supprimé" % services_del) messages.error(request, "Erreur le service\
suivant %s ne peut être supprimé" % services_del)
return redirect("/preferences/") return redirect("/preferences/")
return form({'preferenceform': services}, 'preferences/preferences.html', request) return form(
{'preferenceform': services},
'preferences/preferences.html',
request
)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def history(request, object, id): def history(request, object_name, object_id):
if object == 'service': """Historique de creation et de modification d'un service affiché sur
la page d'accueil"""
if object_name == 'service':
try: try:
object_instance = Service.objects.get(pk=id) object_instance = Service.objects.get(pk=object_id)
except Service.DoesNotExist: except Service.DoesNotExist:
messages.error(request, "Service inexistant") messages.error(request, "Service inexistant")
return redirect("/preferences/") return redirect("/preferences/")
options, created = GeneralOption.objects.get_or_create() options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
reversions = Version.objects.get_for_object(object_instance) reversions = Version.objects.get_for_object(object_instance)
paginator = Paginator(reversions, pagination_number) paginator = Paginator(reversions, pagination_number)
@ -157,4 +199,7 @@ def history(request, object, id):
except EmptyPage: except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results. # If page is out of range (e.g. 9999), deliver last page of results.
reversions = paginator.page(paginator.num_pages) reversions = paginator.page(paginator.num_pages)
return render(request, 're2o/history.html', {'reversions': reversions, 'object': object_instance}) return render(request, 're2o/history.html', {
'reversions': reversions,
'object': object_instance
})

View file

@ -19,15 +19,19 @@
# 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.
"""Fonction de context, variables renvoyées à toutes les vues"""
from __future__ import unicode_literals from __future__ import unicode_literals
from machines.models import Interface, Machine
from preferences.models import GeneralOption, OptionalMachine from preferences.models import GeneralOption, OptionalMachine
def context_user(request): def context_user(request):
general_options, created = GeneralOption.objects.get_or_create() """Fonction de context lorsqu'un user est logué (ou non),
machine_options, created = OptionalMachine.objects.get_or_create() renvoie les infos sur l'user, la liste de ses droits, ses machines"""
general_options, _created = GeneralOption.objects.get_or_create()
machine_options, _created = OptionalMachine.objects.get_or_create()
user = request.user user = request.user
if user.is_authenticated(): if user.is_authenticated():
interfaces = user.user_interfaces() interfaces = user.user_interfaces()
@ -52,8 +56,8 @@ def context_user(request):
'is_bofh': is_bofh, 'is_bofh': is_bofh,
'is_trez': is_trez, 'is_trez': is_trez,
'is_infra': is_infra, 'is_infra': is_infra,
'is_admin' : is_admin, '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,
} }

View file

@ -49,10 +49,16 @@ urlpatterns = [
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^users/', include('users.urls', namespace='users')), url(r'^users/', include('users.urls', namespace='users')),
url(r'^search/', include('search.urls', namespace='search')), url(r'^search/', include('search.urls', namespace='search')),
url(r'^cotisations/', include('cotisations.urls', namespace='cotisations')), url(
r'^cotisations/',
include('cotisations.urls', namespace='cotisations')
),
url(r'^machines/', include('machines.urls', namespace='machines')), url(r'^machines/', include('machines.urls', namespace='machines')),
url(r'^topologie/', include('topologie.urls', namespace='topologie')), url(r'^topologie/', include('topologie.urls', namespace='topologie')),
url(r'^logs/', include('logs.urls', namespace='logs')), url(r'^logs/', include('logs.urls', namespace='logs')),
url(r'^preferences/', include('preferences.urls', namespace='preferences')), url(
r'^preferences/',
include('preferences.urls', namespace='preferences')
),
] ]

136
re2o/utils.py Normal file
View file

@ -0,0 +1,136 @@
# -*- 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.
# -*- coding: utf-8 -*-
# David Sinquin, Gabriel Détraz, Goulven Kermarec
"""
Regroupe les fonctions transversales utiles
Fonction :
- récupérer tous les utilisateurs actifs
- récupérer toutes les machines
- récupérer tous les bans
etc
"""
from __future__ import unicode_literals
from django.utils import timezone
from django.db.models import Q
from cotisations.models import Cotisation, Facture, Paiement, Vente
from machines.models import Domain, Interface, Machine
from users.models import User, Ban, Whitelist
from preferences.models import Service
DT_NOW = timezone.now()
def all_adherent(search_time=DT_NOW):
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est
qu'une seule requete sql
Inspecte les factures de l'user et ses cotisation, regarde si elles
sont posterieur à now (end_time)"""
return User.objects.filter(
facture__in=Facture.objects.filter(
vente__in=Vente.objects.filter(
cotisation__in=Cotisation.objects.filter(
vente__in=Vente.objects.filter(
facture__in=Facture.objects.all().exclude(valid=False)
)
).filter(date_end__gt=search_time)
)
)
).distinct()
def all_baned(search_time=DT_NOW):
""" Fonction renvoyant tous les users bannis """
return User.objects.filter(
ban__in=Ban.objects.filter(
date_end__gt=search_time
)
).distinct()
def all_whitelisted(search_time=DT_NOW):
""" Fonction renvoyant tous les users whitelistes """
return User.objects.filter(
whitelist__in=Whitelist.objects.filter(
date_end__gt=search_time
)
).distinct()
def all_has_access(search_time=DT_NOW):
""" Renvoie tous les users beneficiant d'une connexion
: user adherent ou whiteliste et non banni """
return User.objects.filter(
Q(state=User.STATE_ACTIVE) &
~Q(ban__in=Ban.objects.filter(date_end__gt=search_time)) &
(Q(whitelist__in=Whitelist.objects.filter(date_end__gt=search_time)) |
Q(facture__in=Facture.objects.filter(
vente__in=Vente.objects.filter(
cotisation__in=Cotisation.objects.filter(
vente__in=Vente.objects.filter(
facture__in=Facture.objects.all()
.exclude(valid=False)
)
).filter(date_end__gt=search_time)
)
)))
).distinct()
def all_active_interfaces():
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
return Interface.objects.filter(
machine__in=Machine.objects.filter(
user__in=all_has_access()
).filter(active=True)
).select_related('domain').select_related('machine')\
.select_related('type').select_related('ipv4')\
.select_related('domain__extension').select_related('ipv4__ip_type')\
.distinct()
def all_active_assigned_interfaces():
""" Renvoie l'ensemble des machines qui ont une ipv4 assignées et
disposant de l'accès internet"""
return all_active_interfaces().filter(ipv4__isnull=False)
def all_active_interfaces_count():
""" Version light seulement pour compter"""
return Interface.objects.filter(
machine__in=Machine.objects.filter(
user__in=all_has_access()
).filter(active=True)
)
def all_active_assigned_interfaces_count():
""" Version light seulement pour compter"""
return all_active_interfaces_count().filter(ipv4__isnull=False)

View file

@ -19,25 +19,28 @@
# 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.
"""
Fonctions de la page d'accueil et diverses fonctions utiles pour tous
les views
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.shortcuts import render from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.template.context_processors import csrf from django.template.context_processors import csrf
from django.template import Context, RequestContext, loader
from preferences.models import Service from preferences.models import Service
def form(ctx, template, request): def form(ctx, template, request):
"""Form générique, raccourci importé par les fonctions views du site"""
context = ctx context = ctx
context.update(csrf(request)) context.update(csrf(request))
return render(request, template, context) return render(request, template, context)
def index(request): def index(request):
i = 0 """Affiche la liste des services sur la page d'accueil de re2o"""
services = [[], [], []] services = [[], [], []]
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)

View file

@ -32,9 +32,10 @@ https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
from __future__ import unicode_literals from __future__ import unicode_literals
import os import os
from django.core.wsgi import get_wsgi_application
from os.path import dirname
import sys import sys
from os.path import dirname
from django.core.wsgi import get_wsgi_application
sys.path.append(dirname(dirname(__file__))) sys.path.append(dirname(dirname(__file__)))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings")

View file

@ -381,8 +381,20 @@ def new_switch(request):
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "Le switch a été créé") messages.success(request, "Le switch a été créé")
return redirect("/topologie/") return redirect("/topologie/")
<<<<<<< HEAD
i_bft_param = generate_ipv4_mbf_param( interface, False ) i_bft_param = generate_ipv4_mbf_param( interface, False )
return form({'topoform':switch, 'machineform': machine, 'interfaceform': interface, 'domainform': domain, 'i_bft_param': i_bft_param}, 'topologie/switch.html', request) return form({'topoform':switch, 'machineform': machine, 'interfaceform': interface, 'domainform': domain, 'i_bft_param': i_bft_param}, 'topologie/switch.html', request)
=======
i_bft_param = generate_ipv4_bft_param(interface, False)
return form({
'topoform': switch,
'machineform': machine,
'interfaceform': interface,
'domainform': domain,
'i_bft_param': i_bft_param
}, 'topologie/switch.html', request)
>>>>>>> master
@login_required @login_required
@permission_required('infra') @permission_required('infra')
@ -442,8 +454,20 @@ def edit_switch(request, switch_id):
) )
messages.success(request, "Le switch a bien été modifié") messages.success(request, "Le switch a bien été modifié")
return redirect("/topologie/") return redirect("/topologie/")
<<<<<<< HEAD
i_bft_param = generate_ipv4_mbf_param( interface_form, False ) i_bft_param = generate_ipv4_mbf_param( interface_form, False )
return form({'topoform':switch_form, 'machineform': machine_form, 'interfaceform': interface_form, 'domainform': domain_form, 'i_bft_param': i_bft_param}, 'topologie/switch.html', request) return form({'topoform':switch_form, 'machineform': machine_form, 'interfaceform': interface_form, 'domainform': domain_form, 'i_bft_param': i_bft_param}, 'topologie/switch.html', request)
=======
i_bft_param = generate_ipv4_bft_param(interface_form, False)
return form({
'topoform': switch_form,
'machineform': machine_form,
'interfaceform': interface_form,
'domainform': domain_form,
'i_bft_param': i_bft_param
}, 'topologie/switch.html', request)
>>>>>>> master
@login_required @login_required
@permission_required('infra') @permission_required('infra')

View file

@ -20,6 +20,10 @@
# 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.
"""
Definition des vues pour les admin. Classique, sauf pour users,
on fait appel à UserChange et ServiceUserChange, forms custom
"""
from __future__ import unicode_literals from __future__ import unicode_literals
@ -28,11 +32,15 @@ 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, Ban, Whitelist, Request, LdapUser, LdapServiceUser, LdapServiceUserGroup, LdapUserGroup from .models import User, ServiceUser, School, Right, ListRight, ListShell
from .forms import UserChangeForm, UserCreationForm, ServiceUserChangeForm, ServiceUserCreationForm from .models import Ban, Whitelist, Request, LdapUser, LdapServiceUser
from .models import LdapServiceUserGroup, LdapUserGroup
from .forms import UserChangeForm, UserCreationForm
from .forms import ServiceUserChangeForm, ServiceUserCreationForm
class UserAdmin(admin.ModelAdmin): class UserAdmin(admin.ModelAdmin):
"""Administration d'un user"""
list_display = ( list_display = (
'name', 'name',
'surname', 'surname',
@ -43,51 +51,73 @@ class UserAdmin(admin.ModelAdmin):
'shell', 'shell',
'state' 'state'
) )
search_fields = ('name','surname','pseudo','room') search_fields = ('name', 'surname', 'pseudo', 'room')
class LdapUserAdmin(admin.ModelAdmin): class LdapUserAdmin(admin.ModelAdmin):
list_display = ('name','uidNumber','login_shell') """Administration du ldapuser"""
exclude = ('user_password','sambat_nt_password') list_display = ('name', 'uidNumber', 'login_shell')
exclude = ('user_password', 'sambat_nt_password')
search_fields = ('name',) search_fields = ('name',)
class LdapServiceUserAdmin(admin.ModelAdmin): class LdapServiceUserAdmin(admin.ModelAdmin):
"""Administration du ldapserviceuser"""
list_display = ('name',) list_display = ('name',)
exclude = ('user_password',) exclude = ('user_password',)
search_fields = ('name',) search_fields = ('name',)
class LdapUserGroupAdmin(admin.ModelAdmin): class LdapUserGroupAdmin(admin.ModelAdmin):
list_display = ('name','members','gid') """Administration du ldapusergroupe"""
list_display = ('name', 'members', 'gid')
search_fields = ('name',) search_fields = ('name',)
class LdapServiceUserGroupAdmin(admin.ModelAdmin): class LdapServiceUserGroupAdmin(admin.ModelAdmin):
"""Administration du ldap serviceusergroup"""
list_display = ('name',) list_display = ('name',)
search_fields = ('name',) search_fields = ('name',)
class SchoolAdmin(VersionAdmin): class SchoolAdmin(VersionAdmin):
list_display = ('name',) """Administration, gestion des écoles"""
pass
class ListRightAdmin(VersionAdmin): class ListRightAdmin(VersionAdmin):
"""Gestion de la liste des droits existants
Ne permet pas l'edition du gid (primarykey pour ldap)"""
list_display = ('listright',) list_display = ('listright',)
class ListShellAdmin(VersionAdmin): class ListShellAdmin(VersionAdmin):
list_display = ('shell',) """Gestion de la liste des shells coté admin"""
pass
class RightAdmin(VersionAdmin): class RightAdmin(VersionAdmin):
list_display = ('user', 'right') """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"""
list_display = ('user', 'type', 'created_at', 'expires_at') list_display = ('user', 'type', 'created_at', 'expires_at')
class BanAdmin(VersionAdmin): class BanAdmin(VersionAdmin):
list_display = ('user', 'raison', 'date_start', 'date_end') """Gestion des bannissements"""
pass
class WhitelistAdmin(VersionAdmin): class WhitelistAdmin(VersionAdmin):
list_display = ('user', 'raison', 'date_start', 'date_end') """Gestion des whitelist"""
pass
class UserAdmin(VersionAdmin, BaseUserAdmin): class UserAdmin(VersionAdmin, BaseUserAdmin):
"""Gestion d'un user : modification des champs perso, mot de passe, etc"""
# The forms to add and change user instances # The forms to add and change user instances
form = UserChangeForm form = UserChangeForm
add_form = UserCreationForm add_form = UserCreationForm
@ -95,27 +125,56 @@ class UserAdmin(VersionAdmin, BaseUserAdmin):
# The fields to be used in displaying the User model. # The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin # These override the definitions on the base UserAdmin
# that reference specific fields on auth.User. # that reference specific fields on auth.User.
list_display = ('pseudo', 'name', 'surname', 'email', 'school', 'is_admin', 'shell') list_display = (
'pseudo',
'name',
'surname',
'email',
'school',
'is_admin',
'shell'
)
list_display = ('pseudo',) list_display = ('pseudo',)
list_filter = () list_filter = ()
fieldsets = ( fieldsets = (
(None, {'fields': ('pseudo', 'password')}), (None, {'fields': ('pseudo', 'password')}),
('Personal info', {'fields': ('name', 'surname', 'email', 'school','shell', 'uid_number')}), (
'Personal info',
{
'fields':
('name', 'surname', 'email', 'school', 'shell', 'uid_number')
}
),
('Permissions', {'fields': ('is_admin', )}), ('Permissions', {'fields': ('is_admin', )}),
) )
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user. # overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = ( add_fieldsets = (
(None, { (
None,
{
'classes': ('wide',), 'classes': ('wide',),
'fields': ('pseudo', 'name', 'surname', 'email', 'school', 'is_admin', 'password1', 'password2')} 'fields': (
'pseudo',
'name',
'surname',
'email',
'school',
'is_admin',
'password1',
'password2'
)
}
), ),
) )
search_fields = ('pseudo',) search_fields = ('pseudo',)
ordering = ('pseudo',) ordering = ('pseudo',)
filter_horizontal = () filter_horizontal = ()
class ServiceUserAdmin(VersionAdmin, BaseUserAdmin): class ServiceUserAdmin(VersionAdmin, BaseUserAdmin):
"""Gestion d'un service user admin : champs personnels,
mot de passe; etc"""
# The forms to add and change user instances # The forms to add and change user instances
form = ServiceUserChangeForm form = ServiceUserChangeForm
add_form = ServiceUserCreationForm add_form = ServiceUserCreationForm
@ -131,15 +190,19 @@ class ServiceUserAdmin(VersionAdmin, BaseUserAdmin):
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user. # overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = ( add_fieldsets = (
(None, { (
None,
{
'classes': ('wide',), 'classes': ('wide',),
'fields': ('pseudo', 'password1', 'password2')} 'fields': ('pseudo', 'password1', 'password2')
}
), ),
) )
search_fields = ('pseudo',) search_fields = ('pseudo',)
ordering = ('pseudo',) ordering = ('pseudo',)
filter_horizontal = () filter_horizontal = ()
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
admin.site.register(ServiceUser, ServiceUserAdmin) admin.site.register(ServiceUser, ServiceUserAdmin)
admin.site.register(LdapUser, LdapUserAdmin) admin.site.register(LdapUser, LdapUserAdmin)

View file

@ -20,8 +20,16 @@
# 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.
"""
Definition des forms pour l'application users.
# -*- coding: utf-8 -*- Modification, creation de :
- un user (informations personnelles)
- un bannissement
- le mot de passe d'un user
- une whiteliste
- un user de service
"""
from __future__ import unicode_literals from __future__ import unicode_literals
@ -29,17 +37,34 @@ from django import forms
from django.forms import ModelForm, Form 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 preferences.models import OptionalUser
from django.utils import timezone from django.utils import timezone
from .models import User, ServiceUser, Right, School, ListRight, Whitelist, Ban, Request, remove_user_room
from .models import get_admin_right from preferences.models import OptionalUser
from .models import User, ServiceUser, Right, School, ListRight, Whitelist
from .models import Ban, remove_user_room
NOW = timezone.now()
class PassForm(forms.Form): class PassForm(forms.Form):
passwd1 = forms.CharField(label=u'Nouveau mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput) """Formulaire de changement de mot de passe. Verifie que les 2
passwd2 = forms.CharField(label=u'Saisir à nouveau le mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput) nouveaux mots de passe renseignés sont identiques et respectent
une norme"""
passwd1 = forms.CharField(
label=u'Nouveau mot de passe',
max_length=255,
validators=[MinLengthValidator(8)],
widget=forms.PasswordInput
)
passwd2 = forms.CharField(
label=u'Saisir à nouveau le mot de passe',
max_length=255,
validators=[MinLengthValidator(8)],
widget=forms.PasswordInput
)
def clean_passwd2(self): def clean_passwd2(self):
"""Verifie que passwd1 et 2 sont identiques"""
# Check that the two password entries match # Check that the two password entries match
password1 = self.cleaned_data.get("passwd1") password1 = self.cleaned_data.get("passwd1")
password2 = self.cleaned_data.get("passwd2") password2 = self.cleaned_data.get("passwd2")
@ -47,11 +72,26 @@ class PassForm(forms.Form):
raise forms.ValidationError("Passwords don't match") raise forms.ValidationError("Passwords don't match")
return password2 return password2
class UserCreationForm(forms.ModelForm): class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required """A form for creating new users. Includes all the required
fields, plus a repeated password.""" fields, plus a repeated password.
password1 = forms.CharField(label='Password', widget=forms.PasswordInput, validators=[MinLengthValidator(8)], max_length=255)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput, validators=[MinLengthValidator(8)], max_length=255) Formulaire pour la création d'un user. N'est utilisé que pour
l'admin, lors de la creation d'un user par admin. Inclu tous les
champs obligatoires"""
password1 = forms.CharField(
label='Password',
widget=forms.PasswordInput,
validators=[MinLengthValidator(8)],
max_length=255
)
password2 = forms.CharField(
label='Password confirmation',
widget=forms.PasswordInput,
validators=[MinLengthValidator(8)],
max_length=255
)
is_admin = forms.BooleanField(label='is admin') is_admin = forms.BooleanField(label='is admin')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -63,6 +103,7 @@ class UserCreationForm(forms.ModelForm):
fields = ('pseudo', 'name', 'surname', 'email') fields = ('pseudo', 'name', 'surname', 'email')
def clean_password2(self): def clean_password2(self):
"""Verifie que password1 et 2 sont identiques"""
# Check that the two password entries match # Check that the two password entries match
password1 = self.cleaned_data.get("password1") password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2") password2 = self.cleaned_data.get("password2")
@ -78,21 +119,40 @@ class UserCreationForm(forms.ModelForm):
user.is_admin = self.cleaned_data.get("is_admin") user.is_admin = self.cleaned_data.get("is_admin")
return user return user
class ServiceUserCreationForm(forms.ModelForm): class ServiceUserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required """A form for creating new users. Includes all the required
fields, plus a repeated password.""" fields, plus a repeated password.
password1 = forms.CharField(label='Password', widget=forms.PasswordInput, min_length=8, max_length=255)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput, min_length=8, max_length=255) Formulaire pour la creation de nouveaux serviceusers.
Requiert seulement un mot de passe; et un pseudo"""
password1 = forms.CharField(
label='Password',
widget=forms.PasswordInput,
min_length=8,
max_length=255
)
password2 = forms.CharField(
label='Password confirmation',
widget=forms.PasswordInput,
min_length=8,
max_length=255
)
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(ServiceUserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) super(ServiceUserCreationForm, self).__init__(
*args,
prefix=prefix,
**kwargs
)
class Meta: class Meta:
model = ServiceUser model = ServiceUser
fields = ('pseudo',) fields = ('pseudo',)
def clean_password2(self): def clean_password2(self):
"""Verifie que password1 et 2 sont indentiques"""
# Check that the two password entries match # Check that the two password entries match
password1 = self.cleaned_data.get("password1") password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2") password2 = self.cleaned_data.get("password2")
@ -107,10 +167,13 @@ class ServiceUserCreationForm(forms.ModelForm):
user.save() user.save()
return user return user
class UserChangeForm(forms.ModelForm): class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on """A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's the user, but replaces the password field with admin's
password hash display field. password hash display field.
Formulaire pour la modification d'un user coté admin
""" """
password = ReadOnlyPasswordHashField() password = ReadOnlyPasswordHashField()
is_admin = forms.BooleanField(label='is admin', required=False) is_admin = forms.BooleanField(label='is admin', required=False)
@ -126,6 +189,7 @@ class UserChangeForm(forms.ModelForm):
self.initial['is_admin'] = kwargs['instance'].is_admin self.initial['is_admin'] = kwargs['instance'].is_admin
def clean_password(self): def clean_password(self):
"""Dummy fun"""
# Regardless of what the user provides, return the initial value. # Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the # This is done here, rather than on the field, because the
# field does not have access to the initial value # field does not have access to the initial value
@ -139,42 +203,59 @@ class UserChangeForm(forms.ModelForm):
user.save() user.save()
return user return user
class ServiceUserChangeForm(forms.ModelForm): class ServiceUserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on """A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's the user, but replaces the password field with admin's
password hash display field. password hash display field.
Formulaire pour l'edition des service users coté admin
""" """
password = ReadOnlyPasswordHashField() password = ReadOnlyPasswordHashField()
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(ServiceUserChangeForm, self).__init__(*args, prefix=prefix, **kwargs) super(ServiceUserChangeForm, self).__init__(
*args,
prefix=prefix,
**kwargs
)
class Meta: class Meta:
model = ServiceUser model = ServiceUser
fields = ('pseudo',) fields = ('pseudo',)
def clean_password(self): def clean_password(self):
# Regardless of what the user provides, return the initial value. """Dummy fun"""
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"] return self.initial["password"]
class ResetPasswordForm(forms.Form): class ResetPasswordForm(forms.Form):
"""Formulaire de demande de reinitialisation de mot de passe,
mdp oublié"""
pseudo = forms.CharField(label=u'Pseudo', max_length=255) pseudo = forms.CharField(label=u'Pseudo', max_length=255)
email = forms.EmailField(max_length=255) email = forms.EmailField(max_length=255)
class MassArchiveForm(forms.Form): class MassArchiveForm(forms.Form):
"""Formulaire d'archivage des users inactif. Prend en argument
du formulaire la date de depart avant laquelle archiver les
users"""
date = forms.DateTimeField(help_text='%d/%m/%y') date = forms.DateTimeField(help_text='%d/%m/%y')
def clean(self): def clean(self):
cleaned_data=super(MassArchiveForm, self).clean() cleaned_data = super(MassArchiveForm, self).clean()
date = cleaned_data.get("date") date = cleaned_data.get("date")
if date: if date:
if date>timezone.now(): if date > NOW:
raise forms.ValidationError("Impossible d'archiver des utilisateurs dont la fin d'accès se situe dans le futur !") raise forms.ValidationError("Impossible d'archiver des\
utilisateurs dont la fin d'accès se situe dans le futur !")
class BaseInfoForm(ModelForm): class BaseInfoForm(ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis"""
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(BaseInfoForm, self).__init__(*args, prefix=prefix, **kwargs) super(BaseInfoForm, self).__init__(*args, prefix=prefix, **kwargs)
@ -200,13 +281,21 @@ class BaseInfoForm(ModelForm):
] ]
def clean_telephone(self): def clean_telephone(self):
"""Verifie que le tel est présent si 'option est validée
dans preferences"""
telephone = self.cleaned_data['telephone'] telephone = self.cleaned_data['telephone']
preferences, created = OptionalUser.objects.get_or_create() preferences, _created = OptionalUser.objects.get_or_create()
if not telephone and preferences.is_tel_mandatory: if not telephone and preferences.is_tel_mandatory:
raise forms.ValidationError("Un numéro de téléphone valide est requis") raise forms.ValidationError(
"Un numéro de téléphone valide est requis"
)
return telephone return telephone
class EditInfoForm(BaseInfoForm): class EditInfoForm(BaseInfoForm):
"""Edition complète d'un user. Utilisé par admin,
permet d'editer normalement la chambre, ou le shell
Herite de la base"""
class Meta(BaseInfoForm.Meta): class Meta(BaseInfoForm.Meta):
fields = [ fields = [
'name', 'name',
@ -220,22 +309,33 @@ class EditInfoForm(BaseInfoForm):
'telephone', 'telephone',
] ]
class InfoForm(EditInfoForm): class InfoForm(EditInfoForm):
""" Utile pour forcer un déménagement quand il y a déjà un user en place""" """ Utile pour forcer un déménagement quand il y a déjà un user en place
force = forms.BooleanField(label="Forcer le déménagement ?", initial=False, required=False) Formuaire utilisé pour la creation initiale"""
force = forms.BooleanField(
label="Forcer le déménagement ?",
initial=False,
required=False
)
def clean_force(self): def clean_force(self):
"""On supprime l'ancien user de la chambre si et seulement si la
case est cochée"""
if self.cleaned_data.get('force', False): if self.cleaned_data.get('force', False):
remove_user_room(self.cleaned_data.get('room')) remove_user_room(self.cleaned_data.get('room'))
return return
class UserForm(InfoForm): class UserForm(InfoForm):
""" Model form general""" """ Model form general"""
class Meta(InfoForm.Meta): class Meta(InfoForm.Meta):
fields = '__all__' fields = '__all__'
class PasswordForm(ModelForm): class PasswordForm(ModelForm):
""" Formulaire de changement brut de mot de passe. Ne pas utiliser sans traitement""" """ Formulaire de changement brut de mot de passe.
Ne pas utiliser sans traitement"""
class Meta: class Meta:
model = User model = User
fields = ['password', 'pwd_ntlm'] fields = ['password', 'pwd_ntlm']
@ -244,21 +344,32 @@ class PasswordForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(PasswordForm, self).__init__(*args, prefix=prefix, **kwargs) super(PasswordForm, self).__init__(*args, prefix=prefix, **kwargs)
class ServiceUserForm(ModelForm): class ServiceUserForm(ModelForm):
""" Modification d'un service user""" """ Modification d'un service user"""
password = forms.CharField(label=u'Nouveau mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput, required=False) password = forms.CharField(
label=u'Nouveau mot de passe',
max_length=255,
validators=[MinLengthValidator(8)],
widget=forms.PasswordInput,
required=False
)
class Meta: class Meta:
model = ServiceUser model = ServiceUser
fields = ('pseudo','access_group') fields = ('pseudo', 'access_group')
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(ServiceUserForm, self).__init__(*args, prefix=prefix, **kwargs) super(ServiceUserForm, self).__init__(*args, prefix=prefix, **kwargs)
class EditServiceUserForm(ServiceUserForm): class EditServiceUserForm(ServiceUserForm):
"""Formulaire d'edition de base d'un service user. Ne permet
d'editer que son group d'acl et son commentaire"""
class Meta(ServiceUserForm.Meta): class Meta(ServiceUserForm.Meta):
fields = ['access_group','comment'] fields = ['access_group', 'comment']
class StateForm(ModelForm): class StateForm(ModelForm):
""" Changement de l'état d'un user""" """ Changement de l'état d'un user"""
@ -272,6 +383,7 @@ class StateForm(ModelForm):
class SchoolForm(ModelForm): class SchoolForm(ModelForm):
"""Edition, creation d'un école"""
class Meta: class Meta:
model = School model = School
fields = ['name'] fields = ['name']
@ -281,7 +393,10 @@ class SchoolForm(ModelForm):
super(SchoolForm, self).__init__(*args, prefix=prefix, **kwargs) super(SchoolForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = 'Établissement' self.fields['name'].label = 'Établissement'
class ListRightForm(ModelForm): class ListRightForm(ModelForm):
"""Edition, d'un groupe , équivalent à un droit
Ne peremet pas d'editer le gid, car il sert de primary key"""
class Meta: class Meta:
model = ListRight model = ListRight
fields = ['listright', 'details'] fields = ['listright', 'details']
@ -291,21 +406,38 @@ class ListRightForm(ModelForm):
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['listright'].label = 'Nom du droit/groupe'
class NewListRightForm(ListRightForm): class NewListRightForm(ListRightForm):
"""Ajout d'un groupe/list de droit """
class Meta(ListRightForm.Meta): class Meta(ListRightForm.Meta):
fields = '__all__' fields = '__all__'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(NewListRightForm, self).__init__(*args, **kwargs) super(NewListRightForm, self).__init__(*args, **kwargs)
self.fields['gid'].label = 'Gid, attention, cet attribut ne doit pas être modifié après création' self.fields['gid'].label = 'Gid, attention, cet attribut ne doit\
pas être modifié après création'
class DelListRightForm(Form): class DelListRightForm(Form):
listrights = forms.ModelMultipleChoiceField(queryset=ListRight.objects.all(), label="Droits actuels", widget=forms.CheckboxSelectMultiple) """Suppression d'un ou plusieurs groupes"""
listrights = forms.ModelMultipleChoiceField(
queryset=ListRight.objects.all(),
label="Droits actuels",
widget=forms.CheckboxSelectMultiple
)
class DelSchoolForm(Form): class DelSchoolForm(Form):
schools = forms.ModelMultipleChoiceField(queryset=School.objects.all(), label="Etablissements actuels", widget=forms.CheckboxSelectMultiple) """Suppression d'une ou plusieurs écoles"""
schools = forms.ModelMultipleChoiceField(
queryset=School.objects.all(),
label="Etablissements actuels",
widget=forms.CheckboxSelectMultiple
)
class RightForm(ModelForm): 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__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(RightForm, self).__init__(*args, prefix=prefix, **kwargs) super(RightForm, self).__init__(*args, prefix=prefix, **kwargs)
@ -318,13 +450,19 @@ class RightForm(ModelForm):
class DelRightForm(Form): class DelRightForm(Form):
rights = forms.ModelMultipleChoiceField(queryset=Right.objects.all(), widget=forms.CheckboxSelectMultiple) """Suppression d'un droit d'un user"""
rights = forms.ModelMultipleChoiceField(
queryset=Right.objects.all(),
widget=forms.CheckboxSelectMultiple
)
def __init__(self, right, *args, **kwargs): def __init__(self, right, *args, **kwargs):
super(DelRightForm, self).__init__(*args, **kwargs) super(DelRightForm, self).__init__(*args, **kwargs)
self.fields['rights'].queryset = Right.objects.filter(right=right) self.fields['rights'].queryset = Right.objects.filter(right=right)
class BanForm(ModelForm): class BanForm(ModelForm):
"""Creation, edition d'un objet bannissement"""
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(BanForm, self).__init__(*args, prefix=prefix, **kwargs) super(BanForm, self).__init__(*args, prefix=prefix, **kwargs)
@ -335,13 +473,16 @@ class BanForm(ModelForm):
exclude = ['user'] exclude = ['user']
def clean_date_end(self): def clean_date_end(self):
"""Verification que date_end est après now"""
date_end = self.cleaned_data['date_end'] date_end = self.cleaned_data['date_end']
if date_end < timezone.now(): 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") 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 return date_end
class WhitelistForm(ModelForm): class WhitelistForm(ModelForm):
"""Creation, edition d'un objet whitelist"""
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(WhitelistForm, self).__init__(*args, prefix=prefix, **kwargs) super(WhitelistForm, self).__init__(*args, prefix=prefix, **kwargs)
@ -352,7 +493,9 @@ class WhitelistForm(ModelForm):
exclude = ['user'] exclude = ['user']
def clean_date_end(self): def clean_date_end(self):
"""Verification que la date_end est posterieur à now"""
date_end = self.cleaned_data['date_end'] date_end = self.cleaned_data['date_end']
if date_end < timezone.now(): 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") 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 return date_end

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,9 @@
# 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.
"""
Definition des urls, pointant vers les views
"""
from __future__ import unicode_literals from __future__ import unicode_literals
@ -32,39 +35,88 @@ urlpatterns = [
url(r'^state/(?P<userid>[0-9]+)$', views.state, name='state'), url(r'^state/(?P<userid>[0-9]+)$', views.state, name='state'),
url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'), url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'),
url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'), url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'),
url(r'^edit_serviceuser/(?P<userid>[0-9]+)$', views.edit_serviceuser, name='edit-serviceuser'), url(
url(r'^del_serviceuser/(?P<userid>[0-9]+)$', views.del_serviceuser, name='del-serviceuser'), r'^edit_serviceuser/(?P<userid>[0-9]+)$',
views.edit_serviceuser,
name='edit-serviceuser'
),
url(
r'^del_serviceuser/(?P<userid>[0-9]+)$',
views.del_serviceuser,
name='del-serviceuser'
),
url(r'^add_ban/(?P<userid>[0-9]+)$', views.add_ban, name='add-ban'), url(r'^add_ban/(?P<userid>[0-9]+)$', views.add_ban, name='add-ban'),
url(r'^edit_ban/(?P<banid>[0-9]+)$', views.edit_ban, name='edit-ban'), url(r'^edit_ban/(?P<banid>[0-9]+)$', views.edit_ban, name='edit-ban'),
url(r'^add_whitelist/(?P<userid>[0-9]+)$', views.add_whitelist, name='add-whitelist'), url(
url(r'^edit_whitelist/(?P<whitelistid>[0-9]+)$', views.edit_whitelist, name='edit-whitelist'), r'^add_whitelist/(?P<userid>[0-9]+)$',
views.add_whitelist,
name='add-whitelist'
),
url(
r'^edit_whitelist/(?P<whitelistid>[0-9]+)$',
views.edit_whitelist,
name='edit-whitelist'
),
url(r'^add_right/(?P<userid>[0-9]+)$', views.add_right, name='add-right'), url(r'^add_right/(?P<userid>[0-9]+)$', views.add_right, name='add-right'),
url(r'^del_right/$', views.del_right, name='del-right'), url(r'^del_right/$', views.del_right, name='del-right'),
url(r'^add_school/$', views.add_school, name='add-school'), url(r'^add_school/$', views.add_school, name='add-school'),
url(r'^edit_school/(?P<schoolid>[0-9]+)$', views.edit_school, name='edit-school'), url(
r'^edit_school/(?P<schoolid>[0-9]+)$',
views.edit_school,
name='edit-school'
),
url(r'^del_school/$', views.del_school, name='del-school'), url(r'^del_school/$', views.del_school, name='del-school'),
url(r'^add_listright/$', views.add_listright, name='add-listright'), url(r'^add_listright/$', views.add_listright, name='add-listright'),
url(r'^edit_listright/(?P<listrightid>[0-9]+)$', views.edit_listright, name='edit-listright'), url(
r'^edit_listright/(?P<listrightid>[0-9]+)$',
views.edit_listright,
name='edit-listright'
),
url(r'^del_listright/$', views.del_listright, name='del-listright'), url(r'^del_listright/$', views.del_listright, name='del-listright'),
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'), url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
url(r'^index_ban/$', views.index_ban, name='index-ban'), url(r'^index_ban/$', views.index_ban, name='index-ban'),
url(r'^index_white/$', views.index_white, name='index-white'), url(r'^index_white/$', views.index_white, name='index-white'),
url(r'^index_school/$', views.index_school, name='index-school'), url(r'^index_school/$', views.index_school, name='index-school'),
url(r'^index_listright/$', views.index_listright, name='index-listright'), url(r'^index_listright/$', views.index_listright, name='index-listright'),
url(r'^index_serviceusers/$', views.index_serviceusers, name='index-serviceusers'), url(
r'^index_serviceusers/$',
views.index_serviceusers,
name='index-serviceusers'
),
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'), url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'), url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'),
url(r'^reset_password/$', views.reset_password, name='reset-password'), url(r'^reset_password/$', views.reset_password, name='reset-password'),
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'), url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
url(r'^history/(?P<object>user)/(?P<id>[0-9]+)$', views.history, name='history'), url(
url(r'^history/(?P<object>ban)/(?P<id>[0-9]+)$', views.history, name='history'), r'^history/(?P<object>user)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object>whitelist)/(?P<id>[0-9]+)$', views.history, name='history'), views.history,
url(r'^history/(?P<object>school)/(?P<id>[0-9]+)$', views.history, name='history'), name='history'
url(r'^history/(?P<object>listright)/(?P<id>[0-9]+)$', views.history, name='history'), ),
url(r'^history/(?P<object>serviceuser)/(?P<id>[0-9]+)$', views.history, name='history'), url(
r'^history/(?P<object>ban)/(?P<id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>whitelist)/(?P<id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>school)/(?P<id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>listright)/(?P<id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>serviceuser)/(?P<id>[0-9]+)$',
views.history,
name='history'
),
url(r'^$', views.index, name='index'), url(r'^$', views.index, name='index'),
url(r'^rest/mailing/$', views.mailing, name='mailing'), url(r'^rest/mailing/$', views.mailing, name='mailing'),
] ]

View file

@ -23,20 +23,25 @@
# App de gestion des users pour re2o # App de gestion des users pour re2o
# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin # Goulven Kermarec, Gabriel Détraz, Lemesle Augustin
# Gplv2 # Gplv2
"""
Module des views.
On définit les vues pour l'ajout, l'edition des users : infos personnelles,
mot de passe, etc
Permet aussi l'ajout, edition et suppression des droits, des bannissements,
des whitelist, des services users et des écoles
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from django.shortcuts import get_object_or_404, render, redirect from django.shortcuts import get_object_or_404, render, redirect
from django.template.context_processors import csrf
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.template import Context, RequestContext, loader
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, permission_required
from django.db.models import Max, ProtectedError from django.db.models import ProtectedError
from django.db import IntegrityError from django.db import IntegrityError
from django.core.mail import send_mail
from django.utils import timezone from django.utils import timezone
from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from django.http import HttpResponse from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
@ -47,21 +52,20 @@ from rest_framework.renderers import JSONRenderer
from reversion.models import Version from reversion.models import Version
from reversion import revisions as reversion from reversion import revisions as reversion
from users.serializers import MailSerializer from users.serializers import MailSerializer
from users.models import User, Right, Ban, Whitelist, School, ListRight, Request, ServiceUser, all_has_access from users.models import User, Right, Ban, Whitelist, School, ListRight
from users.forms import DelRightForm, BanForm, WhitelistForm, DelSchoolForm, DelListRightForm, NewListRightForm from users.models import Request, ServiceUser
from users.forms import EditInfoForm, InfoForm, BaseInfoForm, StateForm, RightForm, SchoolForm, EditServiceUserForm, ServiceUserForm, ListRightForm from users.forms import DelRightForm, BanForm, WhitelistForm, DelSchoolForm
from cotisations.models import Facture from users.forms import DelListRightForm, NewListRightForm
from machines.models import Machine, Interface from users.forms import InfoForm, BaseInfoForm, StateForm
from users.forms import RightForm, SchoolForm, EditServiceUserForm
from users.forms import ServiceUserForm, ListRightForm
from users.forms import MassArchiveForm, PassForm, ResetPasswordForm from users.forms import MassArchiveForm, PassForm, ResetPasswordForm
from preferences.models import OptionalUser, AssoOption, GeneralOption from cotisations.models import Facture
from machines.models import Machine
from preferences.models import OptionalUser, GeneralOption
from re2o.login import hashNT from re2o.views import form
from re2o.utils import all_has_access
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render(request, template, c)
def password_change_action(u_form, user, request, req=False): def password_change_action(u_form, user, request, req=False):
""" Fonction qui effectue le changeemnt de mdp bdd""" """ Fonction qui effectue le changeemnt de mdp bdd"""
@ -75,10 +79,12 @@ def password_change_action(u_form, user, request, req=False):
return redirect("/") return redirect("/")
return redirect("/users/profil/" + str(user.id)) return redirect("/users/profil/" + str(user.id))
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def new_user(request): def new_user(request):
""" Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe""" """ Vue de création d'un nouvel utilisateur,
envoie un mail pour le mot de passe"""
user = InfoForm(request.POST or None) user = InfoForm(request.POST or None)
if user.is_valid(): if user.is_valid():
user = user.save(commit=False) user = user.save(commit=False)
@ -87,21 +93,25 @@ def new_user(request):
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Création") reversion.set_comment("Création")
user.reset_passwd_mail(request) user.reset_passwd_mail(request)
messages.success(request, "L'utilisateur %s a été crée, un mail pour l'initialisation du mot de passe a été envoyé" % user.pseudo) messages.success(request, "L'utilisateur %s a été crée, un mail\
pour l'initialisation du mot de passe a été envoyé" % user.pseudo)
return redirect("/users/profil/" + str(user.id)) return redirect("/users/profil/" + str(user.id))
return form({'userform': user}, 'users/user.html', request) return form({'userform': user}, 'users/user.html', request)
@login_required @login_required
def edit_info(request, userid): def edit_info(request, userid):
""" Edite un utilisateur à partir de son id, """ Edite un utilisateur à partir de son id,
si l'id est différent de request.user, vérifie la possession du droit cableur """ si l'id est différent de request.user, vérifie la
possession du droit cableur """
try: try:
user = User.objects.get(pk=userid) user = User.objects.get(pk=userid)
except User.DoesNotExist: except User.DoesNotExist:
messages.error(request, "Utilisateur inexistant") messages.error(request, "Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.has_perms(('cableur',)) and user != request.user: if not request.user.has_perms(('cableur',)) and user != request.user:
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit cableur") messages.error(request, "Vous ne pouvez pas modifier un autre\
user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
if not request.user.has_perms(('cableur',)): if not request.user.has_perms(('cableur',)):
user = BaseInfoForm(request.POST or None, instance=user) user = BaseInfoForm(request.POST or None, instance=user)
@ -111,15 +121,19 @@ def edit_info(request, userid):
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
user.save() user.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in user.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in user.changed_data
))
messages.success(request, "L'user a bien été modifié") messages.success(request, "L'user a bien été modifié")
return redirect("/users/profil/" + userid) return redirect("/users/profil/" + userid)
return form({'userform': user}, 'users/user.html', request) return form({'userform': user}, 'users/user.html', request)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
def state(request, userid): def state(request, userid):
""" Changer l'etat actif/desactivé/archivé d'un user, need droit bureau """ """ Changer l'etat actif/desactivé/archivé d'un user,
need droit bureau """
try: try:
user = User.objects.get(pk=userid) user = User.objects.get(pk=userid)
except User.DoesNotExist: except User.DoesNotExist:
@ -135,12 +149,15 @@ def state(request, userid):
elif state.cleaned_data['state'] == User.STATE_DISABLED: elif state.cleaned_data['state'] == User.STATE_DISABLED:
user.state = User.STATE_DISABLED user.state = User.STATE_DISABLED
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in state.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in state.changed_data
))
user.save() user.save()
messages.success(request, "Etat changé avec succès") messages.success(request, "Etat changé avec succès")
return redirect("/users/profil/" + userid) return redirect("/users/profil/" + userid)
return form({'userform': state}, 'users/user.html', request) return form({'userform': state}, 'users/user.html', request)
@login_required @login_required
def password(request, userid): def password(request, userid):
""" Reinitialisation d'un mot de passe à partir de l'userid, """ Reinitialisation d'un mot de passe à partir de l'userid,
@ -152,16 +169,20 @@ def password(request, userid):
messages.error(request, "Utilisateur inexistant") messages.error(request, "Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.has_perms(('cableur',)) and user != request.user: if not request.user.has_perms(('cableur',)) and user != request.user:
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit cableur") messages.error(request, "Vous ne pouvez pas modifier un\
autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
if not request.user.has_perms(('bureau',)) and user != request.user and Right.objects.filter(user=user): if not request.user.has_perms(('bureau',)) and user != request.user\
messages.error(request, "Il faut les droits bureau pour modifier le mot de passe d'un membre actif") and Right.objects.filter(user=user):
messages.error(request, "Il faut les droits bureau pour modifier le\
mot de passe d'un membre actif")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
u_form = PassForm(request.POST or None) u_form = PassForm(request.POST or None)
if u_form.is_valid(): if u_form.is_valid():
return password_change_action(u_form, user, request) return password_change_action(u_form, user, request)
return form({'userform': u_form}, 'users/user.html', request) return form({'userform': u_form}, 'users/user.html', request)
@login_required @login_required
@permission_required('infra') @permission_required('infra')
def new_serviceuser(request): def new_serviceuser(request):
@ -174,15 +195,20 @@ def new_serviceuser(request):
user_object.save() user_object.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "L'utilisateur %s a été crée" % user_object.pseudo) messages.success(
request,
"L'utilisateur %s a été crée" % user_object.pseudo
)
return redirect("/users/index_serviceusers/") return redirect("/users/index_serviceusers/")
return form({'userform': user}, 'users/user.html', request) return form({'userform': user}, 'users/user.html', request)
@login_required @login_required
@permission_required('infra') @permission_required('infra')
def edit_serviceuser(request, userid): def edit_serviceuser(request, userid):
""" Edite un utilisateur à partir de son id, """ Edite un utilisateur à partir de son id,
si l'id est différent de request.user, vérifie la possession du droit cableur """ si l'id est différent de request.user,
vérifie la possession du droit cableur """
try: try:
user = ServiceUser.objects.get(pk=userid) user = ServiceUser.objects.get(pk=userid)
except ServiceUser.DoesNotExist: except ServiceUser.DoesNotExist:
@ -196,18 +222,22 @@ def edit_serviceuser(request, userid):
user_object.set_password(user.cleaned_data['password']) user_object.set_password(user.cleaned_data['password'])
user_object.save() user_object.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in user.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in user.changed_data
))
messages.success(request, "L'user a bien été modifié") messages.success(request, "L'user a bien été modifié")
return redirect("/users/index_serviceusers") return redirect("/users/index_serviceusers")
return form({'userform': user}, 'users/user.html', request) return form({'userform': user}, 'users/user.html', request)
@login_required @login_required
@permission_required('infra') @permission_required('infra')
def del_serviceuser(request, userid): def del_serviceuser(request, userid):
"""Suppression d'un ou plusieurs serviceusers"""
try: try:
user = ServiceUser.objects.get(pk=userid) user = ServiceUser.objects.get(pk=userid)
except ServiceUser.DoesNotExist: except ServiceUser.DoesNotExist:
messages.error(request, u"Utilisateur inexistant" ) messages.error(request, u"Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
if request.method == "POST": if request.method == "POST":
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -215,7 +245,12 @@ def del_serviceuser(request, userid):
reversion.set_user(request.user) reversion.set_user(request.user)
messages.success(request, "L'user a été détruite") messages.success(request, "L'user a été détruite")
return redirect("/users/index_serviceusers/") return redirect("/users/index_serviceusers/")
return form({'objet': user, 'objet_name': 'serviceuser'}, 'users/delete.html', request) return form(
{'objet': user, 'objet_name': 'serviceuser'},
'users/delete.html',
request
)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
@ -241,28 +276,33 @@ def add_right(request, userid):
return redirect("/users/profil/" + userid) return redirect("/users/profil/" + userid)
return form({'userform': right}, 'users/user.html', request) return form({'userform': right}, 'users/user.html', request)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
def del_right(request): def del_right(request):
""" Supprimer un droit à un user, need droit bureau """ """ Supprimer un droit à un user, need droit bureau """
user_right_list = dict() user_right_list = dict()
for right in ListRight.objects.all(): for right in ListRight.objects.all():
user_right_list[right]= DelRightForm(right, request.POST or None) user_right_list[right] = DelRightForm(right, request.POST or None)
for keys, right_item in user_right_list.items(): for _keys, right_item in user_right_list.items():
if right_item.is_valid(): if right_item.is_valid():
right_del = right_item.cleaned_data['rights'] right_del = right_item.cleaned_data['rights']
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Retrait des droit %s" % ','.join(str(deleted_right) for deleted_right in right_del)) reversion.set_comment("Retrait des droit %s" % ','.join(
str(deleted_right) for deleted_right in right_del
))
right_del.delete() right_del.delete()
messages.success(request, "Droit retiré avec succès") messages.success(request, "Droit retiré avec succès")
return redirect("/users/") return redirect("/users/")
return form({'userform': user_right_list}, 'users/del_right.html', request) return form({'userform': user_right_list}, 'users/del_right.html', request)
@login_required @login_required
@permission_required('bofh') @permission_required('bofh')
def add_ban(request, userid): def add_ban(request, userid):
""" Ajouter un banissement, nécessite au moins le droit bofh (a fortiori bureau) """ Ajouter un banissement, nécessite au moins le droit bofh
(a fortiori bureau)
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
try: try:
user = User.objects.get(pk=userid) user = User.objects.get(pk=userid)
@ -273,7 +313,7 @@ def add_ban(request, userid):
ban = BanForm(request.POST or None, instance=ban_instance) ban = BanForm(request.POST or None, instance=ban_instance)
if ban.is_valid(): if ban.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
ban_object = ban.save() _ban_object = ban.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "Bannissement ajouté") messages.success(request, "Bannissement ajouté")
@ -285,10 +325,12 @@ def add_ban(request, userid):
) )
return form({'userform': ban}, 'users/user.html', request) return form({'userform': ban}, 'users/user.html', request)
@login_required @login_required
@permission_required('bofh') @permission_required('bofh')
def edit_ban(request, banid): def edit_ban(request, banid):
""" Editer un bannissement, nécessite au moins le droit bofh (a fortiori bureau) """ Editer un bannissement, nécessite au moins le droit bofh
(a fortiori bureau)
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
try: try:
ban_instance = Ban.objects.get(pk=banid) ban_instance = Ban.objects.get(pk=banid)
@ -300,23 +342,31 @@ def edit_ban(request, banid):
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
ban.save() ban.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in ban.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in ban.changed_data
))
messages.success(request, "Bannissement modifié") messages.success(request, "Bannissement modifié")
return redirect("/users/") return redirect("/users/")
return form({'userform': ban}, 'users/user.html', request) return form({'userform': ban}, 'users/user.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def add_whitelist(request, userid): def add_whitelist(request, userid):
""" Accorder un accès gracieux, temporaire ou permanent. Need droit cableur """ Accorder un accès gracieux, temporaire ou permanent.
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, raison obligatoire""" Need droit cableur
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
raison obligatoire"""
try: try:
user = User.objects.get(pk=userid) user = User.objects.get(pk=userid)
except User.DoesNotExist: except User.DoesNotExist:
messages.error(request, "Utilisateur inexistant") messages.error(request, "Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
whitelist_instance = Whitelist(user=user) whitelist_instance = Whitelist(user=user)
whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance) whitelist = WhitelistForm(
request.POST or None,
instance=whitelist_instance
)
if whitelist.is_valid(): if whitelist.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
whitelist.save() whitelist.save()
@ -331,30 +381,40 @@ def add_whitelist(request, userid):
) )
return form({'userform': whitelist}, 'users/user.html', request) return form({'userform': whitelist}, 'users/user.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def edit_whitelist(request, whitelistid): def edit_whitelist(request, whitelistid):
""" Editer un accès gracieux, temporaire ou permanent. Need droit cableur """ Editer un accès gracieux, temporaire ou permanent.
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, raison obligatoire""" Need droit cableur
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
raison obligatoire"""
try: try:
whitelist_instance = Whitelist.objects.get(pk=whitelistid) whitelist_instance = Whitelist.objects.get(pk=whitelistid)
except Whitelist.DoesNotExist: except Whitelist.DoesNotExist:
messages.error(request, "Entrée inexistante") messages.error(request, "Entrée inexistante")
return redirect("/users/") return redirect("/users/")
whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance) whitelist = WhitelistForm(
request.POST or None,
instance=whitelist_instance
)
if whitelist.is_valid(): if whitelist.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
whitelist.save() whitelist.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in whitelist.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in whitelist.changed_data
))
messages.success(request, "Whitelist modifiée") messages.success(request, "Whitelist modifiée")
return redirect("/users/") return redirect("/users/")
return form({'userform': whitelist}, 'users/user.html', request) return form({'userform': whitelist}, 'users/user.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def add_school(request): def add_school(request):
""" Ajouter un établissement d'enseignement à la base de donnée, need cableur""" """ Ajouter un établissement d'enseignement à la base de donnée,
need cableur"""
school = SchoolForm(request.POST or None) school = SchoolForm(request.POST or None)
if school.is_valid(): if school.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -365,30 +425,37 @@ def add_school(request):
return redirect("/users/index_school/") return redirect("/users/index_school/")
return form({'userform': school}, 'users/user.html', request) return form({'userform': school}, 'users/user.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def edit_school(request, schoolid): def edit_school(request, schoolid):
""" Editer un établissement d'enseignement à partir du schoolid dans la base de donnée, need cableur""" """ Editer un établissement d'enseignement à partir du schoolid dans
la base de donnée, need cableur"""
try: try:
school_instance = School.objects.get(pk=schoolid) school_instance = School.objects.get(pk=schoolid)
except School.DoesNotExist: except School.DoesNotExist:
messages.error(request, u"Entrée inexistante" ) messages.error(request, u"Entrée inexistante")
return redirect("/users/") return redirect("/users/")
school = SchoolForm(request.POST or None, instance=school_instance) school = SchoolForm(request.POST or None, instance=school_instance)
if school.is_valid(): if school.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
school.save() school.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in school.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in school.changed_data
))
messages.success(request, "Établissement modifié") messages.success(request, "Établissement modifié")
return redirect("/users/index_school/") return redirect("/users/index_school/")
return form({'userform': school}, 'users/user.html', request) return form({'userform': school}, 'users/user.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def del_school(request): def del_school(request):
""" Supprimer un établissement d'enseignement à la base de donnée, need cableur """ Supprimer un établissement d'enseignement à la base de donnée,
Objet protégé, possible seulement si aucun user n'est affecté à l'établissement """ need cableur
Objet protégé, possible seulement si aucun user n'est affecté à
l'établissement """
school = DelSchoolForm(request.POST or None) school = DelSchoolForm(request.POST or None)
if school.is_valid(): if school.is_valid():
school_dels = school.cleaned_data['schools'] school_dels = school.cleaned_data['schools']
@ -406,6 +473,7 @@ def del_school(request):
return redirect("/users/index_school/") return redirect("/users/index_school/")
return form({'userform': school}, 'users/user.html', request) return form({'userform': school}, 'users/user.html', request)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
def add_listright(request): def add_listright(request):
@ -421,29 +489,38 @@ def add_listright(request):
return redirect("/users/index_listright/") return redirect("/users/index_listright/")
return form({'userform': listright}, 'users/user.html', request) return form({'userform': listright}, 'users/user.html', request)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
def edit_listright(request, listrightid): def edit_listright(request, listrightid):
""" Editer un groupe/droit, necessite droit bureau, à partir du listright id """ """ Editer un groupe/droit, necessite droit bureau,
à partir du listright id """
try: try:
listright_instance = ListRight.objects.get(pk=listrightid) listright_instance = ListRight.objects.get(pk=listrightid)
except ListRight.DoesNotExist: except ListRight.DoesNotExist:
messages.error(request, u"Entrée inexistante" ) messages.error(request, u"Entrée inexistante")
return redirect("/users/") return redirect("/users/")
listright = ListRightForm(request.POST or None, instance=listright_instance) listright = ListRightForm(
request.POST or None,
instance=listright_instance
)
if listright.is_valid(): if listright.is_valid():
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
listright.save() listright.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in listright.changed_data)) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
field for field in listright.changed_data
))
messages.success(request, "Droit modifié") messages.success(request, "Droit modifié")
return redirect("/users/index_listright/") return redirect("/users/index_listright/")
return form({'userform': listright}, 'users/user.html', request) return form({'userform': listright}, 'users/user.html', request)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
def del_listright(request): def del_listright(request):
""" Supprimer un ou plusieurs groupe, possible si il est vide, need droit bureau """ """ Supprimer un ou plusieurs groupe, possible si il est vide, need droit
bureau """
listright = DelListRightForm(request.POST or None) listright = DelListRightForm(request.POST or None)
if listright.is_valid(): if listright.is_valid():
listright_dels = listright.cleaned_data['listrights'] listright_dels = listright.cleaned_data['listrights']
@ -461,6 +538,7 @@ def del_listright(request):
return redirect("/users/index_listright/") return redirect("/users/index_listright/")
return form({'userform': listright}, 'users/user.html', request) return form({'userform': listright}, 'users/user.html', request)
@login_required @login_required
@permission_required('bureau') @permission_required('bureau')
def mass_archive(request): def mass_archive(request):
@ -469,7 +547,10 @@ def mass_archive(request):
to_archive_list = [] to_archive_list = []
if to_archive_date.is_valid(): if to_archive_date.is_valid():
date = to_archive_date.cleaned_data['date'] date = to_archive_date.cleaned_data['date']
to_archive_list = [user for user in User.objects.exclude(state=User.STATE_ARCHIVE) if not user.end_access() or user.end_access() < date] to_archive_list = [user for user in
User.objects.exclude(state=User.STATE_ARCHIVE)
if not user.end_access()
or user.end_access() < date]
if "valider" in request.POST: if "valider" in request.POST:
for user in to_archive_list: for user in to_archive_list:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -477,15 +558,22 @@ def mass_archive(request):
user.save() user.save()
reversion.set_user(request.user) reversion.set_user(request.user)
reversion.set_comment("Archivage") reversion.set_comment("Archivage")
messages.success(request, "%s users ont été archivés" % len(to_archive_list)) messages.success(request, "%s users ont été archivés" % len(
to_archive_list
))
return redirect("/users/") return redirect("/users/")
return form({'userform': to_archive_date, 'to_archive_list': to_archive_list}, 'users/mass_archive.html', request) return form(
{'userform': to_archive_date, 'to_archive_list': to_archive_list},
'users/mass_archive.html',
request
)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index(request): def index(request):
""" Affiche l'ensemble des users, need droit cableur """ """ Affiche l'ensemble des users, need droit cableur """
options, created = GeneralOption.objects.get_or_create() options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
users_list = User.objects.select_related('room').order_by('state', 'name') users_list = User.objects.select_related('room').order_by('state', 'name')
paginator = Paginator(users_list, pagination_number) paginator = Paginator(users_list, pagination_number)
@ -500,13 +588,15 @@ def index(request):
users_list = paginator.page(paginator.num_pages) users_list = paginator.page(paginator.num_pages)
return render(request, 'users/index.html', {'users_list': users_list}) return render(request, 'users/index.html', {'users_list': users_list})
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index_ban(request): def index_ban(request):
""" Affiche l'ensemble des ban, need droit cableur """ """ Affiche l'ensemble des ban, need droit cableur """
options, created = GeneralOption.objects.get_or_create() options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
ban_list = Ban.objects.order_by('date_start').select_related('user').reverse() ban_list = Ban.objects.order_by('date_start')\
.select_related('user').reverse()
paginator = Paginator(ban_list, pagination_number) paginator = Paginator(ban_list, pagination_number)
page = request.GET.get('page') page = request.GET.get('page')
try: try:
@ -519,13 +609,15 @@ def index_ban(request):
ban_list = paginator.page(paginator.num_pages) ban_list = paginator.page(paginator.num_pages)
return render(request, 'users/index_ban.html', {'ban_list': ban_list}) return render(request, 'users/index_ban.html', {'ban_list': ban_list})
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index_white(request): def index_white(request):
""" Affiche l'ensemble des whitelist, need droit cableur """ """ Affiche l'ensemble des whitelist, need droit cableur """
options, created = GeneralOption.objects.get_or_create() options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
white_list = Whitelist.objects.select_related('user').order_by('date_start') white_list = Whitelist.objects.select_related('user')\
.order_by('date_start')
paginator = Paginator(white_list, pagination_number) paginator = Paginator(white_list, pagination_number)
page = request.GET.get('page') page = request.GET.get('page')
try: try:
@ -542,84 +634,106 @@ def index_white(request):
{'white_list': white_list} {'white_list': white_list}
) )
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index_school(request): def index_school(request):
""" Affiche l'ensemble des établissement, need droit cableur """ """ Affiche l'ensemble des établissement, need droit cableur """
school_list = School.objects.order_by('name') school_list = School.objects.order_by('name')
return render(request, 'users/index_schools.html', {'school_list':school_list}) return render(
request,
'users/index_schools.html',
{'school_list': school_list}
)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index_listright(request): def index_listright(request):
""" Affiche l'ensemble des droits , need droit cableur """ """ Affiche l'ensemble des droits , need droit cableur """
listright_list = ListRight.objects.order_by('listright') listright_list = ListRight.objects.order_by('listright')
return render(request, 'users/index_listright.html', {'listright_list':listright_list}) return render(
request,
'users/index_listright.html',
{'listright_list': listright_list}
)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index_serviceusers(request): def index_serviceusers(request):
""" Affiche les users de services (pour les accès ldap)""" """ Affiche les users de services (pour les accès ldap)"""
serviceusers_list = ServiceUser.objects.order_by('pseudo') serviceusers_list = ServiceUser.objects.order_by('pseudo')
return render(request, 'users/index_serviceusers.html', {'serviceusers_list':serviceusers_list}) return render(
request,
'users/index_serviceusers.html',
{'serviceusers_list': serviceusers_list}
)
@login_required @login_required
def history(request, object, id): def history(request, object_name, object_id):
""" Affichage de l'historique : (acl, argument) """ Affichage de l'historique : (acl, argument)
user : self or cableur, userid, user : self or cableur, userid,
ban : self or cableur, banid, ban : self or cableur, banid,
whitelist : self or cableur, whitelistid, whitelist : self or cableur, whitelistid,
school : cableur, schoolid, school : cableur, schoolid,
listright : cableur, listrightid """ listright : cableur, listrightid """
if object == 'user': if object_name == 'user':
try: try:
object_instance = User.objects.get(pk=id) object_instance = User.objects.get(pk=object_id)
except User.DoesNotExist: except User.DoesNotExist:
messages.error(request, "Utilisateur inexistant") messages.error(request, "Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.has_perms(('cableur',)) and object_instance != request.user: if not request.user.has_perms(('cableur',)) and\
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit cableur") object_instance != request.user:
messages.error(request, "Vous ne pouvez pas afficher\
l'historique d'un autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
elif object == 'serviceuser' and request.user.has_perms(('cableur',)): elif object_name == 'serviceuser' and request.user.has_perms(('cableur',)):
try: try:
object_instance = ServiceUser.objects.get(pk=id) object_instance = ServiceUser.objects.get(pk=object_id)
except ServiceUser.DoesNotExist: except ServiceUser.DoesNotExist:
messages.error(request, "User service inexistant") messages.error(request, "User service inexistant")
return redirect("/users/") return redirect("/users/")
elif object == 'ban': elif object_name == 'ban':
try: try:
object_instance = Ban.objects.get(pk=id) object_instance = Ban.objects.get(pk=object_id)
except Ban.DoesNotExist: except Ban.DoesNotExist:
messages.error(request, "Bannissement inexistant") messages.error(request, "Bannissement inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.has_perms(('cableur',)) and object_instance.user != request.user: if not request.user.has_perms(('cableur',)) and\
messages.error(request, "Vous ne pouvez pas afficher les bans d'un autre user que vous sans droit cableur") object_instance.user != request.user:
messages.error(request, "Vous ne pouvez pas afficher les bans\
d'un autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
elif object == 'whitelist': elif object_name == 'whitelist':
try: try:
object_instance = Whitelist.objects.get(pk=id) object_instance = Whitelist.objects.get(pk=object_id)
except Whiltelist.DoesNotExist: except Whitelist.DoesNotExist:
messages.error(request, "Whitelist inexistant") messages.error(request, "Whitelist inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.has_perms(('cableur',)) and object_instance.user != request.user: if not request.user.has_perms(('cableur',)) and\
messages.error(request, "Vous ne pouvez pas afficher les whitelist d'un autre user que vous sans droit cableur") object_instance.user != request.user:
messages.error(request, "Vous ne pouvez pas afficher les\
whitelist d'un autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
elif object == 'school' and request.user.has_perms(('cableur',)): elif object_name == 'school' and request.user.has_perms(('cableur',)):
try: try:
object_instance = School.objects.get(pk=id) object_instance = School.objects.get(pk=object_id)
except School.DoesNotExist: except School.DoesNotExist:
messages.error(request, "Ecole inexistante") messages.error(request, "Ecole inexistante")
return redirect("/users/") return redirect("/users/")
elif object == 'listright' and request.user.has_perms(('cableur',)): elif object_name == 'listright' and request.user.has_perms(('cableur',)):
try: try:
object_instance = ListRight.objects.get(pk=id) object_instance = ListRight.objects.get(pk=object_id)
except ListRight.DoesNotExist: except ListRight.DoesNotExist:
messages.error(request, "Droit inexistant") messages.error(request, "Droit inexistant")
return redirect("/users/") return redirect("/users/")
else: else:
messages.error(request, "Objet inconnu") messages.error(request, "Objet inconnu")
return redirect("/users/") return redirect("/users/")
options, created = GeneralOption.objects.get_or_create() options, _created = GeneralOption.objects.get_or_create()
pagination_number = options.pagination_number pagination_number = options.pagination_number
reversions = Version.objects.get_for_object(object_instance) reversions = Version.objects.get_for_object(object_instance)
paginator = Paginator(reversions, pagination_number) paginator = Paginator(reversions, pagination_number)
@ -632,7 +746,11 @@ def history(request, object, id):
except EmptyPage: except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results. # If page is out of range (e.g. 9999), deliver last page of results.
reversions = paginator.page(paginator.num_pages) reversions = paginator.page(paginator.num_pages)
return render(request, 're2o/history.html', {'reversions': reversions, 'object': object_instance}) return render(
request,
're2o/history.html',
{'reversions': reversions, 'object': object_instance}
)
@login_required @login_required
@ -640,6 +758,7 @@ def mon_profil(request):
""" Lien vers profil, renvoie request.id à la fonction """ """ Lien vers profil, renvoie request.id à la fonction """
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
@login_required @login_required
def profil(request, userid): def profil(request, userid):
""" Affiche un profil, self or cableur, prend un userid en argument """ """ Affiche un profil, self or cableur, prend un userid en argument """
@ -649,14 +768,19 @@ def profil(request, userid):
messages.error(request, "Utilisateur inexistant") messages.error(request, "Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.has_perms(('cableur',)) and users != request.user: if not request.user.has_perms(('cableur',)) and users != request.user:
messages.error(request, "Vous ne pouvez pas afficher un autre user que vous sans droit cableur") messages.error(request, "Vous ne pouvez pas afficher un autre user\
que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
machines = Machine.objects.filter(user=users).select_related('user').prefetch_related('interface_set__domain__extension').prefetch_related('interface_set__ipv4__ip_type__extension').prefetch_related('interface_set__type').prefetch_related('interface_set__domain__related_domain__extension') machines = Machine.objects.filter(user=users).select_related('user')\
.prefetch_related('interface_set__domain__extension')\
.prefetch_related('interface_set__ipv4__ip_type__extension')\
.prefetch_related('interface_set__type')\
.prefetch_related('interface_set__domain__related_domain__extension')
factures = Facture.objects.filter(user=users) factures = Facture.objects.filter(user=users)
bans = Ban.objects.filter(user=users) bans = Ban.objects.filter(user=users)
whitelists = Whitelist.objects.filter(user=users) whitelists = Whitelist.objects.filter(user=users)
list_droits = Right.objects.filter(user=users) list_droits = Right.objects.filter(user=users)
options, created = OptionalUser.objects.get_or_create() options, _created = OptionalUser.objects.get_or_create()
user_solde = options.user_solde user_solde = options.user_solde
return render( return render(
request, request,
@ -672,46 +796,56 @@ def profil(request, userid):
} }
) )
def reset_password(request): def reset_password(request):
""" Reintialisation du mot de passe si mdp oublié """ """ Reintialisation du mot de passe si mdp oublié """
userform = ResetPasswordForm(request.POST or None) userform = ResetPasswordForm(request.POST or None)
if userform.is_valid(): if userform.is_valid():
try: try:
user = User.objects.get(pseudo=userform.cleaned_data['pseudo'],email=userform.cleaned_data['email']) user = User.objects.get(
pseudo=userform.cleaned_data['pseudo'],
email=userform.cleaned_data['email']
)
except User.DoesNotExist: except User.DoesNotExist:
messages.error(request, "Cet utilisateur n'existe pas") messages.error(request, "Cet utilisateur n'existe pas")
return form({'userform': userform}, 'users/user.html', request) return form({'userform': userform}, 'users/user.html', request)
user.reset_passwd_mail(request) user.reset_passwd_mail(request)
messages.success(request, "Un mail pour l'initialisation du mot de passe a été envoyé") messages.success(request, "Un mail pour l'initialisation du mot\
de passe a été envoyé")
redirect("/") redirect("/")
return form({'userform': userform}, 'users/user.html', request) return form({'userform': userform}, 'users/user.html', request)
def process(request, token): def process(request, token):
"""Process, lien pour la reinitialisation du mot de passe"""
valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) valid_reqs = Request.objects.filter(expires_at__gt=timezone.now())
req = get_object_or_404(valid_reqs, token=token) req = get_object_or_404(valid_reqs, token=token)
if req.type == Request.PASSWD: if req.type == Request.PASSWD:
return process_passwd(request, req) return process_passwd(request, req)
elif req.type == Request.EMAIL:
return process_email(request, req=req)
else: else:
messages.error(request, "Entrée incorrecte, contactez un admin") messages.error(request, "Entrée incorrecte, contactez un admin")
redirect("/") redirect("/")
def process_passwd(request, req): def process_passwd(request, req):
"""Process le changeemnt de mot de passe, renvoie le formulaire
demandant le nouveau password"""
u_form = PassForm(request.POST or None) u_form = PassForm(request.POST or None)
user = req.user user = req.user
if u_form.is_valid(): if u_form.is_valid():
return password_change_action(u_form, user, request, req=req) return password_change_action(u_form, user, request, req=req)
return form({'userform': u_form}, 'users/user.html', request) return form({'userform': u_form}, 'users/user.html', request)
""" Framework Rest """
class JSONResponse(HttpResponse): class JSONResponse(HttpResponse):
""" Framework Rest """
def __init__(self, data, **kwargs): def __init__(self, data, **kwargs):
content = JSONRenderer().render(data) content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json' kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs) super(JSONResponse, self).__init__(content, **kwargs)
@csrf_exempt @csrf_exempt
@login_required @login_required
@permission_required('serveur') @permission_required('serveur')
@ -721,4 +855,3 @@ def mailing(request):
mails = all_has_access().values('email').distinct() mails = all_has_access().values('email').distinct()
seria = MailSerializer(mails, many=True) seria = MailSerializer(mails, many=True)
return JSONResponse(seria.data) return JSONResponse(seria.data)