1
0
mirror of https://gitlab.crans.org/mediatek/med.git synced 2024-11-30 06:53:03 +00:00

Initial commit, projet med

This commit is contained in:
Med 2017-06-30 03:25:07 +02:00
parent 287d60db54
commit 9501a10eac
71 changed files with 1795 additions and 591 deletions

View File

@ -11,7 +11,7 @@ Ce portail minimaliste permet aux utilisateurs de s'identifier. Leurs mac sont c
## Installation des dépendances ## Installation des dépendances
L'installation comporte 2 parties : le serveur web où se trouve le depot portail_captif ainsi que toutes ses dépendances, et le serveur bdd (mysql ou pgsql). Ces 2 serveurs peuvent en réalité être la même machine, ou séparés (recommandé en production). L'installation comporte 2 parties : le serveur web où se trouve le depot med ainsi que toutes ses dépendances, et le serveur bdd (mysql ou pgsql). Ces 2 serveurs peuvent en réalité être la même machine, ou séparés (recommandé en production).
Le serveur web sera nommé serveur A, le serveur bdd serveur B . Le serveur web sera nommé serveur A, le serveur bdd serveur B .
### Prérequis sur le serveur A ### Prérequis sur le serveur A
@ -49,19 +49,19 @@ Sur le serveur B, installer mysql ou postgresql, dans la version jessie ou stret
### Installation sur le serveur principal A ### Installation sur le serveur principal A
Cloner le dépot portail_captif à partir du gitlab, par exemple dans /var/www/portail_captif. Cloner le dépot med à partir du gitlab, par exemple dans /var/www/med.
Ensuite, il faut créer le fichier settings_local.py dans le sous dossier portail_captif, un settings_local.example.py est présent. Les options sont commentées, et des options par défaut existent. Ensuite, il faut créer le fichier settings_local.py dans le sous dossier med, un settings_local.example.py est présent. Les options sont commentées, et des options par défaut existent.
En particulier, il est nécessaire de générer un login/mdp admin pour le ldap et un login/mdp pour l'utilisateur sql (cf ci-dessous), à mettre dans settings_local.py En particulier, il est nécessaire de générer un login/mdp admin pour le ldap et un login/mdp pour l'utilisateur sql (cf ci-dessous), à mettre dans settings_local.py
### Installation du serveur mysql/postgresql sur B ### Installation du serveur mysql/postgresql sur B
Sur le serveur mysql ou postgresl, il est nécessaire de créer une base de donnée portail_captif, ainsi qu'un user portail_captif et un mot de passe associé. Ne pas oublier de faire écouter le serveur mysql ou postgresql avec les acl nécessaire pour que A puisse l'utiliser. Sur le serveur mysql ou postgresl, il est nécessaire de créer une base de donnée med, ainsi qu'un user med et un mot de passe associé. Ne pas oublier de faire écouter le serveur mysql ou postgresql avec les acl nécessaire pour que A puisse l'utiliser.
Voici les étapes à éxecuter pour mysql : Voici les étapes à éxecuter pour mysql :
* CREATE DATABASE portail_captif; * CREATE DATABASE med;
* CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password'; * CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
* GRANT ALL PRIVILEGES ON portail_captif.* TO 'newuser'@'localhost'; * GRANT ALL PRIVILEGES ON med.* TO 'newuser'@'localhost';
* FLUSH PRIVILEGES; * FLUSH PRIVILEGES;
Si les serveurs A et B ne sont pas la même machine, il est nécessaire de remplacer localhost par l'ip avec laquelle A contacte B dans les commandes du dessus. Si les serveurs A et B ne sont pas la même machine, il est nécessaire de remplacer localhost par l'ip avec laquelle A contacte B dans les commandes du dessus.
@ -71,7 +71,7 @@ Une fois ces commandes effectuées, ne pas oublier de vérifier que newuser et p
Normalement à cette étape, le ldap et la bdd sql sont configurées correctement. Normalement à cette étape, le ldap et la bdd sql sont configurées correctement.
Il faut alors lancer dans le dépot portail_captif '''python3 manage.py migrate''' qui va structurer initialement la base de données. Il faut alors lancer dans le dépot med '''python3 manage.py migrate''' qui va structurer initialement la base de données.
Les migrations sont normalement comitées au fur et à mesure, néanmoins cette étape peut crasher, merci de reporter les bugs. Les migrations sont normalement comitées au fur et à mesure, néanmoins cette étape peut crasher, merci de reporter les bugs.
## Démarer le site web ## Démarer le site web
@ -81,13 +81,12 @@ Pour apache2 :
* apt install apache2 * apt install apache2
* apt install libapache2-mod-wsgi-py3 (pour le module wsgi) * apt install libapache2-mod-wsgi-py3 (pour le module wsgi)
portail_captif/wsgi.py permet de fonctionner avec apache2 en production med/wsgi.py permet de fonctionner avec apache2 en production
Pour nginx : Pour nginx :
* apt install nginx * apt install nginx
* apt install gunicorn3 * apt install gunicorn3
Utilisez alors un site nginx qui proxifie vers une socket gunicorn. Ensuite, utilisez les fichier portail_captif.service et portail_captif.socket avec systemd pour lancer le sous process gunicorn, présents dans portail_captif/ .
## Configuration avancée ## Configuration avancée

View File

@ -3,7 +3,7 @@ import os
import sys import sys
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portail_captif.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "med.settings")
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line

View File

@ -24,9 +24,11 @@ from .settings import SITE_NAME
def context_user(request): def context_user(request):
user = request.user user = request.user
is_admin = user.is_admin if hasattr(user,'is_admin') else False is_perm = user.has_perms(['perm'])
is_bureau = user.has_perms(['bureau'])
return { return {
'is_admin' : is_admin, 'is_perm' : is_perm,
'is_bureau': is_bureau,
'request_user': user, 'request_user': user,
'site_name': SITE_NAME, 'site_name': SITE_NAME,
} }

View File

@ -21,7 +21,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """
Django settings for portail_captif project. Django settings for med project.
Generated by 'django-admin startproject' using Django 1.8.13. Generated by 'django-admin startproject' using Django 1.8.13.
@ -45,7 +45,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Auth definition # Auth definition
PASSWORD_HASHERS = ( PASSWORD_HASHERS = (
'portail_captif.login.SSHAPasswordHasher', 'med.login.SSHAPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
) )
@ -65,7 +65,9 @@ INSTALLED_APPS = (
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'bootstrap3', 'bootstrap3',
'users', 'users',
'portail_captif', 'med',
'media',
'search',
'reversion' 'reversion'
) )
@ -80,7 +82,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
) )
ROOT_URLCONF = 'portail_captif.urls' ROOT_URLCONF = 'med.urls'
TEMPLATES = [ TEMPLATES = [
{ {
@ -96,13 +98,13 @@ TEMPLATES = [
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'django.template.context_processors.request', 'django.template.context_processors.request',
'portail_captif.context_processors.context_user', 'med.context_processors.context_user',
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'portail_captif.wsgi.application' WSGI_APPLICATION = 'med.wsgi.application'
# Internationalization # Internationalization

View File

@ -1,4 +1,4 @@
{% extends "portail_captif/sidebar.html" %} {% extends "med/sidebar.html" %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block content %} {% block content %}
<h2>Historique de {{ object }}</h2> <h2>Historique de {{ object }}</h2>
{% include "portail_captif/aff_history.html" with reversions=reversions %} {% include "med/aff_history.html" with reversions=reversions %}
<br /> <br />
<br /> <br />
<br /> <br />

View File

@ -0,0 +1,38 @@
{% extends "med/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% load staticfiles %}
{% block title %}Accueil{% endblock %}
{% block content %}
<h1>Bienvenue sur {{ site_name }} !</h1>
<h1>Liens utiles</h1>
{% endblock %}

View File

@ -20,7 +20,7 @@
# 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.
"""portail_captif URL Configuration """med URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see: The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.8/topics/http/urls/ https://docs.djangoproject.com/en/1.8/topics/http/urls/
@ -46,4 +46,6 @@ urlpatterns = [
url('^', include('django.contrib.auth.urls')), url('^', include('django.contrib.auth.urls')),
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'^media/', include('media.urls', namespace='media')),
url(r'^search/', include('search.urls', namespace='search')),
] ]

View File

@ -24,7 +24,7 @@ from django.shortcuts import render
from django.shortcuts import get_object_or_404 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 django.template import Context, RequestContext, loader
from portail_captif.settings import services_urls from med.settings import services_urls
def form(ctx, template, request): def form(ctx, template, request):
c = ctx c = ctx
@ -40,4 +40,4 @@ def index(request):
services[i][key] = s services[i][key] = s
i = i + 1 if i < 2 else 0 i = i + 1 if i < 2 else 0
return form({'services_urls': services}, 'portail_captif/index.html', request) return form({'services_urls': services}, 'med/index.html', request)

View File

@ -21,7 +21,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """
WSGI config for portail_captif project. WSGI config for med project.
It exposes the WSGI callable as a module-level variable named ``application``. It exposes the WSGI callable as a module-level variable named ``application``.
@ -34,9 +34,8 @@ from django.core.wsgi import get_wsgi_application
from os.path import dirname from os.path import dirname
import sys import sys
# On démarre le système du portail
sys.path.append(dirname(dirname(__file__))) sys.path.append(dirname(dirname(__file__)))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portail_captif.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "med.settings")
application = get_wsgi_application() application = get_wsgi_application()

0
media/__init__.py Normal file
View File

20
media/admin.py Normal file
View File

@ -0,0 +1,20 @@
from reversion.admin import VersionAdmin
from django.contrib import admin
from django.contrib.auth.models import Group
from .models import Auteur, Emprunt, Media
class AuteurAdmin(VersionAdmin):
list_display = ('nom',)
class MediaAdmin(VersionAdmin):
list_display = ('titre','cote')
class EmpruntAdmin(VersionAdmin):
list_display = ('media','user','date_emprunt', 'date_rendu', 'permanencier_emprunt', 'permanencier_rendu')
admin.site.register(Auteur, AuteurAdmin)
admin.site.register(Media, MediaAdmin)
admin.site.register(Emprunt, EmpruntAdmin)

5
media/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class MediaConfig(AppConfig):
name = 'media'

View File

@ -1,3 +1,7 @@
# 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 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Goulven Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
@ -15,27 +19,27 @@
# 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.
#
# Ce script est appellé avant le démarage du portail, il insère les bonnes règles
# dans l'iptables et active le routage
from django.core.management.base import BaseCommand, CommandError from django.forms import ModelForm, Form, ValidationError
from django import forms
from .models import Auteur, Media, Emprunt
from users.models import restore_iptables, create_ip_set, fill_ipset, disable_iptables, apply
from portail_captif.settings import AUTORIZED_INTERFACES
class Command(BaseCommand):
help = 'Mets en place iptables et le set ip au démarage'
def handle(self, *args, **options):
# Destruction de l'iptables
disable_iptables()
# Desactivation du routage sur les bonnes if
for interface in AUTORIZED_INTERFACES:
apply(["sudo", "-n", "sysctl", "net.ipv6.conf.%s.forwarding=0" % interface])
apply(["sudo", "-n", "sysctl", "net.ipv4.conf.%s.forwarding=0" % interface])
class AuteurForm(ModelForm):
class Meta:
model = Auteur
fields = '__all__'
class MediaForm(ModelForm):
class Meta:
model = Media
fields = '__all__'
class EmpruntForm(ModelForm):
class Meta:
model = Emprunt
fields = ['media']
class EditEmpruntForm(ModelForm):
class Meta:
model = Emprunt
fields = ['media', 'permanencier_emprunt', 'permanencier_rendu', 'date_rendu']

View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-28 10:35
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Auteur',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nom', models.CharField(max_length=255)),
],
),
migrations.CreateModel(
name='Emprunt',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_emprunt', models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')),
('date_rendu', models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')),
],
),
migrations.CreateModel(
name='Media',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('titre', models.CharField(max_length=255)),
('cote', models.CharField(max_length=31)),
],
),
migrations.AddField(
model_name='emprunt',
name='media',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='media.Media'),
),
migrations.AddField(
model_name='emprunt',
name='permanencier_emprunt',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_emprunt', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='emprunt',
name='permanencier_rendu',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_rendu', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='emprunt',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-29 12:38
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('media', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='media',
name='auteur',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.PROTECT, to='media.Auteur'),
preserve_default=False,
),
]

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-29 13:36
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('media', '0002_media_auteur'),
]
operations = [
migrations.AlterField(
model_name='emprunt',
name='date_rendu',
field=models.DateTimeField(blank=True, help_text='%d/%m/%y %H:%M:%S'),
),
migrations.AlterField(
model_name='emprunt',
name='permanencier_rendu',
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_rendu', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-29 13:39
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('media', '0003_auto_20170629_1536'),
]
operations = [
migrations.AlterField(
model_name='emprunt',
name='date_rendu',
field=models.DateTimeField(blank=True, help_text='%d/%m/%y %H:%M:%S', null=True),
),
migrations.AlterField(
model_name='emprunt',
name='permanencier_rendu',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_rendu', to=settings.AUTH_USER_MODEL),
),
]

View File

29
media/models.py Normal file
View File

@ -0,0 +1,29 @@
from django.db import models
class Auteur(models.Model):
nom = models.CharField(max_length=255)
def __str__(self):
return self.nom
class Media(models.Model):
titre = models.CharField(max_length=255)
cote = models.CharField(max_length=31)
auteur = models.ForeignKey('Auteur', on_delete=models.PROTECT)
# type = TODO
def __str__(self):
return str(self.titre) + ' - ' + str(self.auteur)
class Emprunt(models.Model):
media = models.ForeignKey('Media', on_delete=models.PROTECT)
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
date_emprunt = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
date_rendu = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S', blank=True, null=True)
permanencier_emprunt = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='user_permanencier_emprunt')
permanencier_rendu = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='user_permanencier_rendu', blank=True, null=True)

View File

@ -0,0 +1,40 @@
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
<table class="table table-striped">
<thead>
<tr>
<th>Nom</th>
<th></th>
</tr>
</thead>
{% for auteur in auteurs_list %}
<tr>
<td>{{ auteur.nom }}</td>
<td>{% include 'buttons/edit.html' with href='media:edit-auteur' id=auteur.id %}
{% include 'buttons/suppr.html' with href='media:del-auteur' id=auteur.id %}
{% include 'buttons/history.html' with href='media:history' name='auteur' id=auteur.id %}</td>
</tr>
{% endfor %}
</table>

View File

@ -0,0 +1,66 @@
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
<table class="table table-striped">
<thead>
<tr>
<th>Media</th>
<th>Utilisateur</th>
<th>Date emprunt</th>
<th>Permanencier emprunt</th>
<th>Date rendu</th>
<th>Permanencier rendu</th>
<th></th>
</tr>
</thead>
{% for emprunt in emprunts_list %}
<tr>
<td>{{ emprunt.media }}</td>
<td>
<a href="{% url 'users:profil' userid=emprunt.user.id %}"><b>{{ emprunt.user }}</b></a>
</td>
<td>{{ emprunt.date_emprunt }}</td>
<td>{{ emprunt.permanencier_emprunt }}</td>
<td>{% if not emprunt.date_rendu %}<a class="btn btn-primary btn-sm" role="button" href="{% url 'media:retour-emprunt' emprunt.id %}"><i class="glyphicon glyphicon-ok"></i> Retour</a>{% else %}{{ emprunt.date_rendu }}{% endif %}</td>
<td>{{ emprunt.permanencier_rendu }}</td>
<td>
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="editionemprunt" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Modifier
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="editionemprunt">
{% if is_perm %}
<li><a href="{% url 'media:edit-emprunt' emprunt.id %}"><i class="glyphicon glyphicon-edit"></i> Editer</a></li>
{% endif %}
{% if is_bureau %}
<li><a href="{% url 'media:del-emprunt' emprunt.id %}"><i class="glyphicon glyphicon-trash"></i> Supprimer</a></li>
{% endif %}
<li><a href="{% url 'media:history' 'emprunt' emprunt.id %}"><i class="glyphicon glyphicon-time"></i> Historique</a></li>
</ul>
</div>
</td>
</tr>
{% endfor %}
</table>

View File

@ -0,0 +1,44 @@
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
<table class="table table-striped">
<thead>
<tr>
<th>Titre</th>
<th>Auteur</th>
<th>Cote</th>
<th></th>
</tr>
</thead>
{% for media in medias_list %}
<tr>
<td>{{ media.titre }}</td>
<td>{{ media.auteur }}</td>
<td>{{ media.cote }}</td>
<td>{% include 'buttons/edit.html' with href='media:edit-media' id=media.id %}
{% include 'buttons/suppr.html' with href='media:del-media' id=media.id %}
{% include 'buttons/history.html' with href='media:history' name='media' id=media.id %}</td>
</tr>
{% endfor %}
</table>

View File

@ -0,0 +1,40 @@
{% extends "media/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Création et modification de media{% endblock %}
{% block content %}
<form class="form" method="post">
{% csrf_token %}
<h4>Attention, voulez-vous vraiment supprimer cet objet {{ objet_name }} ( {{ objet }} ) ?</h4>
{% bootstrap_button "Confirmer" button_type="submit" icon="trash" %}
</form>
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends "media/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Auteurs{% endblock %}
{% block content %}
<h2>Liste des auteurs</h2>
{% if is_perm %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'media:add-auteur' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un auteur</a>
{% endif %}
{% include "media/aff_auteurs.html" with auteurs_list=auteurs_list %}
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "media/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Emprunts{% endblock %}
{% block content %}
<h2>Liste des emprunts</h2>
{% include "media/aff_emprunts.html" with emprunts_list=emprunts_list %}
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends "media/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Media{% endblock %}
{% block content %}
<h2>Liste des medias</h2>
{% if is_perm %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'media:add-media' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un media</a>
{% endif %}
{% include "media/aff_medias.html" with medias_list=medias_list %}
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,42 @@
{% extends "media/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Création et modification de media{% endblock %}
{% block content %}
{% bootstrap_form_errors mediaform %}
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_form mediaform %}
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
</form>
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends "base.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% block sidebar %}
<a class="list-group-item list-group-item-info" href="{% url "media:index" %}">
<i class="glyphicon glyphicon-list"></i>
Emprunts
</a>
<a class="list-group-item list-group-item-info" href="{% url "media:index-auteurs" %}">
<i class="glyphicon glyphicon-list"></i>
Auteurs
</a>
<a class="list-group-item list-group-item-info" href="{% url "media:index-medias" %}">
<i class="glyphicon glyphicon-list"></i>
Medias
</a>
{% endblock %}

3
media/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

45
media/urls.py Normal file
View File

@ -0,0 +1,45 @@
# 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.
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^add_auteur/$', views.add_auteur, name='add-auteur'),
url(r'^edit_auteur/(?P<auteurid>[0-9]+)$', views.edit_auteur, name='edit-auteur'),
url(r'^del_auteur/(?P<auteurid>[0-9]+)$', views.del_auteur, name='del-auteur'),
url(r'^index_auteurs/$', views.index_auteurs, name='index-auteurs'),
url(r'^history/(?P<object>auteur)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^add_media/$', views.add_media, name='add-media'),
url(r'^edit_media/(?P<mediaid>[0-9]+)$', views.edit_media, name='edit-media'),
url(r'^del_media/(?P<mediaid>[0-9]+)$', views.del_media, name='del-media'),
url(r'^index_medias/$', views.index_medias, name='index-medias'),
url(r'^history/(?P<object>media)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^add_emprunt/(?P<userid>[0-9]+)$', views.add_emprunt, name='add-emprunt'),
url(r'^retour_emprunt/(?P<empruntid>[0-9]+)$', views.retour_emprunt, name='retour-emprunt'),
url(r'^edit_emprunt/(?P<empruntid>[0-9]+)$', views.edit_emprunt, name='edit-emprunt'),
url(r'^del_emprunt/(?P<empruntid>[0-9]+)$', views.del_emprunt, name='del-emprunt'),
url(r'^index_emprunts/$', views.index, name='index'),
url(r'^history/(?P<object>emprunt)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^$', views.index, name='index'),
]

252
media/views.py Normal file
View File

@ -0,0 +1,252 @@
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template.context_processors import csrf
from django.template import Context, RequestContext, loader
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from .forms import AuteurForm, MediaForm, EmpruntForm, EditEmpruntForm
from .models import Auteur, Media, Emprunt
from users.models import User
from django.db import transaction
from reversion import revisions as reversion
from reversion.models import Version
from med.settings import PAGINATION_NUMBER as pagination_number
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render(request, template, c)
@login_required
@permission_required('perm')
def add_auteur(request):
auteur = AuteurForm(request.POST or None)
if auteur.is_valid():
with transaction.atomic(), reversion.create_revision():
auteur.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "L'auteur a été ajouté")
return redirect("/media/index_auteurs/")
return form({'mediaform': auteur}, 'media/media.html', request)
@login_required
@permission_required('perm')
def edit_auteur(request, auteurid):
try:
auteur_instance = Auteur.objects.get(pk=auteurid)
except Auteur.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_auteurs/")
auteur = AuteurForm(request.POST or None, instance=auteur_instance)
if auteur.is_valid():
with transaction.atomic(), reversion.create_revision():
auteur.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in auteur.changed_data))
messages.success(request, "Auteur modifié")
return redirect("/media/index_auteurs/")
return form({'mediaform': auteur}, 'media/media.html', request)
@login_required
@permission_required('perm')
def del_auteur(request, auteurid):
try:
auteur_instance = Auteur.objects.get(pk=auteurid)
except Auteur.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_auteurs/")
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
auteur_instance.delete()
reversion.set_user(request.user)
messages.success(request, "L'auteur a été détruit")
return redirect("/media/index_auteurs")
return form({'objet': auteur_instance, 'objet_name': 'auteur'}, 'media/delete.html', request)
@login_required
@permission_required('perm')
def add_media(request):
media = MediaForm(request.POST or None)
if media.is_valid():
with transaction.atomic(), reversion.create_revision():
media.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Le media a été ajouté")
return redirect("/media/index_medias/")
return form({'mediaform': media}, 'media/media.html', request)
@login_required
@permission_required('perm')
def edit_media(request, mediaid):
try:
media_instance = Media.objects.get(pk=mediaid)
except Media.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_medias/")
media = MediaForm(request.POST or None, instance=media_instance)
if media.is_valid():
with transaction.atomic(), reversion.create_revision():
media.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in media.changed_data))
messages.success(request, "Media modifié")
return redirect("/media/index_medias/")
return form({'mediaform': media}, 'media/media.html', request)
@login_required
@permission_required('perm')
def del_media(request, mediaid):
try:
media_instance = Media.objects.get(pk=mediaid)
except Media.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_medias/")
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
media_instance.delete()
reversion.set_user(request.user)
messages.success(request, "Le media a été détruit")
return redirect("/media/index_medias")
return form({'objet': media_instance, 'objet_name': 'media'}, 'media/delete.html', request)
@login_required
@permission_required('perm')
def add_emprunt(request, userid):
try:
user = User.objects.get(pk=userid)
except User.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_emprunts/")
emprunts_en_cours = Emprunt.objects.filter(date_rendu=None, user=user).count()
if emprunts_en_cours >= user.maxemprunt:
messages.error(request, "Maximum d'emprunts atteint de l'user %s" % user.maxemprunt)
return redirect("/media/index_emprunts/")
emprunt = EmpruntForm(request.POST or None)
if emprunt.is_valid():
emprunt = emprunt.save(commit=False)
emprunt.user = user
emprunt.permanencier_emprunt = request.user
emprunt.date_emprunt = timezone.now()
with transaction.atomic(), reversion.create_revision():
emprunt.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Le emprunt a été ajouté")
return redirect("/media/index_emprunts/")
return form({'mediaform': emprunt}, 'media/media.html', request)
@login_required
@permission_required('perm')
def edit_emprunt(request, empruntid):
try:
emprunt_instance = Emprunt.objects.get(pk=empruntid)
except Emprunt.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_emprunts/")
emprunt = EditEmpruntForm(request.POST or None, instance=emprunt_instance)
if emprunt.is_valid():
with transaction.atomic(), reversion.create_revision():
emprunt.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in emprunt.changed_data))
messages.success(request, "Emprunt modifié")
return redirect("/media/index_emprunts/")
return form({'mediaform': emprunt}, 'media/media.html', request)
@login_required
@permission_required('bureau')
def retour_emprunt(request, empruntid):
try:
emprunt_instance = Emprunt.objects.get(pk=empruntid)
except Emprunt.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_emprunts/")
with transaction.atomic(), reversion.create_revision():
emprunt_instance.permanencier_rendu = request.user
emprunt_instance.date_rendu = timezone.now()
emprunt_instance.save()
reversion.set_user(request.user)
messages.success(request, "Retour enregistré")
return redirect("/media/index_emprunts/")
@login_required
@permission_required('bureau')
def del_emprunt(request, empruntid):
try:
emprunt_instance = Emprunt.objects.get(pk=empruntid)
except Emprunt.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/media/index_emprunts/")
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
emprunt_instance.delete()
reversion.set_user(request.user)
messages.success(request, "L'emprunt a été détruit")
return redirect("/media/index_emprunts")
return form({'objet': emprunt_instance, 'objet_name': 'emprunt'}, 'media/delete.html', request)
@login_required
def index_auteurs(request):
auteurs_list = Auteur.objects.all()
return render(request, 'media/index_auteurs.html', {'auteurs_list':auteurs_list})
@login_required
def index_medias(request):
medias_list = Media.objects.all()
return render(request, 'media/index_medias.html', {'medias_list':medias_list})
@login_required
def index(request):
if request.user.has_perms(['perm']):
emprunts_list = Emprunt.objects.all()
else:
emprunts_list = Emprunt.objects.filter(user=request.user)
return render(request, 'media/index_emprunts.html', {'emprunts_list':emprunts_list})
@login_required
def history(request, object, id):
if object == 'auteur':
try:
object_instance = Auteur.objects.get(pk=id)
except Auteur.DoesNotExist:
messages.error(request, "Auteur inexistant")
return redirect("/media/index_auteurs")
elif object == 'media':
try:
object_instance = Media.objects.get(pk=id)
except Media.DoesNotExist:
messages.error(request, "Media inexistant")
return redirect("/media/index_medias")
elif object == 'emprunt':
try:
object_instance = Emprunt.objects.get(pk=id)
except Emprunt.DoesNotExist:
messages.error(request, "Emprunt inexistant")
return redirect("/media/index_emprunts")
reversions = Version.objects.get_for_object(object_instance)
paginator = Paginator(reversions, pagination_number)
page = request.GET.get('page')
try:
reversions = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
reversions = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
reversions = paginator.page(paginator.num_pages)
return render(request, 'med/history.html', {'reversions': reversions, 'object': object_instance})

View File

@ -1,26 +0,0 @@
[Unit]
Description=Crans Portail Captif
Requires=nginx.service
Requires=portail_captif.socket
After=nginx.service
After=network-online.target
[Service]
Type=forking
User=root
Group=root
PIDFile=/run/portail_captif.pid
WorkingDirectory=/var/www/portail_captif/
ExecStartPre=/usr/bin/python3 /var/www/portail_captif/manage.py start_portail
ExecStart=/usr/bin/gunicorn3 portail_captif.wsgi:application --pid=/run/portail_captif.pid --name www-data --user www-data --group www-data --daemon --log-file /var/log/gunicorn/portail_captif.log --log-level=info --bind=unix:///tmp/gunicorn-portail_captif.sock --workers=1
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
ExecStopPost=/usr/bin/python3 /var/www/portail_captif/manage.py stop_portail
Restart=on-failure
RestartSec=65
StartLimitInterval=60
StartLimitBurst=2
[Install]
WantedBy=multi-user.target
Also=portail_captif.socket

View File

@ -1,8 +0,0 @@
[Unit]
Description=Socket Portail Captif
[Socket]
ListenStream=/tmp/gunicorn-portail_captif.sock
[Install]
WantedBy=sockets.target

View File

@ -1,92 +0,0 @@
{% extends "portail_captif/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% load staticfiles %}
{% block title %}Accueil{% endblock %}
{% block content %}
<h1>Bienvenue sur {{ site_name }} !</h1>
<h4>Bienvenue à toi ! Ce portail mis en place par le Cr@ns te permet d'accéder gratuitement à Internet sur le campus durant ton passage</h4>
<center>
<div class="thumbnail">
<img src="/static/logo/logo.png" alt="logo">
</div>
</center>
<div class="row">
<div class="col-sm-6 col-md-6">
<div class="col-12">
<div class="thumbnail">
<div class="caption">
<h3>Inscription</h3>
<p>Si vous n'avez pas de compte, inscrivez-vous pour bénéficier d'un accès à internet !</p>
<p><a href="{% url 'users:new-user' %}" class="btn btn-primary" role="button">Inscription</a></p>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-md-6">
<div class="col-12">
<div class="thumbnail">
<div class="caption">
<h3>Indentification</h3>
<p>Si avez déjà un compte, identifiez-vous pour accèder à Internet</p>
<p><a href="{% url 'login' %}" class="btn btn-primary" role="button">Identification</a></p>
</div>
</div>
</div>
</div>
</div>
<h1>Liens utiles</h1>
<div class="row">
{% for col in services_urls %}
<div class="col-sm-6 col-md-6">
{% for key, s in col.items %}
<div class="col-12">
<div class="thumbnail">
<img src="{% static "logo/"|add:s.logo %}" alt="{{ key }}">
<div class="caption">
<h3>{{ key }}</h3>
<p>{{ s.description }}</p>
<p><a href="{{ s.url }}" class="btn btn-primary" role="button">Accéder</a></p>
</div>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
<p>Compte tenu des obligations de traçabilité des connexions à internet, vous vous engagez à fournir des informations exactes lors de votre inscription conformément à l'article 441-1 du nouveau code pénal.
Vous êtes seul responsable de l'utilisation du service, conformément aux articles L32 et L33 du code des postes et télécomunications éléctroniques.</p>
{% endblock %}

0
search/__init__.py Normal file
View File

3
search/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
search/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class SearchConfig(AppConfig):
name = 'search'

49
search/forms.py Normal file
View File

@ -0,0 +1,49 @@
# 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.
from django.db import models
from django import forms
from django.forms import Form
from django.forms import ModelForm
CHOICES = (
('0', 'Actifs'),
('1', 'Désactivés'),
('2', 'Archivés'),
)
CHOICES2 = (
('0', 'Utilisateurs'),
('1', 'Media'),
('2', 'Emprunts'),
)
class SearchForm(Form):
search_field = forms.CharField(label = 'Search', max_length = 100)
class SearchFormPlus(Form):
search_field = forms.CharField(label = 'Search', max_length = 100, required=False)
filtre = forms.MultipleChoiceField(label="Filtre utilisateurs", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES)
affichage = forms.MultipleChoiceField(label="Filtre affichage", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES2)
date_deb = forms.DateField(required=False, label="Date de début", help_text='DD/MM/YYYY', input_formats=['%d/%m/%Y'])
date_fin = forms.DateField(required=False, help_text='DD/MM/YYYY', input_formats=['%d/%m/%Y'], label="Date de fin")

View File

3
search/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,50 @@
{% extends "search/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Résultats de la recherche{% endblock %}
{% block content %}
{% if users_list %}
<h2>Résultats dans les utilisateurs</h2>
{% include "users/aff_users.html" with users_list=users_list %}
{% endif%}
{% if emprunts_list %}
<h2>Résultats dans les emprunt : </h2>
{% include "media/aff_emprunts.html" with emprunts_list=emprunts_list %}
{% endif %}
{% if medias_list %}
<h2>Résultats dans les media : </h2>
{% include "media/aff_medias.html" with medias_list=medias_list %}
{% endif %}
{% if not users_list and not emprunts_list and not medias_list %}
<h3>Aucun résultat</h3>
{% endif %}
<h6>(Seulement les {{ max_result }} premiers résultats sont affichés dans chaque catégorie)</h6>
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,43 @@
{% extends "search/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Recherche{% endblock %}
{% block content %}
{% bootstrap_form_errors searchform %}
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_form searchform %}
{% bootstrap_button "Search" button_type="submit" icon="search" %}
</form>
<br />
<br />
<br />
<br />
<br />
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% block sidebar %}
<a class="list-group-item list-group-item-warning" href="{% url "search:search" %}">
<i class="glyphicon glyphicon-search"></i>
Recherche simple
</a>
<a class="list-group-item list-group-item-warning" href="{% url "search:searchp" %}">
<i class="glyphicon glyphicon-zoom-in"></i>
Recherche avancée
</a>
{% endblock %}

3
search/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

30
search/urls.py Normal file
View File

@ -0,0 +1,30 @@
# 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.
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.search, name='search'),
url(r'^avance/$', views.searchp, name='searchp'),
]

109
search/views.py Normal file
View File

@ -0,0 +1,109 @@
# 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.
# App de recherche pour re2o
# Augustin lemesle, Gabriel Détraz, Goulven Kermarec
# Gplv2
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.template.context_processors import csrf
from django.template import Context, RequestContext, loader
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from users.models import User
from search.forms import SearchForm, SearchFormPlus
from media.models import Media, Emprunt
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render(request, template, c)
def search_result(search, type, request):
date_deb = None
date_fin = None
states=[]
aff=[]
if(type):
aff = search.cleaned_data['affichage']
states = search.cleaned_data['filtre']
date_deb = search.cleaned_data['date_deb']
date_fin = search.cleaned_data['date_fin']
date_query = Q()
if aff==[]:
aff = ['0','1','2']
if date_deb != None:
date_query = date_query & Q(date_emprunt__gte=date_deb)
if date_fin != None:
date_query = date_query & Q(date_emprunt__lte=date_fin)
search = search.cleaned_data['search_field']
query1 = Q()
for s in states:
query1 = query1 | Q(state = s)
connexion = []
recherche = {'users_list': None, 'emprunts_list' : None, 'medias_list' : None}
if request.user.has_perms(('perm',)):
query = Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search)
else:
query = (Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search)) & Q(user = request.user)
for i in aff:
if i == '0':
query_user_list = Q(pseudo__icontains = search) | Q(name__icontains = search) | Q(surname__icontains = search) & query1
if request.user.has_perms(('perm',)):
recherche['users_list'] = User.objects.filter(query_user_list).order_by('state', 'surname')
else :
recherche['users_list'] = User.objects.filter(query_user_list & Q(id=request.user.id)).order_by('state', 'surname')
if i == '1':
recherche['emprunts_list'] = Emprunt.objects.filter(query & date_query).order_by('date_emprunt').reverse()
if i == '2':
recherche['medias_list'] = Media.objects.filter(Q(auteur__nom__icontains = search) | Q(titre__icontains = search))
search_display_page = 5
for r in recherche:
if recherche[r] != None:
recherche[r] = recherche[r][:search_display_page]
recherche.update({'max_result': search_display_page})
return recherche
@login_required
def search(request):
search = SearchForm(request.POST or None)
if search.is_valid():
return form(search_result(search, False, request), 'search/index.html',request)
return form({'searchform' : search}, 'search/search.html', request)
@login_required
def searchp(request):
search = SearchFormPlus(request.POST or None)
if search.is_valid():
return form(search_result(search, True, request), 'search/index.html',request)
return form({'searchform' : search}, 'search/search.html', request)

View File

@ -54,10 +54,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<div class="collapse navbar-collapse" id="myNavbar"> <div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="{% url "users:mon-profil" %}">Mon profil</a></li> <li><a href="{% url "users:mon-profil" %}">Mon profil</a></li>
{% if is_admin %} {% if is_perm %}
<li><a href="{% url "users:index" %}">Utilisateurs</a></li> <li><a href="{% url "users:index" %}">Utilisateurs</a></li>
{% endif %} {% endif %}
<li><a href="{% url "media:index" %}">Media</a></li>
</ul> </ul>
<div class="col-sm-3 col-md-3 navbar-right">
<form action="{% url "search:search"%}" method="POST" class="navbar-form" role="search">
{% csrf_token %}
<div class="input-group">
<input type="text" class="form-control" placeholder="Search" name="search_field" id="search-term">
<div class="input-group-btn">
<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
<a href="{% url "search:searchp" %}" class="btn btn-default" role="button"><i class="glyphicon glyphicon-plus"></i></a>
</div>
</div>
</form>
</div>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li> <li>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}

View File

@ -25,7 +25,7 @@ from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import User, Machine, Request from .models import User, Right, ListRight, Request
from .forms import UserChangeForm, UserCreationForm from .forms import UserChangeForm, UserCreationForm
@ -42,8 +42,11 @@ class UserAdmin(admin.ModelAdmin):
class RequestAdmin(admin.ModelAdmin): class RequestAdmin(admin.ModelAdmin):
list_display = ('user', 'type', 'created_at', 'expires_at') list_display = ('user', 'type', 'created_at', 'expires_at')
class MachineAdmin(VersionAdmin): class RightAdmin(VersionAdmin):
list_display = ('mac_address','proprio') list_display = ('user', 'right')
class ListRightAdmin(VersionAdmin):
list_display = ('listright',)
class UserAdmin(VersionAdmin, BaseUserAdmin): class UserAdmin(VersionAdmin, BaseUserAdmin):
# The forms to add and change user instances # The forms to add and change user instances
@ -72,9 +75,10 @@ class UserAdmin(VersionAdmin, BaseUserAdmin):
ordering = ('pseudo',) ordering = ('pseudo',)
filter_horizontal = () filter_horizontal = ()
admin.site.register(Machine, MachineAdmin)
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
admin.site.register(Request, RequestAdmin) admin.site.register(Request, RequestAdmin)
admin.site.register(ListRight, ListRightAdmin)
admin.site.register(Right, RightAdmin)
# Now register the new UserAdmin... # Now register the new UserAdmin...
admin.site.unregister(User) admin.site.unregister(User)
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)

View File

@ -1,44 +0,0 @@
# 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.
#
# Ce script est appellé avant le démarage du portail, il insère les bonnes règles
# dans l'iptables et active le routage
from django.core.management.base import BaseCommand, CommandError
from users.models import restore_iptables, create_ip_set, fill_ipset, apply
from portail_captif.settings import AUTORIZED_INTERFACES
class Command(BaseCommand):
help = 'Mets en place iptables et le set ip au démarage'
def handle(self, *args, **options):
# Creation de l'ipset
create_ip_set()
# Remplissage avec les macs autorisées
fill_ipset()
# Restauration de l'iptables
restore_iptables()
# Activation du routage sur les bonnes if
for interface in AUTORIZED_INTERFACES:
apply(["sudo", "-n", "sysctl", "net.ipv6.conf.%s.forwarding=1" % interface])
apply(["sudo", "-n", "sysctl", "net.ipv4.conf.%s.forwarding=1" % interface])

View File

@ -1,13 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-28 10:30
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
] ]
@ -15,16 +18,20 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='User', name='User',
fields=[ fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('name', models.CharField(max_length=255)), ('name', models.CharField(max_length=255)),
('surname', models.CharField(max_length=255)), ('surname', models.CharField(max_length=255)),
('email', models.EmailField(max_length=254)), ('email', models.EmailField(blank=True, max_length=254, null=True)),
('state', models.IntegerField(default=0, choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE')])), ('telephone', models.CharField(blank=True, max_length=15, null=True)),
('pseudo', models.CharField(max_length=32, unique=True)), ('adresse', models.CharField(blank=True, max_length=255, null=True)),
('maxemprunt', models.IntegerField(default=5)),
('state', models.IntegerField(choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE')], default=0)),
('pseudo', models.CharField(help_text='Doit contenir uniquement des lettres, chiffres, ou tirets. ', max_length=32, unique=True)),
('comment', models.CharField(blank=True, help_text='Commentaire, promo', max_length=255)),
('registered', models.DateTimeField(auto_now_add=True)), ('registered', models.DateTimeField(auto_now_add=True)),
('admin', models.BooleanField(default=False)), ('right', models.IntegerField(choices=[(0, 'BASIC'), (1, 'PERM'), (2, 'BUREAU')], default=0)),
], ],
options={ options={
'abstract': False, 'abstract': False,
@ -33,8 +40,8 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Request', name='Request',
fields=[ fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.CharField(max_length=2, choices=[('PW', 'Mot de passe'), ('EM', 'Email')])), ('type', models.CharField(choices=[('PW', 'Mot de passe'), ('EM', 'Email')], max_length=2)),
('token', models.CharField(max_length=32)), ('token', models.CharField(max_length=32)),
('created_at', models.DateTimeField(auto_now_add=True)), ('created_at', models.DateTimeField(auto_now_add=True)),
('expires_at', models.DateTimeField()), ('expires_at', models.DateTimeField()),

View File

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='comment',
field=models.CharField(blank=True, help_text='Commentaire, promo', max_length=255),
),
migrations.AlterField(
model_name='user',
name='pseudo',
field=models.CharField(unique=True, help_text='Doit contenir uniquement des lettres, chiffres, ou tirets. ', max_length=32),
),
]

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-29 12:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='maxemprunt',
field=models.IntegerField(default=5, help_text="Maximum d'emprunts autorisés"),
),
migrations.AlterField(
model_name='user',
name='right',
field=models.IntegerField(choices=[(0, 'BASIC'), (1, 'PERM'), (2, 'BUREAU')], default=0, help_text='Droits accordés'),
),
]

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-29 19:56
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0002_auto_20170629_1438'),
]
operations = [
migrations.CreateModel(
name='ListRight',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('listright', models.CharField(max_length=255, unique=True)),
('details', models.CharField(blank=True, help_text='Description', max_length=255)),
],
),
migrations.CreateModel(
name='Right',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('right', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.ListRight')),
],
),
migrations.RemoveField(
model_name='user',
name='right',
),
migrations.AddField(
model_name='right',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
migrations.AlterUniqueTogether(
name='right',
unique_together=set([('user', 'right')]),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import macaddress.fields
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('users', '0002_auto_20170610_1550'),
]
operations = [
migrations.CreateModel(
name='Machines',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('mac_address', macaddress.fields.MACAddressField(unique=True, max_length=17, integer=False)),
('proprio', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)),
],
),
]

View File

@ -1,32 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import macaddress.fields
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('users', '0003_machines'),
]
operations = [
migrations.CreateModel(
name='Machine',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('mac_address', macaddress.fields.MACAddressField(max_length=17, unique=True, integer=False)),
('proprio', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)),
],
),
migrations.RemoveField(
model_name='machines',
name='proprio',
),
migrations.DeleteModel(
name='Machines',
),
]

View File

@ -28,9 +28,8 @@ from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.functional import cached_property from django.utils.functional import cached_property
from macaddress.fields import MACAddressField
from portail_captif.settings import GENERIC_IPSET_COMMAND, IPSET_NAME, REQ_EXPIRE_HRS,FORBIDEN_INTERFACES, SERVER_SELF_IP, AUTORIZED_INTERFACES, PORTAIL_ACTIVE from med.settings import MAX_EMPRUNT, REQ_EXPIRE_HRS
import re, uuid import re, uuid
import datetime import datetime
@ -38,116 +37,6 @@ from django.utils import timezone
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
import subprocess import subprocess
def apply(cmd):
return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def mac_from_ip(ip):
cmd = ['/usr/sbin/arp','-na',ip]
p = apply(cmd)
output, errors = p.communicate()
if output is not None :
mac_addr = output.decode().split()[3]
return str(mac_addr)
else:
return None
def create_ip_set():
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["create",IPSET_NAME,"hash:mac","hashsize","1024","maxelem","65536"]
apply(command_to_execute)
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["flush",IPSET_NAME]
apply(command_to_execute)
return
def fill_ipset():
all_machines = Machine.objects.filter(proprio__in=User.objects.filter(state=User.STATE_ACTIVE))
ipset = "%s\nCOMMIT\n" % '\n'.join(["add %s %s" % (IPSET_NAME, str(machine.mac_address)) for machine in all_machines])
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["restore"]
process = apply(command_to_execute)
process.communicate(input=ipset.encode('utf-8'))
return
class iptables:
def __init__(self):
self.nat = "\n*nat"
self.mangle = "\n*mangle"
self.filter = "\n*filter"
def commit(self, chain):
self.add(chain, "COMMIT\n")
def add(self, chain, value):
setattr(self, chain, getattr(self, chain) + "\n" + value)
def init_filter(self, subchain, decision="ACCEPT"):
self.add("filter", ":" + subchain + " " + decision)
def init_nat(self, subchain, decision="ACCEPT"):
self.add("nat", ":" + subchain + " " + decision)
def init_mangle(self, subchain, decision="ACCEPT"):
self.add("mangle", ":" + subchain + " " + decision)
def jump(self, chain, subchainA, subchainB):
self.add(chain, "-A " + subchainA + " -j " + subchainB)
def gen_filter(ipt):
ipt.init_filter("INPUT")
ipt.init_filter("FORWARD")
ipt.init_filter("OUTPUT")
for interface in FORBIDEN_INTERFACES:
ipt.add("filter", "-A FORWARD -o %s -j REJECT --reject-with icmp-port-unreachable" % interface)
ipt.commit("filter")
return ipt
def gen_nat(ipt, nat_active=True):
ipt.init_nat("PREROUTING")
ipt.init_nat("INPUT")
ipt.init_nat("OUTPUT")
ipt.init_nat("POSTROUTING")
if nat_active:
ipt.init_nat("CAPTIF", decision="-")
ipt.jump("nat", "PREROUTING", "CAPTIF")
ipt.jump("nat", "POSTROUTING", "MASQUERADE")
if PORTAIL_ACTIVE:
ipt.add("nat", "-A CAPTIF -m set ! --match-set %s src -j DNAT --to-destination %s" % (IPSET_NAME, SERVER_SELF_IP))
ipt.jump("nat", "CAPTIF", "RETURN")
ipt.commit("nat")
return ipt
def gen_mangle(ipt):
ipt.init_mangle("PREROUTING")
ipt.init_mangle("INPUT")
ipt.init_mangle("FORWARD")
ipt.init_mangle("OUTPUT")
ipt.init_mangle("POSTROUTING")
for interface in AUTORIZED_INTERFACES:
ipt.add("mangle", """-A PREROUTING -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL " """ % interface)
ipt.commit("mangle")
return ipt
def restore_iptables():
""" Restrore l'iptables pour la création du portail. Est appellé par root au démarage du serveur"""
ipt = iptables()
gen_mangle(ipt)
gen_nat(ipt)
gen_filter(ipt)
global_chain = ipt.nat + ipt.filter + ipt.mangle
command_to_execute = ["sudo","-n","/sbin/iptables-restore"]
process = apply(command_to_execute)
process.communicate(input=global_chain.encode('utf-8'))
return
def disable_iptables():
""" Insère une iptables minimaliste sans nat"""
ipt = iptables()
gen_mangle(ipt)
gen_filter(ipt)
gen_nat(ipt, nat_active=False)
global_chain = ipt.nat + ipt.filter + ipt.mangle
command_to_execute = ["sudo","-n","/sbin/iptables-restore"]
process = apply(command_to_execute)
process.communicate(input=global_chain.encode('utf-8'))
return
class UserManager(BaseUserManager): class UserManager(BaseUserManager):
def _create_user(self, pseudo, name, surname, email, password=None, su=False): def _create_user(self, pseudo, name, surname, email, password=None, su=False):
@ -193,15 +82,17 @@ class User(AbstractBaseUser):
(2, 'STATE_ARCHIVE'), (2, 'STATE_ARCHIVE'),
) )
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
surname = models.CharField(max_length=255) surname = models.CharField(max_length=255)
email = models.EmailField() email = models.EmailField(null=True, blank=True)
telephone = models.CharField(max_length=15, null=True, blank=True)
adresse = models.CharField(max_length=255, null=True, blank=True)
maxemprunt = models.IntegerField(default=MAX_EMPRUNT, help_text="Maximum d'emprunts autorisés")
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE) state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets. ") pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets. ")
comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True) comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
registered = models.DateTimeField(auto_now_add=True) registered = models.DateTimeField(auto_now_add=True)
admin = models.BooleanField(default=False)
USERNAME_FIELD = 'pseudo' USERNAME_FIELD = 'pseudo'
REQUIRED_FIELDS = ['name', 'surname', 'email'] REQUIRED_FIELDS = ['name', 'surname', 'email']
@ -218,7 +109,11 @@ class User(AbstractBaseUser):
@property @property
def is_admin(self): def is_admin(self):
return self.admin try:
Right.objects.get(user=self, right__listright='admin')
except Right.DoesNotExist:
return False
return True
@is_admin.setter @is_admin.setter
def is_admin(self, value): def is_admin(self, value):
@ -229,8 +124,10 @@ class User(AbstractBaseUser):
def has_perms(self, perms, obj=None): def has_perms(self, perms, obj=None):
for perm in perms: for perm in perms:
if perm=="admin": try:
return self.is_admin Right.objects.get(user=self, right__listright=perm)
return True
except Right.DoesNotExist:
return False return False
def get_full_name(self): def get_full_name(self):
@ -242,48 +139,33 @@ class User(AbstractBaseUser):
def has_perm(self, perm, obj=None): def has_perm(self, perm, obj=None):
return True return True
def has_right(self, right):
return Right.objects.filter(user=self).filter(right=ListRight.objects.get(listright=right)).exists()
def has_module_perms(self, app_label): def has_module_perms(self, app_label):
# Simplest version again # Simplest version again
return True return True
def get_admin_right(self):
admin, created = ListRight.objects.get_or_create(listright="admin")
return admin
def make_admin(self): def make_admin(self):
""" Make User admin """ """ Make User admin """
self.admin = True user_admin_right = Right(user=self, right=self.get_admin_right())
self.save() user_admin_right.save()
def un_admin(self): def un_admin(self):
self.admin = False try:
self.save() user_right = Right.objects.get(user=self,right=self.get_admin_right())
except Right.DoesNotExist:
def machines(self): return
return Machine.objects.filter(proprio=self) user_right.delete()
def __str__(self): def __str__(self):
return self.name + " " + self.surname return self.pseudo
class Machine(models.Model):
proprio = models.ForeignKey('User', on_delete=models.PROTECT)
mac_address = MACAddressField(integer=False, unique=True)
def add_to_set(self):
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["add",IPSET_NAME,str(self.mac_address)]
apply(command_to_execute)
def del_to_set(self):
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["del",IPSET_NAME,str(self.mac_address)]
apply(command_to_execute)
@receiver(post_save, sender=Machine)
def machine_post_save(sender, **kwargs):
machine = kwargs['instance']
machine.add_to_set()
@receiver(post_delete, sender=Machine)
def machine_post_delete(sender, **kwargs):
machine = kwargs['instance']
machine.del_to_set()
class Request(models.Model): class Request(models.Model):
PASSWD = 'PW' PASSWD = 'PW'
EMAIL = 'EM' EMAIL = 'EM'
@ -305,6 +187,27 @@ class Request(models.Model):
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
super(Request, self).save() super(Request, self).save()
class Right(models.Model):
PRETTY_NAME = "Droits affectés à des users"
user = models.ForeignKey('User', on_delete=models.PROTECT)
right = models.ForeignKey('ListRight', on_delete=models.PROTECT)
class Meta:
unique_together = ("user", "right")
def __str__(self):
return str(self.user) + " - " + str(self.right)
class ListRight(models.Model):
PRETTY_NAME = "Liste des droits existants"
listright = models.CharField(max_length=255, unique=True)
details = models.CharField(help_text="Description", max_length=255, blank=True)
def __str__(self):
return self.listright
class BaseInfoForm(ModelForm): class BaseInfoForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BaseInfoForm, self).__init__(*args, **kwargs) super(BaseInfoForm, self).__init__(*args, **kwargs)
@ -319,17 +222,8 @@ class BaseInfoForm(ModelForm):
'pseudo', 'pseudo',
'surname', 'surname',
'email', 'email',
] 'telephone',
'adresse',
class EditInfoForm(BaseInfoForm):
class Meta(BaseInfoForm.Meta):
fields = [
'name',
'pseudo',
'surname',
'comment',
'email',
'admin',
] ]
class InfoForm(BaseInfoForm): class InfoForm(BaseInfoForm):
@ -337,15 +231,13 @@ class InfoForm(BaseInfoForm):
fields = [ fields = [
'name', 'name',
'pseudo', 'pseudo',
'comment',
'surname', 'surname',
'email', 'email',
'admin', 'telephone',
'adresse',
'maxemprunt',
] ]
class UserForm(EditInfoForm):
class Meta(EditInfoForm.Meta):
fields = '__all__'
class PasswordForm(ModelForm): class PasswordForm(ModelForm):
class Meta: class Meta:
@ -357,7 +249,35 @@ class StateForm(ModelForm):
model = User model = User
fields = ['state'] fields = ['state']
class MachineForm(ModelForm): class RightForm(ModelForm):
def __init__(self, *args, **kwargs):
super(RightForm, self).__init__(*args, **kwargs)
self.fields['right'].label = 'Droit'
self.fields['right'].empty_label = "Choisir un nouveau droit"
class Meta: class Meta:
model = Machine model = Right
exclude = '__all__' fields = ['right']
class DelRightForm(Form):
rights = forms.ModelMultipleChoiceField(queryset=Right.objects.all(), label="Droits actuels", widget=forms.CheckboxSelectMultiple)
class ListRightForm(ModelForm):
class Meta:
model = ListRight
fields = ['listright', 'details']
def __init__(self, *args, **kwargs):
super(ListRightForm, self).__init__(*args, **kwargs)
self.fields['listright'].label = 'Nom du droit/groupe'
class NewListRightForm(ListRightForm):
class Meta(ListRightForm.Meta):
fields = '__all__'
class DelListRightForm(Form):
listrights = forms.ModelMultipleChoiceField(queryset=ListRight.objects.all(), label="Droits actuels", widget=forms.CheckboxSelectMultiple)

View File

@ -0,0 +1,44 @@
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
<table class="table table-striped">
<thead>
<tr>
<th>Droit</th>
<th>Details</th>
<th></th>
<th></th>
</tr>
</thead>
{% for listright in listright_list %}
<tr>
<td>{{ listright.listright }}</td>
<td>{{ listright.details }}</td>
<td class="text-right">
{% include 'buttons/edit.html' with href='users:edit-listright' id=listright.id %}
{% include 'buttons/history.html' with href='users:history' name='listright' id=listright.id %}
</td>
</tr>
{% endfor %}
</table>

View File

@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Nom</th> <th>Nom</th>
<th>Pseudo</th> <th>Pseudo</th>
<th>Mail</th> <th>Mail</th>
<th>Machines</th> <th>Max emprunts</th>
<th>Profil</th> <th>Profil</th>
</tr> </tr>
</thead> </thead>
@ -43,13 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ user.surname }}</td> <td>{{ user.surname }}</td>
<td>{{ user.pseudo }}</td> <td>{{ user.pseudo }}</td>
<td>{{ user.email }}</td> <td>{{ user.email }}</td>
<td><table class="table table-striped"> <td>{{ user.maxemprunt }}</td>
{% for machine in user.machines %}
<tr>
<td>{{ machine.mac_address }}</td>
</tr>
{% endfor %}
</table>
</td> </td>
<td><a href="{% url "users:profil" user.id%}" class="btn btn-primary btn-sm" role="button"><i class="glyphicon glyphicon-user"></i></a> <td><a href="{% url "users:profil" user.id%}" class="btn btn-primary btn-sm" role="button"><i class="glyphicon glyphicon-user"></i></a>
</td> </td>

View File

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "users/sidebar.html" %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en

View File

@ -0,0 +1,38 @@
{% extends "users/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
<h2>Liste des droits</h2>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-listright' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un droit ou groupe</a>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'users:del-listright' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs droits/groupes</a>
{% include "users/aff_listright.html" with listright_list=listright_list %}
<br />
<br />
<br />
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "users/sidebar.html" %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
@ -38,11 +38,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i class="glyphicon glyphicon-lock"></i> <i class="glyphicon glyphicon-lock"></i>
Changer le mot de passe Changer le mot de passe
</a> </a>
{% if is_admin %} {% if is_bureau %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:state' user.id %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'users:state' user.id %}">
<i class="glyphicon glyphicon-flash"></i> <i class="glyphicon glyphicon-flash"></i>
Changer le statut Changer le statut
</a> </a>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-right' user.id %}">
<i class="glyphicon glyphicon-ok"></i>
Ajouter un droit
</a>
{% endif %} {% endif %}
<a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'user' user.id %}"> <a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'user' user.id %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
@ -71,6 +75,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Date d'inscription</th> <th>Date d'inscription</th>
<td>{{ user.registered }}</td> <td>{{ user.registered }}</td>
</tr> </tr>
<tr>
<th>Adresse</th>
<td>{{ user.adresse }}</td>
<th>Telephone</th>
<td>{{ user.telephone }}</td>
</tr>
<tr>
<th>Emprunts maximums autorisés</th>
<td>{{ user.maxemprunt }}</td>
<th>Droits</th>
{% if list_droits %}
<td>{% for droit in list_droits %}{{ droit.right }}{% if list_droits|length != forloop.counter %} - {% endif %} {% endfor %}</td>
{% else %}
<td>Aucun</td>
{% endif %}
</tr>
<tr>
<th>Statut</th> <th>Statut</th>
{% if user.state == 0 %} {% if user.state == 0 %}
<td><font color="green">Actif</font></td> <td><font color="green">Actif</font></td>
@ -83,28 +104,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ user.last_login }}</td> <td>{{ user.last_login }}</td>
</tr> </tr>
</table> </table>
<h2>Machines enregistrées</h2> <h2>Emprunts</h2>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:capture' %}"> <h4><a class="btn btn-primary btn-sm" role="button" href="{% url 'media:add-emprunt' user.id %}"><i class="glyphicon glyphicon-flag"></i> Ajouter</a></h4>
<i class="glyphicon glyphicon-flash"></i> {% if emprunts_list %}
Enregistrer la machine utilisée actuellement {% include "media/aff_emprunts.html" with emprunts_list=emprunts_list %}
</a> {% else %}
<table class="table table-striped"> <p>Aucun emprunt</p>
<thead> {% endif %}
<tr>
<th>Adresse mac</th>
<th>Historique</th>
</tr>
</thead>
{% for machine in machines_list %}
<tr>
<td>{{ machine.mac_address }}</td>
<td><a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'machines' machine.id %}">
<i class="glyphicon glyphicon-time"></i>
</a>
</td>
</tr>
{% endfor %}
</table>
<br /> <br />
<br /> <br />
<br /> <br />

View File

@ -0,0 +1,48 @@
{% extends "base.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% block sidebar %}
{% if is_perm %}
<a class="list-group-item list-group-item-success" href="{% url "users:new-user" %}">
<i class="glyphicon glyphicon-plus"></i>
Créer un adhérent
</a>
<a class="list-group-item list-group-item-info" href="{% url "users:index" %}">
<i class="glyphicon glyphicon-list"></i>
Adhérents
</a>
<a class="list-group-item list-group-item-info" href="{% url "users:index-listright" %}">
<i class="glyphicon glyphicon-list"></i>
Droits
</a>
{% endif %}
{% if is_bureau %}
<a class="list-group-item list-group-item-danger" href="{% url "users:del-right" %}">
<i class="glyphicon glyphicon-trash"></i>
Retirer un droit
</a>
{% endif %}
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "users/sidebar.html" %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en

View File

@ -27,16 +27,21 @@ from . import views
app_name = 'users' app_name = 'users'
urlpatterns = [ urlpatterns = [
url(r'^new_user/$', views.new_user, name='new-user'), url(r'^new_user/$', views.new_user, name='new-user'),
url(r'^capture/$', views.capture, name='capture'),
url(r'^edit_info/(?P<userid>[0-9]+)$', views.edit_info, name='edit-info'), url(r'^edit_info/(?P<userid>[0-9]+)$', views.edit_info, name='edit-info'),
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'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'), url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'), url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
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'^del_listright/$', views.del_listright, name='del-listright'),
url(r'^index_listright/$', views.index_listright, name='index-listright'),
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'^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'^history/(?P<object>user)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>user)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>machines)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>listright)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^$', views.index, name='index'), url(r'^$', views.index, name='index'),
] ]

View File

@ -20,7 +20,7 @@
# 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.
# App de gestion des users pour portail_captif # App de gestion des users pour med
# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin # Goulven Kermarec, Gabriel Détraz, Lemesle Augustin
# Gplv2 # Gplv2
from django.shortcuts import get_object_or_404, render, redirect from django.shortcuts import get_object_or_404, render, redirect
@ -39,13 +39,12 @@ 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 users.models import User, MachineForm, Request from users.models import User, Request, ListRight, Right, DelListRightForm, NewListRightForm, ListRightForm, RightForm, DelRightForm
from users.models import EditInfoForm, InfoForm, BaseInfoForm, Machine, StateForm, mac_from_ip from users.models import InfoForm, BaseInfoForm, StateForm
from users.forms import PassForm, ResetPasswordForm from users.forms import PassForm, ResetPasswordForm
import ipaddress from media.models import Emprunt
import subprocess
from portail_captif.settings import REQ_EXPIRE_STR, EMAIL_FROM, ASSO_NAME, ASSO_EMAIL, SITE_NAME, CAPTIVE_IP_RANGE, CAPTIVE_WIFI, PAGINATION_NUMBER from med.settings import REQ_EXPIRE_STR, EMAIL_FROM, ASSO_NAME, ASSO_EMAIL, SITE_NAME, PAGINATION_NUMBER
def form(ctx, template, request): def form(ctx, template, request):
@ -85,6 +84,8 @@ def reset_passwd_mail(req, request):
return return
@login_required
@permission_required('perm')
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 = BaseInfoForm(request.POST or None) user = BaseInfoForm(request.POST or None)
@ -111,10 +112,10 @@ def edit_info(request, 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.is_admin and user != request.user: if not request.user.has_perms(('perm',)) and user != request.user:
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin") messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
if not request.user.is_admin: if not request.user.has_perms(('perm',)):
user = BaseInfoForm(request.POST or None, instance=user) user = BaseInfoForm(request.POST or None, instance=user)
else: else:
user = InfoForm(request.POST or None, instance=user) user = InfoForm(request.POST or None, instance=user)
@ -128,7 +129,7 @@ def edit_info(request, userid):
return form({'userform': user}, 'users/user.html', request) return form({'userform': user}, 'users/user.html', request)
@login_required @login_required
@permission_required('admin') @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:
@ -156,7 +157,7 @@ def password(request, 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.is_admin and user != request.user: if not request.user.has_perms(('perm',)) and user != request.user:
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin") messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
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)
@ -165,7 +166,108 @@ def password(request, userid):
return form({'userform': u_form}, 'users/user.html', request) return form({'userform': u_form}, 'users/user.html', request)
@login_required @login_required
@permission_required('admin') @permission_required('bureau')
def add_listright(request):
""" Ajouter un droit/groupe, nécessite droit bureau.
Obligation de fournir un gid pour la synchro ldap, unique """
listright = NewListRightForm(request.POST or None)
if listright.is_valid():
with transaction.atomic(), reversion.create_revision():
listright.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Le droit/groupe a été ajouté")
return redirect("/users/index_listright/")
return form({'userform': listright}, 'users/user.html', request)
@login_required
@permission_required('bureau')
def edit_listright(request, listrightid):
""" Editer un groupe/droit, necessite droit bureau, à partir du listright id """
try:
listright_instance = ListRight.objects.get(pk=listrightid)
except ListRight.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/users/")
listright = ListRightForm(request.POST or None, instance=listright_instance)
if listright.is_valid():
with transaction.atomic(), reversion.create_revision():
listright.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in listright.changed_data))
messages.success(request, "Droit modifié")
return redirect("/users/index_listright/")
return form({'userform': listright}, 'users/user.html', request)
@login_required
@permission_required('bureau')
def del_listright(request):
""" Supprimer un ou plusieurs groupe, possible si il est vide, need droit bureau """
listright = DelListRightForm(request.POST or None)
if listright.is_valid():
listright_dels = listright.cleaned_data['listrights']
for listright_del in listright_dels:
try:
with transaction.atomic(), reversion.create_revision():
listright_del.delete()
reversion.set_comment("Destruction")
messages.success(request, "Le droit/groupe a été supprimé")
except ProtectedError:
messages.error(
request,
"L'établissement %s est affecté à au moins un user, \
vous ne pouvez pas le supprimer" % listright_del)
return redirect("/users/index_listright/")
return form({'userform': listright}, 'users/user.html', request)
@login_required
@permission_required('bureau')
def add_right(request, userid):
""" Ajout d'un droit à un user, need droit bureau """
try:
user = User.objects.get(pk=userid)
except User.DoesNotExist:
messages.error(request, "Utilisateur inexistant")
return redirect("/users/")
right = RightForm(request.POST or None)
if right.is_valid():
right = right.save(commit=False)
right.user = user
try:
with transaction.atomic(), reversion.create_revision():
reversion.set_user(request.user)
reversion.set_comment("Ajout du droit %s" % right.right)
right.save()
messages.success(request, "Droit ajouté")
except IntegrityError:
pass
return redirect("/users/profil/" + userid)
return form({'userform': right}, 'users/user.html', request)
@login_required
@permission_required('bureau')
def del_right(request):
""" Supprimer un droit à un user, need droit bureau """
user_right_list = DelRightForm(request.POST or None)
if user_right_list.is_valid():
right_del = user_right_list.cleaned_data['rights']
with transaction.atomic(), reversion.create_revision():
reversion.set_user(request.user)
reversion.set_comment("Retrait des droit %s" % ','.join(str(deleted_right) for deleted_right in right_del))
right_del.delete()
messages.success(request, "Droit retiré avec succès")
return redirect("/users/")
return form({'userform': user_right_list}, 'users/user.html', request)
@login_required
@permission_required('perm')
def index_listright(request):
""" Affiche l'ensemble des droits , need droit perm """
listright_list = ListRight.objects.order_by('listright')
return render(request, 'users/index_listright.html', {'listright_list':listright_list})
@login_required
@permission_required('perm')
def index(request): def index(request):
""" Affiche l'ensemble des users, need droit admin """ """ Affiche l'ensemble des users, need droit admin """
users_list = User.objects.order_by('state', 'name') users_list = User.objects.order_by('state', 'name')
@ -191,18 +293,15 @@ def history(request, 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.is_admin and object_instance != request.user: if not request.user.has_perms(('perm',)) and object_instance != request.user:
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit admin") messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit admin")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
elif object == 'machines': elif object == 'listright' and request.user.has_perms(('perm',)):
try: try:
object_instance = Machine.objects.get(pk=id) object_instance = ListRight.objects.get(pk=id)
except User.DoesNotExist: except ListRight.DoesNotExist:
messages.error(request, "Machine inexistante") messages.error(request, "Droit inexistant")
return redirect("/users/") return redirect("/users/")
if not request.user.is_admin and object_instance.proprio != request.user:
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit admin")
return redirect("/users/profil/" + str(request.user.id))
else: else:
messages.error(request, "Objet inconnu") messages.error(request, "Objet inconnu")
return redirect("/users/") return redirect("/users/")
@ -217,7 +316,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, 'portail_captif/history.html', {'reversions': reversions, 'object': object_instance}) return render(request, 'med/history.html', {'reversions': reversions, 'object': object_instance})
@login_required @login_required
def mon_profil(request): def mon_profil(request):
@ -230,70 +329,21 @@ def profil(request, userid):
except User.DoesNotExist: except User.DoesNotExist:
messages.error(request, "Utilisateur inexistant") messages.error(request, "Utilisateur inexistant")
return redirect("/users/") return redirect("/users/")
machines_list = Machine.objects.filter(proprio=users) if not request.user.has_perms(('perm',)) and users != request.user:
if not request.user.is_admin and users != request.user:
messages.error(request, "Vous ne pouvez pas afficher un autre user que vous sans droit admin") messages.error(request, "Vous ne pouvez pas afficher un autre user que vous sans droit admin")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
emprunts_list = Emprunt.objects.filter(user=users)
list_droits = Right.objects.filter(user=users)
return render( return render(
request, request,
'users/profil.html', 'users/profil.html',
{ {
'user': users, 'user': users,
'machines_list': machines_list, 'emprunts_list': emprunts_list,
'list_droits': list_droits,
} }
) )
def get_ip(request):
"""Returns the IP of the request, accounting for the possibility of being
behind a proxy.
"""
ip = request.META.get("HTTP_X_FORWARDED_FOR", None)
if ip:
# X_FORWARDED_FOR returns client1, proxy1, proxy2,...
ip = ip.split(", ")[0]
else:
ip = request.META.get("REMOTE_ADDR", "")
return ip
def capture_mac(request, users, verbose=True):
remote_ip = get_ip(request)
if ipaddress.ip_address(remote_ip) in ipaddress.ip_network(CAPTIVE_IP_RANGE):
mac_addr = mac_from_ip(remote_ip)
if mac_addr:
machine = Machine()
machine.proprio = users
machine.mac_address = str(mac_addr)
try:
with transaction.atomic(), reversion.create_revision():
machine.save()
reversion.set_comment("Enregistrement de la machine")
except:
if verbose:
messages.error(request, "Assurez-vous que la machine n'est pas déjà enregistrée")
else:
if verbose:
messages.error(request, "Impossible d'enregistrer la machine")
else:
if verbose:
messages.error(request, "Merci de vous connecter sur le réseau du portail captif pour capturer la machine (WiFi %s)" % CAPTIVE_WIFI)
def capture_mac_afterlogin(sender, user, request, **kwargs):
capture_mac(request, user, verbose=False)
# On récupère la mac après le login
user_logged_in.connect(capture_mac_afterlogin)
@login_required
def capture(request):
userid = str(request.user.id)
try:
users = User.objects.get(pk=userid)
except User.DoesNotExist:
messages.error(request, "Utilisateur inexistant")
return redirect("/users/")
capture_mac(request, users)
return redirect("/users/profil/" + str(users.id))
def reset_password(request): def reset_password(request):
userform = ResetPasswordForm(request.POST or None) userform = ResetPasswordForm(request.POST or None)
if userform.is_valid(): if userform.is_valid():