Commit f1d96c19 authored by root's avatar root

Merge branch 'master' into klafyvel

parents 7bc0f2b7 0efe0c27
...@@ -292,11 +292,12 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address): ...@@ -292,11 +292,12 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
if not port.room: if not port.room:
return (sw_name, u'Chambre inconnue', VLAN_NOK) return (sw_name, u'Chambre inconnue', VLAN_NOK)
room_user = User.objects.filter(room=port.room) room_user = User.objects.filter(Q(club__room=port.room) | Q(adherent__room=port.room))
if not room_user: if not room_user:
return (sw_name, u'Chambre non cotisante', VLAN_NOK) return (sw_name, u'Chambre non cotisante', VLAN_NOK)
elif not room_user.first().has_access(): for user in room_user:
return (sw_name, u'Chambre resident desactive', VLAN_NOK) if not user.has_access():
return (sw_name, u'Chambre resident desactive', VLAN_NOK)
# else: user OK, on passe à la verif MAC # else: user OK, on passe à la verif MAC
if port.radius == 'COMMON' or port.radius == 'STRICT': if port.radius == 'COMMON' or port.radius == 'STRICT':
...@@ -309,9 +310,12 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address): ...@@ -309,9 +310,12 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
elif not port.room: elif not port.room:
return (sw_name, u'Chambre et machine inconnues', VLAN_NOK) return (sw_name, u'Chambre et machine inconnues', VLAN_NOK)
else: else:
room_user = User.objects.filter(room=Room.objects.filter(name=port.room)) if not room_user:
room_user = User.objects.filter(Q(club__room=port.room) | Q(adherent__room=port.room))
if not room_user: if not room_user:
return (sw_name, u'Machine et propriétaire de la chambre inconnus', VLAN_NOK) return (sw_name, u'Machine et propriétaire de la chambre inconnus', VLAN_NOK)
elif room_user.count() > 1:
return (sw_name, u'Machine inconnue, il y a au moins 2 users dans la chambre/local -> ajout de mac automatique impossible', VLAN_NOK)
elif not room_user.first().has_access(): elif not room_user.first().has_access():
return (sw_name, u'Machine inconnue et adhérent non cotisant', VLAN_NOK) return (sw_name, u'Machine inconnue et adhérent non cotisant', VLAN_NOK)
else: else:
......
...@@ -42,7 +42,7 @@ from django.db.models import Q ...@@ -42,7 +42,7 @@ from django.db.models import Q
from cotisations.models import Cotisation, Facture, Paiement, Vente from cotisations.models import Cotisation, Facture, Paiement, Vente
from machines.models import Domain, Interface, Machine from machines.models import Domain, Interface, Machine
from users.models import User, Ban, Whitelist from users.models import Adherent, User, Ban, Whitelist
from preferences.models import Service from preferences.models import Service
DT_NOW = timezone.now() DT_NOW = timezone.now()
...@@ -238,3 +238,13 @@ class SortTable: ...@@ -238,3 +238,13 @@ class SortTable:
return request.reverse() return request.reverse()
else: else:
return request return request
def remove_user_room(room):
""" Déménage de force l'ancien locataire de la chambre """
try:
user = Adherent.objects.get(room=room)
except Adherent.DoesNotExist:
return
user.room = None
user.save()
...@@ -81,7 +81,7 @@ def search_result(search, type, request): ...@@ -81,7 +81,7 @@ def search_result(search, type, request):
for i in aff: for i in aff:
if i == '0': if i == '0':
query_user_list = Q(room__name__icontains = search) | Q(pseudo__icontains = search) | Q(adherent__name__icontains = search) | Q(surname__icontains = search) & query1 query_user_list = Q(adherent__room__name__icontains = search) | Q(club__room__name__icontains = search) | Q(pseudo__icontains = search) | Q(adherent__name__icontains = search) | Q(surname__icontains = search) & query1
if request.user.has_perms(('cableur',)): if request.user.has_perms(('cableur',)):
recherche['users_list'] = User.objects.filter(query_user_list).order_by('state', 'surname').distinct() recherche['users_list'] = User.objects.filter(query_user_list).order_by('state', 'surname').distinct()
else : else :
......
...@@ -44,13 +44,12 @@ class UserAdmin(admin.ModelAdmin): ...@@ -44,13 +44,12 @@ class UserAdmin(admin.ModelAdmin):
list_display = ( list_display = (
'surname', 'surname',
'pseudo', 'pseudo',
'room',
'email', 'email',
'school', 'school',
'shell', 'shell',
'state' 'state'
) )
search_fields = ('surname', 'pseudo', 'room') search_fields = ('surname', 'pseudo')
class LdapUserAdmin(admin.ModelAdmin): class LdapUserAdmin(admin.ModelAdmin):
......
...@@ -41,7 +41,8 @@ from django.utils import timezone ...@@ -41,7 +41,8 @@ from django.utils import timezone
from preferences.models import OptionalUser from preferences.models import OptionalUser
from .models import User, ServiceUser, Right, School, ListRight, Whitelist from .models import User, ServiceUser, Right, School, ListRight, Whitelist
from .models import Ban, remove_user_room, Adherent, Club from .models import Ban, Adherent, Club
from re2o.utils import remove_user_room
NOW = timezone.now() NOW = timezone.now()
...@@ -252,13 +253,13 @@ class MassArchiveForm(forms.Form): ...@@ -252,13 +253,13 @@ class MassArchiveForm(forms.Form):
utilisateurs dont la fin d'accès se situe dans le futur !") utilisateurs dont la fin d'accès se situe dans le futur !")
class NewUserForm(ModelForm): class AdherentForm(ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé """Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis""" avec des label plus jolis"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NewUserForm, self).__init__(*args, prefix=prefix, **kwargs) super(AdherentForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = 'Prénom' self.fields['name'].label = 'Prénom'
self.fields['surname'].label = 'Nom' self.fields['surname'].label = 'Nom'
self.fields['school'].label = 'Établissement' self.fields['school'].label = 'Établissement'
...@@ -291,18 +292,31 @@ class NewUserForm(ModelForm): ...@@ -291,18 +292,31 @@ class NewUserForm(ModelForm):
) )
return telephone return telephone
force = forms.BooleanField(
label="Forcer le déménagement ?",
initial=False,
required=False
)
def clean_force(self):
"""On supprime l'ancien user de la chambre si et seulement si la
case est cochée"""
if self.cleaned_data.get('force', False):
remove_user_room(self.cleaned_data.get('room'))
return
class NewClubForm(ModelForm):
class ClubForm(ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé """Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis""" avec des label plus jolis"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NewClubForm, self).__init__(*args, prefix=prefix, **kwargs) super(ClubForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['surname'].label = 'Nom' self.fields['surname'].label = 'Nom'
self.fields['school'].label = 'Établissement' self.fields['school'].label = 'Établissement'
self.fields['comment'].label = 'Commentaire' self.fields['comment'].label = 'Commentaire'
self.fields['room'].label = 'Chambre' self.fields['room'].label = 'Local'
self.fields['room'].empty_label = "Pas de chambre" self.fields['room'].empty_label = "Pas de chambre"
self.fields['school'].empty_label = "Séléctionner un établissement" self.fields['school'].empty_label = "Séléctionner un établissement"
...@@ -330,50 +344,29 @@ class NewClubForm(ModelForm): ...@@ -330,50 +344,29 @@ class NewClubForm(ModelForm):
return telephone return telephone
class FullAdherentForm(AdherentForm):
class BaseInfoForm(ModelForm): """Edition complète d'un user. Utilisé par admin,
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé permet d'editer normalement la chambre, ou le shell
pour l'edition de self par self ou un cableur. On formate les champs Herite de la base"""
avec des label plus jolis""" class Meta(AdherentForm.Meta):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(BaseInfoForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['surname'].label = 'Nom'
self.fields['school'].label = 'Établissement'
self.fields['comment'].label = 'Commentaire'
self.fields['room'].label = 'Chambre'
self.fields['room'].empty_label = "Pas de chambre"
self.fields['school'].empty_label = "Séléctionner un établissement"
class Meta:
model = User
fields = [ fields = [
'name',
'surname', 'surname',
'pseudo', 'pseudo',
'email', 'email',
'school', 'school',
'comment', 'comment',
'room', 'room',
'shell',
'telephone', 'telephone',
] ]
def clean_telephone(self):
"""Verifie que le tel est présent si 'option est validée
dans preferences"""
telephone = self.cleaned_data['telephone']
preferences, _created = OptionalUser.objects.get_or_create()
if not telephone and preferences.is_tel_mandatory:
raise forms.ValidationError(
"Un numéro de téléphone valide est requis"
)
return telephone
class EditInfoForm(BaseInfoForm): class FullClubForm(ClubForm):
"""Edition complète d'un user. Utilisé par admin, """Edition complète d'un user. Utilisé par admin,
permet d'editer normalement la chambre, ou le shell permet d'editer normalement la chambre, ou le shell
Herite de la base""" Herite de la base"""
class Meta(BaseInfoForm.Meta): class Meta(ClubForm.Meta):
fields = [ fields = [
'surname', 'surname',
'pseudo', 'pseudo',
...@@ -386,29 +379,6 @@ class EditInfoForm(BaseInfoForm): ...@@ -386,29 +379,6 @@ class EditInfoForm(BaseInfoForm):
] ]
class InfoForm(EditInfoForm):
""" Utile pour forcer un déménagement quand il y a déjà un user en place
Formuaire utilisé pour la creation initiale"""
force = forms.BooleanField(
label="Forcer le déménagement ?",
initial=False,
required=False
)
def clean_force(self):
"""On supprime l'ancien user de la chambre si et seulement si la
case est cochée"""
if self.cleaned_data.get('force', False):
remove_user_room(self.cleaned_data.get('room'))
return
class UserForm(InfoForm):
""" Model form general"""
class Meta(InfoForm.Meta):
fields = '__all__'
class PasswordForm(ModelForm): class PasswordForm(ModelForm):
""" Formulaire de changement brut de mot de passe. """ Formulaire de changement brut de mot de passe.
Ne pas utiliser sans traitement""" Ne pas utiliser sans traitement"""
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-24 23:54
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
def create_move_room(apps, schema_editor):
User = apps.get_model('users', 'User')
Adherent = apps.get_model('users', 'Adherent')
Club = apps.get_model('users', 'Club')
db_alias = schema_editor.connection.alias
users = Adherent.objects.using(db_alias).all()
clubs = Club.objects.using(db_alias).all()
for user in users:
user.room_adherent_id = user.room_id
user.save(using=db_alias)
for user in clubs:
user.room_club_id = user.room_id
user.save(using=db_alias)
def delete_move_room(apps, schema_editor):
User = apps.get_model('users', 'User')
Adherent = apps.get_model('users', 'Adherent')
Club = apps.get_model('users', 'Club')
db_alias = schema_editor.connection.alias
users = Adherent.objects.using(db_alias).all()
clubs = Club.objects.using(db_alias).all()
for user in users:
user.room_id = user.room_adherent_id
user.save(using=db_alias)
for user in clubs:
user.room_id = user.room_club_id
user.save(using=db_alias)
class Migration(migrations.Migration):
dependencies = [
('topologie', '0031_auto_20171015_2033'),
('users', '0057_auto_20171023_0301'),
]
operations = [
migrations.AddField(
model_name='adherent',
name='room_adherent',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'),
),
migrations.AddField(
model_name='club',
name='room_club',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'),
),
migrations.RunPython(create_move_room, delete_move_room),
migrations.RemoveField(
model_name='user',
name='room',
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-25 16:54
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0058_auto_20171025_0154'),
]
operations = [
migrations.RenameField(
model_name='adherent',
old_name='room_adherent',
new_name='room',
),
migrations.RenameField(
model_name='club',
old_name='room_club',
new_name='room',
),
]
...@@ -81,15 +81,6 @@ DT_NOW = timezone.now() ...@@ -81,15 +81,6 @@ DT_NOW = timezone.now()
# Utilitaires généraux # Utilitaires généraux
def remove_user_room(room):
""" Déménage de force l'ancien locataire de la chambre """
try:
user = User.objects.get(room=room)
except User.DoesNotExist:
return
user.room = None
user.save()
def linux_user_check(login): def linux_user_check(login):
""" Validation du pseudo pour respecter les contraintes unix""" """ Validation du pseudo pour respecter les contraintes unix"""
...@@ -230,12 +221,6 @@ class User(AbstractBaseUser): ...@@ -230,12 +221,6 @@ class User(AbstractBaseUser):
max_length=255, max_length=255,
blank=True blank=True
) )
room = models.OneToOneField(
'topologie.Room',
on_delete=models.PROTECT,
blank=True,
null=True
)
pwd_ntlm = models.CharField(max_length=255) pwd_ntlm = models.CharField(max_length=255)
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE) state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
registered = models.DateTimeField(auto_now_add=True) registered = models.DateTimeField(auto_now_add=True)
...@@ -256,6 +241,16 @@ class User(AbstractBaseUser): ...@@ -256,6 +241,16 @@ class User(AbstractBaseUser):
else: else:
return '' return ''
@cached_property
def room(self):
"""Alias vers room """
if self.is_class_adherent:
return self.adherent.room
elif self.is_class_club:
return self.club.room
else:
raise NotImplementedError("Type inconnu")
@cached_property @cached_property
def class_name(self): def class_name(self):
"""Renvoie si il s'agit d'un adhérent ou d'un club""" """Renvoie si il s'agit d'un adhérent ou d'un club"""
...@@ -674,10 +669,34 @@ class User(AbstractBaseUser): ...@@ -674,10 +669,34 @@ class User(AbstractBaseUser):
domain.interface_parent = interface_cible domain.interface_parent = interface_cible
domain.clean() domain.clean()
domain.save() domain.save()
self.notif_auto_newmachine(interface_cible)
except Exception as error: except Exception as error:
return False, error return False, error
return True, "Ok" return True, "Ok"
def notif_auto_newmachine(self, interface):
"""Notification mail lorsque une machine est automatiquement
ajoutée par le radius"""
template = loader.get_template('users/email_auto_newmachine')
assooptions, _created = AssoOption.objects.get_or_create()
general_options, _created = GeneralOption.objects.get_or_create()
context = Context({
'nom': self.get_full_name(),
'mac_address' : interface.mac_address,
'asso_name': assooptions.name,
'interface_name' : interface.domain,
'asso_email': assooptions.contact,
'pseudo': self.pseudo,
})
send_mail(
"Ajout automatique d'une machine / New machine autoregistered",
'',
general_options.email_from,
[self.email],
html_message=template.render(context)
)
return
def set_user_password(self, password): def set_user_password(self, password):
""" A utiliser de préférence, set le password en hash courrant et """ A utiliser de préférence, set le password en hash courrant et
dans la version ntlm""" dans la version ntlm"""
...@@ -712,10 +731,22 @@ class User(AbstractBaseUser): ...@@ -712,10 +731,22 @@ class User(AbstractBaseUser):
class Adherent(User): class Adherent(User):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
room = models.OneToOneField(
'topologie.Room',
on_delete=models.PROTECT,
blank=True,
null=True
)
pass pass
class Club(User): class Club(User):
room = models.ForeignKey(
'topologie.Room',
on_delete=models.PROTECT,
blank=True,
null=True
)
pass pass
......
<p>Bonjour {{nom}} </p>
<p>Une nouvelle machine a automatiquement été inscrite sur votre compte.</p>
<p>Si vous êtes à l'origine de la connexion de cet appareil en filaire ou wifi, ne tenez pas compte de cette notification</p>
<p>La nouvelle machine possède l'adresse mac {{ mac_address }}, et s'est vu assigner le nom suivant : {{ interface_name }}</p>
<p>Vous pouvez à tout moment modifier ces informations sur votre compte en ligne</p>
<p>Si vous n'êtes pas à l'origine de cette connexion, merci de le signaler rapidement à {{asso_email}}</p>
<p>À bientôt,<br>
L'équipe de {{asso_name}}.</p>
<p>---</p>
<p>A new device has been automatically added on your account.</p>
<p>If you connected a new device recently, please don't take this mail into account<p>
<p>The new device has this mac address : {{ mac_address }}, and this name : {{ interface_name }}</p>
<p>Please contact us if you didn't connect a new device with this mail address {{asso_email}}.</p>
<p>Regards,<br>
The {{asso_name}} team.</p>
...@@ -55,10 +55,10 @@ from users.serializers import MailSerializer ...@@ -55,10 +55,10 @@ from users.serializers import MailSerializer
from users.models import User, Right, Ban, Whitelist, School, ListRight from users.models import User, Right, Ban, Whitelist, School, ListRight
from users.models import Request, ServiceUser, Adherent, Club from users.models import Request, ServiceUser, Adherent, Club
from users.forms import DelRightForm, BanForm, WhitelistForm, DelSchoolForm from users.forms import DelRightForm, BanForm, WhitelistForm, DelSchoolForm
from users.forms import DelListRightForm, NewListRightForm from users.forms import DelListRightForm, NewListRightForm, FullAdherentForm
from users.forms import InfoForm, BaseInfoForm, StateForm from users.forms import StateForm, FullClubForm
from users.forms import RightForm, SchoolForm, EditServiceUserForm from users.forms import RightForm, SchoolForm, EditServiceUserForm
from users.forms import ServiceUserForm, ListRightForm, NewUserForm, NewClubForm from users.forms import ServiceUserForm, ListRightForm, AdherentForm, ClubForm
from users.forms import MassArchiveForm, PassForm, ResetPasswordForm from users.forms import MassArchiveForm, PassForm, ResetPasswordForm
from cotisations.models import Facture from cotisations.models import Facture
from machines.models import Machine from machines.models import Machine
...@@ -85,7 +85,7 @@ def password_change_action(u_form, user, request, req=False): ...@@ -85,7 +85,7 @@ def password_change_action(u_form, user, request, req=False):
def new_user(request): def new_user(request):
""" Vue de création d'un nouvel utilisateur, """ Vue de création d'un nouvel utilisateur,
envoie un mail pour le mot de passe""" envoie un mail pour le mot de passe"""
user = NewUserForm(request.POST or None) user = AdherentForm(request.POST or None)
if user.is_valid(): if user.is_valid():
user = user.save(commit=False) user = user.save(commit=False)
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
...@@ -104,7 +104,7 @@ def new_user(request): ...@@ -104,7 +104,7 @@ def new_user(request):
def new_club(request): def new_club(request):
""" Vue de création d'un nouveau club, """ Vue de création d'un nouveau club,
envoie un mail pour le mot de passe""" envoie un mail pour le mot de passe"""
club = NewClubForm(request.POST or None) club = ClubForm(request.POST or None)
if club.is_valid(): if club.is_valid():
club = club.save(commit=False) club = club.save(commit=False)
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
...@@ -118,6 +118,24 @@ def new_club(request): ...@@ -118,6 +118,24 @@ def new_club(request):
return form({'userform': club}, 'users/user.html', request) return form({'userform': club}, 'users/user.html', request)
def select_user_edit_form(request, user):
"""Fonction de choix du bon formulaire, en fonction de:
- droit
- type d'object
"""
if not request.user.has_perms(('cableur',)):
if user.is_class_adherent:
user = AdherentForm(request.POST or None, instance=user.adherent)
elif user.is_class_club:
user = ClubForm(request.POST or None, instance=user.club)
else:
if user.is_class_adherent:
user = FullAdherentForm(request.POST or None, instance=user.adherent)
elif user.is_class_club:
user = FullClubForm(request.POST or None, instance=user.club)
return user
@login_required @login_required
def edit_info(request, userid): def edit_info(request, userid):
""" Edite un utilisateur à partir de son id, """ Edite un utilisateur à partir de son id,
...@@ -132,10 +150,7 @@ def edit_info(request, userid): ...@@ -132,10 +150,7 @@ def edit_info(request, userid):
messages.error(request, "Vous ne pouvez pas modifier un autre\ messages.error(request, "Vous ne pouvez pas modifier un autre\
user que vous sans droit cableur") user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) return redirect("/users/profil/" + str(request.user.id))
if not request.user.has_perms(('cableur',)): user = select_user_edit_form(request, user)
user = BaseInfoForm(request.POST or None, instance=user)
else: