mirror of
https://gitlab.crans.org/mediatek/med.git
synced 2025-06-30 02:31:09 +02:00
Initial commit pour portail_captif, forké depuis re2o (https://gitlab.rezometz.org/rezo/re2o)
This commit is contained in:
22
users/__init__.py
Normal file
22
users/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# 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.
|
||||
|
83
users/admin.py
Normal file
83
users/admin.py
Normal file
@ -0,0 +1,83 @@
|
||||
# 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.contrib import admin
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from reversion.admin import VersionAdmin
|
||||
|
||||
from .models import User, Machine, Request
|
||||
from .forms import UserChangeForm, UserCreationForm
|
||||
|
||||
|
||||
class UserAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'name',
|
||||
'surname',
|
||||
'pseudo',
|
||||
'email',
|
||||
'state'
|
||||
)
|
||||
search_fields = ('name','surname','pseudo')
|
||||
|
||||
class RequestAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'type', 'created_at', 'expires_at')
|
||||
|
||||
class MachineAdmin(VersionAdmin):
|
||||
list_display = ('mac_address','proprio')
|
||||
|
||||
class UserAdmin(VersionAdmin, BaseUserAdmin):
|
||||
# The forms to add and change user instances
|
||||
form = UserChangeForm
|
||||
add_form = UserCreationForm
|
||||
|
||||
# The fields to be used in displaying the User model.
|
||||
# These override the definitions on the base UserAdmin
|
||||
# that reference specific fields on auth.User.
|
||||
list_display = ('pseudo', 'name', 'surname', 'email', 'is_admin')
|
||||
list_filter = ()
|
||||
fieldsets = (
|
||||
(None, {'fields': ('pseudo', 'password')}),
|
||||
('Personal info', {'fields': ('name', 'surname', 'email')}),
|
||||
('Permissions', {'fields': ('is_admin', )}),
|
||||
)
|
||||
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
|
||||
# overrides get_fieldsets to use this attribute when creating a user.
|
||||
add_fieldsets = (
|
||||
(None, {
|
||||
'classes': ('wide',),
|
||||
'fields': ('pseudo', 'name', 'surname', 'email', 'is_admin', 'password1', 'password2')}
|
||||
),
|
||||
)
|
||||
search_fields = ('pseudo',)
|
||||
ordering = ('pseudo',)
|
||||
filter_horizontal = ()
|
||||
|
||||
admin.site.register(Machine, MachineAdmin)
|
||||
admin.site.register(User, UserAdmin)
|
||||
admin.site.register(Request, RequestAdmin)
|
||||
# Now register the new UserAdmin...
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserAdmin)
|
||||
# ... and, since we're not using Django's built-in permissions,
|
||||
# unregister the Group model from admin.
|
||||
admin.site.unregister(Group)
|
100
users/forms.py
Normal file
100
users/forms.py
Normal file
@ -0,0 +1,100 @@
|
||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||
# quelques clics.
|
||||
#
|
||||
# Copyright © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Goulven Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import ReadOnlyPasswordHashField
|
||||
from django.core.validators import MinLengthValidator
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import User
|
||||
|
||||
class PassForm(forms.Form):
|
||||
passwd1 = forms.CharField(label=u'Nouveau mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput)
|
||||
passwd2 = forms.CharField(label=u'Saisir à nouveau le mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput)
|
||||
|
||||
|
||||
class UserCreationForm(forms.ModelForm):
|
||||
"""A form for creating new users. Includes all the required
|
||||
fields, plus a repeated password."""
|
||||
password1 = forms.CharField(label='Password', widget=forms.PasswordInput, validators=[MinLengthValidator(8)], max_length=255)
|
||||
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput, validators=[MinLengthValidator(8)], max_length=255)
|
||||
is_admin = forms.BooleanField(label='is admin')
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('pseudo', 'name', 'surname', 'email')
|
||||
|
||||
def clean_password2(self):
|
||||
# Check that the two password entries match
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
if password1 and password2 and password1 != password2:
|
||||
raise forms.ValidationError("Passwords don't match")
|
||||
return password2
|
||||
|
||||
def save(self, commit=True):
|
||||
# Save the provided password in hashed format
|
||||
user = super(UserCreationForm, self).save(commit=False)
|
||||
user.set_password(self.cleaned_data["password1"])
|
||||
user.save()
|
||||
user.is_admin = self.cleaned_data.get("is_admin")
|
||||
return user
|
||||
|
||||
|
||||
class UserChangeForm(forms.ModelForm):
|
||||
"""A form for updating users. Includes all the fields on
|
||||
the user, but replaces the password field with admin's
|
||||
password hash display field.
|
||||
"""
|
||||
password = ReadOnlyPasswordHashField()
|
||||
is_admin = forms.BooleanField(label='is admin', required=False)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('pseudo', 'password', 'name', 'surname', 'email')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UserChangeForm, self).__init__(*args, **kwargs)
|
||||
print("User is admin : %s" % kwargs['instance'].is_admin)
|
||||
self.initial['is_admin'] = kwargs['instance'].is_admin
|
||||
|
||||
def clean_password(self):
|
||||
# Regardless of what the user provides, return the initial value.
|
||||
# This is done here, rather than on the field, because the
|
||||
# field does not have access to the initial value
|
||||
return self.initial["password"]
|
||||
|
||||
def save(self, commit=True):
|
||||
# Save the provided password in hashed format
|
||||
user = super(UserChangeForm, self).save(commit=False)
|
||||
user.is_admin = self.cleaned_data.get("is_admin")
|
||||
if commit:
|
||||
user.save()
|
||||
return user
|
||||
|
||||
class ResetPasswordForm(forms.Form):
|
||||
pseudo = forms.CharField(label=u'Pseudo', max_length=255)
|
||||
email = forms.EmailField(max_length=255)
|
||||
|
44
users/migrations/0001_initial.py
Normal file
44
users/migrations/0001_initial.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('surname', models.CharField(max_length=255)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('state', models.IntegerField(default=0, choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE')])),
|
||||
('pseudo', models.CharField(max_length=32, unique=True)),
|
||||
('registered', models.DateTimeField(auto_now_add=True)),
|
||||
('admin', models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Request',
|
||||
fields=[
|
||||
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('type', models.CharField(max_length=2, choices=[('PW', 'Mot de passe'), ('EM', 'Email')])),
|
||||
('token', models.CharField(max_length=32)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('expires_at', models.DateTimeField()),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
24
users/migrations/0002_auto_20170610_1550.py
Normal file
24
users/migrations/0002_auto_20170610_1550.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- 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),
|
||||
),
|
||||
]
|
25
users/migrations/0003_machines.py
Normal file
25
users/migrations/0003_machines.py
Normal file
@ -0,0 +1,25 @@
|
||||
# -*- 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)),
|
||||
],
|
||||
),
|
||||
]
|
32
users/migrations/0004_auto_20170610_1736.py
Normal file
32
users/migrations/0004_auto_20170610_1736.py
Normal file
@ -0,0 +1,32 @@
|
||||
# -*- 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',
|
||||
),
|
||||
]
|
0
users/migrations/__init__.py
Normal file
0
users/migrations/__init__.py
Normal file
281
users/models.py
Normal file
281
users/models.py
Normal file
@ -0,0 +1,281 @@
|
||||
# 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.db.models import Q
|
||||
from django.forms import ModelForm, Form
|
||||
from django import forms
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
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
|
||||
import re, uuid
|
||||
import datetime
|
||||
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||||
import subprocess
|
||||
|
||||
def apply(cmd):
|
||||
return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||||
|
||||
def mac_from_ip(ip):
|
||||
cmd = '/usr/sbin/arp -na %s' % 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 " + GENERIC_IPSET_COMMAND + " create " + IPSET_NAME + " hash:mac hashsize 1024 maxelem 65536"
|
||||
apply(command_to_execute)
|
||||
command_to_execute = "sudo " + GENERIC_IPSET_COMMAND + " 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))
|
||||
file = open("/tmp/ipset_restore", 'w+')
|
||||
file.write("%s\nCOMMIT\n" % '\n'.join(["add %s %s" % (IPSET_NAME, str(machine.mac_address)) for machine in all_machines]))
|
||||
file.close()
|
||||
command_to_execute = "sudo " + GENERIC_IPSET_COMMAND + " restore < /tmp/ipset_restore"
|
||||
apply(command_to_execute)
|
||||
return
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
def _create_user(self, pseudo, name, surname, email, password=None, su=False):
|
||||
if not pseudo:
|
||||
raise ValueError('Users must have an username')
|
||||
|
||||
user = self.model(
|
||||
pseudo=pseudo,
|
||||
name=name,
|
||||
surname=surname,
|
||||
email=self.normalize_email(email),
|
||||
)
|
||||
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
if su:
|
||||
user.make_admin()
|
||||
return user
|
||||
|
||||
def create_user(self, pseudo, name, surname, email, password=None):
|
||||
"""
|
||||
Creates and saves a User with the given pseudo, name, surname, email,
|
||||
and password.
|
||||
"""
|
||||
return self._create_user(pseudo, name, surname, email, password, False)
|
||||
|
||||
def create_superuser(self, pseudo, name, surname, email, password):
|
||||
"""
|
||||
Creates and saves a superuser with the given pseudo, name, surname,
|
||||
email, and password.
|
||||
"""
|
||||
return self._create_user(pseudo, name, surname, email, password, True)
|
||||
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
PRETTY_NAME = "Utilisateurs"
|
||||
STATE_ACTIVE = 0
|
||||
STATE_DISABLED = 1
|
||||
STATE_ARCHIVE = 2
|
||||
STATES = (
|
||||
(0, 'STATE_ACTIVE'),
|
||||
(1, 'STATE_DISABLED'),
|
||||
(2, 'STATE_ARCHIVE'),
|
||||
)
|
||||
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
surname = models.CharField(max_length=255)
|
||||
email = models.EmailField()
|
||||
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. ")
|
||||
comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
|
||||
registered = models.DateTimeField(auto_now_add=True)
|
||||
admin = models.BooleanField(default=False)
|
||||
|
||||
USERNAME_FIELD = 'pseudo'
|
||||
REQUIRED_FIELDS = ['name', 'surname', 'email']
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.state == self.STATE_ACTIVE
|
||||
|
||||
@property
|
||||
def is_staff(self):
|
||||
return self.is_admin
|
||||
|
||||
@property
|
||||
def is_admin(self):
|
||||
return self.admin
|
||||
|
||||
@is_admin.setter
|
||||
def is_admin(self, value):
|
||||
if value and not self.is_admin:
|
||||
self.make_admin()
|
||||
elif not value and self.is_admin:
|
||||
self.un_admin()
|
||||
|
||||
def has_perms(self, perms, obj=None):
|
||||
for perm in perms:
|
||||
if perm=="admin":
|
||||
return self.is_admin
|
||||
return False
|
||||
|
||||
def get_full_name(self):
|
||||
return '%s %s' % (self.name, self.surname)
|
||||
|
||||
def get_short_name(self):
|
||||
return self.name
|
||||
|
||||
def has_perm(self, perm, obj=None):
|
||||
return True
|
||||
|
||||
def has_module_perms(self, app_label):
|
||||
# Simplest version again
|
||||
return True
|
||||
|
||||
def make_admin(self):
|
||||
""" Make User admin """
|
||||
self.admin = True
|
||||
self.save()
|
||||
|
||||
def un_admin(self):
|
||||
self.admin = False
|
||||
self.save()
|
||||
|
||||
def machines(self):
|
||||
return Machine.objects.filter(proprio=self)
|
||||
|
||||
def __str__(self):
|
||||
return self.name + " " + self.surname
|
||||
|
||||
|
||||
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 " + GENERIC_IPSET_COMMAND + " add " + IPSET_NAME + " " + str(self.mac_address)
|
||||
apply(command_to_execute)
|
||||
|
||||
def del_to_set(self):
|
||||
command_to_execute = "sudo " + GENERIC_IPSET_COMMAND + " 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):
|
||||
PASSWD = 'PW'
|
||||
EMAIL = 'EM'
|
||||
TYPE_CHOICES = (
|
||||
(PASSWD, 'Mot de passe'),
|
||||
(EMAIL, 'Email'),
|
||||
)
|
||||
type = models.CharField(max_length=2, choices=TYPE_CHOICES)
|
||||
token = models.CharField(max_length=32)
|
||||
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||
created_at = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
expires_at = models.DateTimeField()
|
||||
|
||||
def save(self):
|
||||
if not self.expires_at:
|
||||
self.expires_at = timezone.now() \
|
||||
+ datetime.timedelta(hours=REQ_EXPIRE_HRS)
|
||||
if not self.token:
|
||||
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
|
||||
super(Request, self).save()
|
||||
|
||||
class BaseInfoForm(ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseInfoForm, self).__init__(*args, **kwargs)
|
||||
self.fields['name'].label = 'Prénom'
|
||||
self.fields['surname'].label = 'Nom'
|
||||
#self.fields['comment'].label = 'Commentaire'
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
'name',
|
||||
'pseudo',
|
||||
'surname',
|
||||
'email',
|
||||
]
|
||||
|
||||
class EditInfoForm(BaseInfoForm):
|
||||
class Meta(BaseInfoForm.Meta):
|
||||
fields = [
|
||||
'name',
|
||||
'pseudo',
|
||||
'surname',
|
||||
'comment',
|
||||
'email',
|
||||
'admin',
|
||||
]
|
||||
|
||||
class InfoForm(BaseInfoForm):
|
||||
class Meta(BaseInfoForm.Meta):
|
||||
fields = [
|
||||
'name',
|
||||
'pseudo',
|
||||
'comment',
|
||||
'surname',
|
||||
'email',
|
||||
'admin',
|
||||
]
|
||||
|
||||
class UserForm(EditInfoForm):
|
||||
class Meta(EditInfoForm.Meta):
|
||||
fields = '__all__'
|
||||
|
||||
class PasswordForm(ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['password']
|
||||
|
||||
class StateForm(ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['state']
|
||||
|
||||
class MachineForm(ModelForm):
|
||||
class Meta:
|
||||
model = Machine
|
||||
exclude = '__all__'
|
52
users/templates/users/aff_users.html
Normal file
52
users/templates/users/aff_users.html
Normal file
@ -0,0 +1,52 @@
|
||||
{% 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 %}
|
||||
|
||||
{% if users_list.paginator %}
|
||||
{% include "pagination.html" with list=users_list %}
|
||||
{% endif %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Prénom</th>
|
||||
<th>Nom</th>
|
||||
<th>Pseudo</th>
|
||||
<th>Mail</th>
|
||||
<th>Machines</th>
|
||||
<th>Profil</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for user in users_list %}
|
||||
<tr>
|
||||
<td>{{ user.name }}</td>
|
||||
<td>{{ user.surname }}</td>
|
||||
<td>{{ user.pseudo }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td><table class="table table-striped"><tr>{% for machine in user.machines %}<td>{{ machine.mac_address }}</td>{% endfor %}</tr></table></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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
15
users/templates/users/email_passwd_request
Normal file
15
users/templates/users/email_passwd_request
Normal file
@ -0,0 +1,15 @@
|
||||
Bonjour {{ name }},
|
||||
|
||||
Vous trouverez ci-dessous une url permetant d'initialiser ou de reinitialiser votre
|
||||
compte {{ site_name }}. Celui-ci vous permet de gérer l'ensemble de vos équipements
|
||||
connectés, votre compte, et les services proposés par le {{ asso }}.
|
||||
|
||||
{{ url }}
|
||||
|
||||
Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête.
|
||||
|
||||
Ce lien expirera dans {{ expire_in }}.
|
||||
|
||||
Cordialement,
|
||||
|
||||
L'équipe de {{ asso }} {{ asso_mail }}.
|
37
users/templates/users/index.html
Normal file
37
users/templates/users/index.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% 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 %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Utilisateurs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Users</h2>
|
||||
{% include "users/aff_users.html" with users_list=users_list %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
||||
|
112
users/templates/users/profil.html
Normal file
112
users/templates/users/profil.html
Normal file
@ -0,0 +1,112 @@
|
||||
{% 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 %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Profil{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Compte</h2>
|
||||
<div>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-info' user.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
Editer
|
||||
</a>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:password' user.id %}">
|
||||
<i class="glyphicon glyphicon-lock"></i>
|
||||
Changer le mot de passe
|
||||
</a>
|
||||
{% if is_admin %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:state' user.id %}">
|
||||
<i class="glyphicon glyphicon-flash"></i>
|
||||
Changer le statut
|
||||
</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'user' user.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
Historique
|
||||
</a>
|
||||
</div>
|
||||
<p>
|
||||
<br />
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Prénom</th>
|
||||
<td>{{ user.name }}</td>
|
||||
<th>Nom</th>
|
||||
<td>{{ user.surname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Pseudo</th>
|
||||
<td>{{ user.pseudo }}</td>
|
||||
<th>E-mail</th>
|
||||
<td>{{ user.email }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Commentaire</th>
|
||||
<td>{{ user.comment }}</td>
|
||||
<th>Date d'inscription</th>
|
||||
<td>{{ user.registered }}</td>
|
||||
</tr>
|
||||
<th>Statut</th>
|
||||
{% if user.state == 0 %}
|
||||
<td><font color="green">Actif</font></td>
|
||||
{% elif user.state == 1 %}
|
||||
<td><font color="red">Désactivé</font></td>
|
||||
{% else %}
|
||||
<td><font color="orange">Archivé</font></td>
|
||||
{% endif %}
|
||||
<th>Dernière connexion</th>
|
||||
<td>{{ user.last_login }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>Machines enregistrées</h2>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:capture' %}">
|
||||
<i class="glyphicon glyphicon-flash"></i>
|
||||
Enregistrer la machine utilisée actuellement
|
||||
</a>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<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 />
|
||||
{% endblock %}
|
||||
|
41
users/templates/users/user.html
Normal file
41
users/templates/users/user.html
Normal 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 %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Création et modification d'utilisateur{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% bootstrap_form_errors userform %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form userform %}
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||
</form>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
25
users/tests.py
Normal file
25
users/tests.py
Normal file
@ -0,0 +1,25 @@
|
||||
# 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.test import TestCase
|
||||
|
||||
# Create your tests here.
|
42
users/urls.py
Normal file
42
users/urls.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 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'^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'^state/(?P<userid>[0-9]+)$', views.state, name='state'),
|
||||
url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'),
|
||||
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
|
||||
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
|
||||
url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'),
|
||||
url(r'^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>machines)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^$', views.index, name='index'),
|
||||
]
|
||||
|
||||
|
331
users/views.py
Normal file
331
users/views.py
Normal file
@ -0,0 +1,331 @@
|
||||
# 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 gestion des users pour portail_captif
|
||||
# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin
|
||||
# Gplv2
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.template.context_processors import csrf
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.template import Context, RequestContext, loader
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.signals import user_logged_in
|
||||
from django.db.models import Max, ProtectedError
|
||||
from django.db import IntegrityError
|
||||
from django.core.mail import send_mail
|
||||
from django.utils import timezone
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
|
||||
from reversion.models import Version
|
||||
from reversion import revisions as reversion
|
||||
from users.models import User, MachineForm, Request
|
||||
from users.models import EditInfoForm, InfoForm, BaseInfoForm, Machine, StateForm, mac_from_ip, apply
|
||||
from users.forms import PassForm, ResetPasswordForm
|
||||
import ipaddress
|
||||
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
|
||||
|
||||
|
||||
def form(ctx, template, request):
|
||||
c = ctx
|
||||
c.update(csrf(request))
|
||||
return render(request, template, c)
|
||||
|
||||
def password_change_action(u_form, user, request, req=False):
|
||||
""" Fonction qui effectue le changeemnt de mdp bdd"""
|
||||
if u_form.cleaned_data['passwd1'] != u_form.cleaned_data['passwd2']:
|
||||
messages.error(request, "Les 2 mots de passe différent")
|
||||
return form({'userform': u_form}, 'users/user.html', request)
|
||||
user.set_password(u_form.cleaned_data['passwd1'])
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
user.save()
|
||||
reversion.set_comment("Réinitialisation du mot de passe")
|
||||
messages.success(request, "Le mot de passe a changé")
|
||||
if req:
|
||||
req.delete()
|
||||
return redirect("/")
|
||||
return redirect("/users/profil/" + str(user.id))
|
||||
|
||||
def reset_passwd_mail(req, request):
|
||||
""" Prend en argument un request, envoie un mail de réinitialisation de mot de pass """
|
||||
t = loader.get_template('users/email_passwd_request')
|
||||
c = Context({
|
||||
'name': str(req.user.name) + ' ' + str(req.user.surname),
|
||||
'asso': ASSO_NAME,
|
||||
'asso_mail': ASSO_EMAIL,
|
||||
'site_name': SITE_NAME,
|
||||
'url': request.build_absolute_uri(
|
||||
reverse('users:process', kwargs={'token': req.token})),
|
||||
'expire_in': REQ_EXPIRE_STR,
|
||||
})
|
||||
send_mail('Changement de mot de passe', t.render(c),
|
||||
EMAIL_FROM, [req.user.email], fail_silently=False)
|
||||
return
|
||||
|
||||
|
||||
def new_user(request):
|
||||
""" Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe"""
|
||||
user = BaseInfoForm(request.POST or None)
|
||||
if user.is_valid():
|
||||
user = user.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
user.save()
|
||||
reversion.set_comment("Création")
|
||||
req = Request()
|
||||
req.type = Request.PASSWD
|
||||
req.user = user
|
||||
req.save()
|
||||
reset_passwd_mail(req, request)
|
||||
messages.success(request, "L'utilisateur %s a été crée, un mail pour l'initialisation du mot de passe a été envoyé" % user.pseudo)
|
||||
capture_mac(request, user)
|
||||
return redirect("/users/profil/" + str(user.id))
|
||||
return form({'userform': user}, 'users/user.html', request)
|
||||
|
||||
@login_required
|
||||
def edit_info(request, userid):
|
||||
""" Edite un utilisateur à partir de son id, si l'id est différent de request.user, vérifie la possession du droit admin """
|
||||
try:
|
||||
user = User.objects.get(pk=userid)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Utilisateur inexistant")
|
||||
return redirect("/users/")
|
||||
if not request.user.is_admin and user != request.user:
|
||||
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
if not request.user.is_admin:
|
||||
user = BaseInfoForm(request.POST or None, instance=user)
|
||||
else:
|
||||
user = InfoForm(request.POST or None, instance=user)
|
||||
if user.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
user.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in user.changed_data))
|
||||
messages.success(request, "L'user a bien été modifié")
|
||||
return redirect("/users/profil/" + userid)
|
||||
return form({'userform': user}, 'users/user.html', request)
|
||||
|
||||
@login_required
|
||||
@permission_required('admin')
|
||||
def state(request, userid):
|
||||
""" Changer l'etat actif/desactivé/archivé d'un user, need droit bureau """
|
||||
try:
|
||||
user = User.objects.get(pk=userid)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Utilisateur inexistant")
|
||||
return redirect("/users/")
|
||||
state = StateForm(request.POST or None, instance=user)
|
||||
if state.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
state.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in state.changed_data))
|
||||
messages.success(request, "Etat changé avec succès")
|
||||
return redirect("/users/profil/" + userid)
|
||||
return form({'userform': state}, 'users/user.html', request)
|
||||
|
||||
@login_required
|
||||
def password(request, userid):
|
||||
""" Reinitialisation d'un mot de passe à partir de l'userid,
|
||||
pour self par défaut, pour tous sans droit si droit admin,
|
||||
pour tous si droit bureau """
|
||||
try:
|
||||
user = User.objects.get(pk=userid)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Utilisateur inexistant")
|
||||
return redirect("/users/")
|
||||
if not request.user.is_admin and user != request.user:
|
||||
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
u_form = PassForm(request.POST or None)
|
||||
if u_form.is_valid():
|
||||
return password_change_action(u_form, user, request)
|
||||
return form({'userform': u_form}, 'users/user.html', request)
|
||||
|
||||
@login_required
|
||||
@permission_required('admin')
|
||||
def index(request):
|
||||
""" Affiche l'ensemble des users, need droit admin """
|
||||
users_list = User.objects.order_by('state', 'name')
|
||||
paginator = Paginator(users_list, PAGINATION_NUMBER)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
users_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
users_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
users_list = paginator.page(paginator.num_pages)
|
||||
return render(request, 'users/index.html', {'users_list': users_list})
|
||||
|
||||
@login_required
|
||||
def history(request, object, id):
|
||||
""" Affichage de l'historique : (acl, argument)
|
||||
user : self, userid"""
|
||||
if object == 'user':
|
||||
try:
|
||||
object_instance = User.objects.get(pk=id)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Utilisateur inexistant")
|
||||
return redirect("/users/")
|
||||
if not request.user.is_admin and object_instance != 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))
|
||||
elif object == 'machines':
|
||||
try:
|
||||
object_instance = Machine.objects.get(pk=id)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Machine inexistante")
|
||||
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:
|
||||
messages.error(request, "Objet inconnu")
|
||||
return redirect("/users/")
|
||||
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, 'portail_captif/history.html', {'reversions': reversions, 'object': object_instance})
|
||||
|
||||
@login_required
|
||||
def mon_profil(request):
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
|
||||
@login_required
|
||||
def profil(request, userid):
|
||||
try:
|
||||
users = User.objects.get(pk=userid)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Utilisateur inexistant")
|
||||
return redirect("/users/")
|
||||
machines_list = Machine.objects.filter(proprio=users)
|
||||
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")
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
return render(
|
||||
request,
|
||||
'users/profil.html',
|
||||
{
|
||||
'user': users,
|
||||
'machines_list': machines_list,
|
||||
}
|
||||
)
|
||||
|
||||
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):
|
||||
userform = ResetPasswordForm(request.POST or None)
|
||||
if userform.is_valid():
|
||||
try:
|
||||
user = User.objects.get(pseudo=userform.cleaned_data['pseudo'],email=userform.cleaned_data['email'])
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, "Cet utilisateur n'existe pas")
|
||||
return form({'userform': userform}, 'users/user.html', request)
|
||||
req = Request()
|
||||
req.type = Request.PASSWD
|
||||
req.user = user
|
||||
req.save()
|
||||
reset_passwd_mail(req, request)
|
||||
messages.success(request, "Un mail pour l'initialisation du mot de passe a été envoyé")
|
||||
redirect("/")
|
||||
return form({'userform': userform}, 'users/user.html', request)
|
||||
|
||||
def process(request, token):
|
||||
valid_reqs = Request.objects.filter(expires_at__gt=timezone.now())
|
||||
req = get_object_or_404(valid_reqs, token=token)
|
||||
|
||||
if req.type == Request.PASSWD:
|
||||
return process_passwd(request, req)
|
||||
elif req.type == Request.EMAIL:
|
||||
return process_email(request, req=req)
|
||||
else:
|
||||
messages.error(request, "Entrée incorrecte, contactez un admin")
|
||||
redirect("/")
|
||||
|
||||
def process_passwd(request, req):
|
||||
u_form = PassForm(request.POST or None)
|
||||
user = req.user
|
||||
if u_form.is_valid():
|
||||
return password_change_action(u_form, user, request, req=req)
|
||||
return form({'userform': u_form}, 'users/user.html', request)
|
Reference in New Issue
Block a user