Ce serveur Gitlab sera éteint le 30 juin 2020, pensez à migrer vos projets vers les serveurs gitlab-research.centralesupelec.fr et gitlab-student.centralesupelec.fr !

Commit 729683c9 authored by chirac's avatar chirac

Ajout des groupes ldap, et d'une vue pour modifier la liste des groupes/droits

parent ca0a6298
......@@ -3,7 +3,7 @@ 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, School, Right, ListRight, Ban, Whitelist, Request
from .models import User, School, Right, ListRight, ListShell, Ban, Whitelist, Request, LdapUser, LdapUserGroup
from .forms import UserChangeForm, UserCreationForm
......@@ -15,17 +15,28 @@ class UserAdmin(admin.ModelAdmin):
'room',
'email',
'school',
'shell',
'state'
)
search_fields = ('name','surname','pseudo','room')
class LdapUserAdmin(admin.ModelAdmin):
list_display = ('name','uidNumber','loginShell')
search_fields = ('name',)
class LdapUserGroupAdmin(admin.ModelAdmin):
list_display = ('name','members','gid')
search_fields = ('name',)
class SchoolAdmin(VersionAdmin):
list_display = ('name',)
class ListRightAdmin(VersionAdmin):
list_display = ('listright',)
class ListShellAdmin(VersionAdmin):
list_display = ('shell',)
class RightAdmin(admin.ModelAdmin):
list_display = ('user', 'right')
......@@ -49,11 +60,11 @@ class UserAdmin(VersionAdmin, BaseUserAdmin):
# 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', 'school', 'is_admin')
list_display = ('pseudo', 'name', 'surname', 'email', 'school', 'is_admin', 'shell')
list_filter = ()
fieldsets = (
(None, {'fields': ('pseudo', 'password')}),
('Personal info', {'fields': ('name', 'surname', 'email', 'school')}),
('Personal info', {'fields': ('name', 'surname', 'email', 'school','shell')}),
('Permissions', {'fields': ('is_admin', )}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
......@@ -69,9 +80,12 @@ class UserAdmin(VersionAdmin, BaseUserAdmin):
filter_horizontal = ()
admin.site.register(User, UserAdmin)
admin.site.register(LdapUser, LdapUserAdmin)
admin.site.register(LdapUserGroup, LdapUserGroupAdmin)
admin.site.register(School, SchoolAdmin)
admin.site.register(Right, RightAdmin)
admin.site.register(ListRight, ListRightAdmin)
admin.site.register(ListShell, ListShellAdmin)
admin.site.register(Ban, BanAdmin)
admin.site.register(Whitelist, WhitelistAdmin)
admin.site.register(Request, RequestAdmin)
......
......@@ -15,8 +15,8 @@ class PassForm(forms.Form):
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)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
password1 = forms.CharField(label='Password', widget=forms.PasswordInput, min_length=8, max_length=255)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput, min_length=8, max_length=255)
is_admin = forms.BooleanField(label='is admin')
class Meta:
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0020_request'),
]
operations = [
migrations.CreateModel(
name='LdapUser',
fields=[
('dn', models.CharField(max_length=200)),
('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')),
('name', ldapdb.models.fields.CharField(primary_key=True, max_length=200, db_column='cn', serialize=False)),
('uid', ldapdb.models.fields.CharField(max_length=200, db_column='uid')),
('uidNumber', ldapdb.models.fields.IntegerField(unique=True, db_column='uidNumber')),
('sn', ldapdb.models.fields.CharField(max_length=200, db_column='sn')),
('loginShell', ldapdb.models.fields.CharField(default='/bin/zsh', max_length=200, db_column='loginShell')),
('mail', ldapdb.models.fields.CharField(max_length=200, db_column='mail')),
('given_name', ldapdb.models.fields.CharField(max_length=200, db_column='givenName')),
('home_directory', ldapdb.models.fields.CharField(max_length=200, db_column='homeDirectory')),
('dialupAccess', ldapdb.models.fields.CharField(max_length=200, db_column='dialupAccess')),
('mac_list', ldapdb.models.fields.CharField(max_length=200, db_column='radiusCallingStationId')),
],
options={
'abstract': False,
},
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0021_ldapuser'),
]
operations = [
migrations.AddField(
model_name='ldapuser',
name='sambaSID',
field=ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True, null=True),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0022_ldapuser_sambasid'),
]
operations = [
migrations.AlterField(
model_name='ldapuser',
name='sambaSID',
field=ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True),
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0023_auto_20160724_1908'),
]
operations = [
migrations.RemoveField(
model_name='ldapuser',
name='mac_list',
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0024_remove_ldapuser_mac_list'),
]
operations = [
migrations.CreateModel(
name='ListShell',
fields=[
('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')),
('shell', models.CharField(unique=True, max_length=255)),
],
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0025_listshell'),
]
operations = [
migrations.AddField(
model_name='user',
name='shell',
field=models.ForeignKey(to='users.ListShell', default=1, on_delete=django.db.models.deletion.PROTECT),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0026_user_shell'),
]
operations = [
migrations.CreateModel(
name='LdapUserGroup',
fields=[
('dn', models.CharField(max_length=200)),
('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')),
('members', ldapdb.models.fields.ListField(db_column='memberUid', blank=True)),
('name', ldapdb.models.fields.CharField(db_column='cn', primary_key=True, serialize=False, max_length=200)),
],
options={
'abstract': False,
},
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import ldapdb.models.fields
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0027_auto_20160726_0216'),
]
operations = [
migrations.AddField(
model_name='ldapuser',
name='display_name',
field=ldapdb.models.fields.CharField(null=True, blank=True, max_length=200, db_column='displayName'),
),
migrations.AddField(
model_name='ldapuser',
name='macs',
field=ldapdb.models.fields.ListField(null=True, blank=True, max_length=200, db_column='radiusCallingStationId'),
),
migrations.AddField(
model_name='ldapuser',
name='sambat_nt_password',
field=ldapdb.models.fields.CharField(null=True, blank=True, max_length=200, db_column='sambaNTPassword'),
),
migrations.AddField(
model_name='ldapuser',
name='user_password',
field=ldapdb.models.fields.CharField(null=True, blank=True, max_length=200, db_column='userPassword'),
),
migrations.AddField(
model_name='listright',
name='gid',
field=models.IntegerField(null=True, unique=True),
),
migrations.AlterField(
model_name='user',
name='shell',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, default=1, to='users.ListShell'),
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0028_auto_20160726_0227'),
]
operations = [
migrations.AlterField(
model_name='ldapuser',
name='display_name',
field=ldapdb.models.fields.CharField(db_column='displayName', max_length=200),
),
]
......@@ -2,8 +2,13 @@ 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 re2o.settings import RIGHTS_LINK, REQ_EXPIRE_HRS
import ldapdb.models
import ldapdb.models.fields
from re2o.settings import RIGHTS_LINK, REQ_EXPIRE_HRS, LDAP_SETTINGS
import re, uuid
import datetime
......@@ -12,6 +17,7 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from topologie.models import Room
from cotisations.models import Cotisation, Facture, Vente
from machines.models import Interface, Machine
def remove_user_room(room):
""" Déménage de force l'ancien locataire de la chambre """
......@@ -97,6 +103,7 @@ class User(AbstractBaseUser):
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator])
email = models.EmailField()
school = models.ForeignKey('School', on_delete=models.PROTECT, null=False, blank=False)
shell = models.ForeignKey('ListShell', on_delete=models.PROTECT, null=False, blank=False, default=1)
comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
room = models.OneToOneField('topologie.Room', on_delete=models.PROTECT, blank=True, null=True)
pwd_ntlm = models.CharField(max_length=255)
......@@ -218,9 +225,46 @@ class User(AbstractBaseUser):
return
user_right.delete()
def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True):
try:
user_ldap = LdapUser.objects.get(name=self.pseudo)
except LdapUser.DoesNotExist:
user_ldap = LdapUser(name=self.pseudo)
if base:
user_ldap.sn = self.pseudo
user_ldap.dialupAccess = str(self.has_access())
user_ldap.uidNumber = self.id
user_ldap.home_directory = '/home/' + self.pseudo
user_ldap.mail = self.email
user_ldap.given_name = str(self.surname).lower() + '_' + str(self.name).lower()[:3]
user_ldap.gid = LDAP_SETTINGS['user_gid']
user_ldap.user_password = self.password
user_ldap.sambat_nt_password = self.pwd_ntlm
if access_refresh:
user_ldap.dialupAccess = str(self.has_access())
if mac_refresh:
user_ldap.macs = [inter.mac_address for inter in Interface.objects.filter(machine=Machine.objects.filter(user=self))]
user_ldap.save()
def ldap_del(self):
try:
user_ldap = LdapUser.objects.get(name=self.pseudo)
user_ldap.delete()
except LdapUser.DoesNotExist:
pass
def __str__(self):
return self.pseudo
@receiver(post_save, sender=User)
def user_post_save(sender, **kwargs):
user = kwargs['instance']
user.ldap_sync(base=True, access_refresh=True, mac_refresh=False)
@receiver(post_delete, sender=User)
def user_post_delete(sender, **kwargs):
user = kwargs['instance']
user.ldap_del()
class Right(models.Model):
user = models.ForeignKey('User', on_delete=models.PROTECT)
......@@ -232,6 +276,15 @@ class Right(models.Model):
def __str__(self):
return str(self.user) + " - " + str(self.right)
@receiver(post_save, sender=Right)
def right_post_save(sender, **kwargs):
right = kwargs['instance'].right
right.ldap_sync()
@receiver(post_delete, sender=Right)
def right_post_delete(sender, **kwargs):
right = kwargs['instance'].right
right.ldap_sync()
class School(models.Model):
name = models.CharField(max_length=255)
......@@ -242,10 +295,42 @@ class School(models.Model):
class ListRight(models.Model):
listright = models.CharField(max_length=255, unique=True)
gid = models.IntegerField(unique=True, null=True)
def __str__(self):
return self.listright
def ldap_sync(self):
try:
group_ldap = LdapUserGroup.objects.get(gid=self.gid)
except LdapUserGroup.DoesNotExist:
group_ldap = LdapUserGroup(gid=self.gid)
group_ldap.name = self.listright
group_ldap.members = [right.user.pseudo for right in Right.objects.filter(right=self)]
group_ldap.save()
def ldap_del(self):
try:
group_ldap = LdapUserGroup.objects.get(gid=self.gid)
group_ldap.delete()
except LdapUserGroup.DoesNotExist:
pass
@receiver(post_save, sender=ListRight)
def listright_post_save(sender, **kwargs):
right = kwargs['instance']
right.ldap_sync()
@receiver(post_delete, sender=ListRight)
def listright_post_delete(sender, **kwargs):
right = kwargs['instance']
right.ldap_del()
class ListShell(models.Model):
shell = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.shell
class Ban(models.Model):
user = models.ForeignKey('User', on_delete=models.PROTECT)
......@@ -287,6 +372,59 @@ class Request(models.Model):
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
super(Request, self).save()
class LdapUser(ldapdb.models.Model):
"""
Class for representing an LDAP user entry.
"""
# LDAP meta-data
base_dn = LDAP_SETTINGS['base_user_dn']
object_classes = ['inetOrgPerson','top','posixAccount','sambaSamAccount','radiusprofile']
# attributes
gid = ldapdb.models.fields.IntegerField(db_column='gidNumber')
name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
uid = ldapdb.models.fields.CharField(db_column='uid', max_length=200)
uidNumber = ldapdb.models.fields.IntegerField(db_column='uidNumber', unique=True)
sn = ldapdb.models.fields.CharField(db_column='sn', max_length=200)
loginShell = ldapdb.models.fields.CharField(db_column='loginShell', max_length=200, default="/bin/zsh")
mail = ldapdb.models.fields.CharField(db_column='mail', max_length=200)
given_name = ldapdb.models.fields.CharField(db_column='givenName', max_length=200)
home_directory = ldapdb.models.fields.CharField(db_column='homeDirectory', max_length=200)
display_name = ldapdb.models.fields.CharField(db_column='displayName', max_length=200)
dialupAccess = ldapdb.models.fields.CharField(db_column='dialupAccess')
sambaSID = ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)
user_password = ldapdb.models.fields.CharField(db_column='userPassword', max_length=200, blank=True, null=True)
sambat_nt_password = ldapdb.models.fields.CharField(db_column='sambaNTPassword', max_length=200, blank=True, null=True)
macs = ldapdb.models.fields.ListField(db_column='radiusCallingStationId', max_length=200, blank=True, null=True)
def __str__(self):
return self.name
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
self.sn = self.name
self.uid = self.name
self.sambaSID = self.uidNumber
super(LdapUser, self).save(*args, **kwargs)
class LdapUserGroup(ldapdb.models.Model):
"""
Class for representing an LDAP user entry.
"""
# LDAP meta-data
base_dn = LDAP_SETTINGS['base_usergroup_dn']
object_classes = ['posixGroup']
# attributes
gid = ldapdb.models.fields.IntegerField(db_column='gidNumber')
members = ldapdb.models.fields.ListField(db_column='memberUid', blank=True)
name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
def __str__(self):
return self.name
class BaseInfoForm(ModelForm):
def __init__(self, *args, **kwargs):
super(BaseInfoForm, self).__init__(*args, **kwargs)
......@@ -343,6 +481,29 @@ class SchoolForm(ModelForm):
super(SchoolForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Établissement'
class ListRightForm(ModelForm):
class Meta:
model = ListRight
fields = ['listright']
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__'
def __init__(self, *args, **kwargs):
super(NewListRightForm, self).__init__(*args, **kwargs)
self.fields['gid'].label = 'Gid, attention, cet attribut ne doit pas être modifié après création'
class DelListRightForm(ModelForm):
listrights = forms.ModelMultipleChoiceField(queryset=ListRight.objects.all(), label="Droits actuels", widget=forms.CheckboxSelectMultiple)
class Meta:
exclude = ['listright','gid']
model = ListRight
class DelSchoolForm(ModelForm):
schools = forms.ModelMultipleChoiceField(queryset=School.objects.all(), label="Etablissements actuels", widget=forms.CheckboxSelectMultiple)
......
<table class="table table-striped">
<thead>
<tr>
<th>Droit</th>
<th>Gid</th>
<th></th>
<th></th>
</tr>
</thead>
{% for listright in listright_list %}
<tr>
<td>{{ listright.listright }}</td>
<td>{{ listright.gid }}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-listright' listright.id %}"><i class="glyphicon glyphicon-pushpin"></i> Editer</a></td>
<td><a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'listright' listright.id %}"><i class="glyphicon glyphicon-repeat"></i> Historique</a></td>
</tr>
{% endfor %}
</table>
<table class="table table-striped">
<thead>
<tr>
{% for right in right_list %}
<th>{{ right }}</th>
<th></th>
{% endfor %}
</tr>
</thead>
{% for user_right in user_right_list %}
<tr>
<td> {{ user_right }}</td>
</tr>
{% endfor %}
</table>
{% extends "users/sidebar.html" %}
{% 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 %}
{% extends "users/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
<h2>Droits</h2>
{% include "users/aff_rights.html" %}
<br />
<br />
<br />
{% endblock %}
......@@ -7,6 +7,7 @@
<p><a href="{% url "users:index-ban" %}">Liste des bannissements</a></p>
<p><a href="{% url "users:index-white" %}">Liste des accès à titre gracieux</a></p>
<p><a href="{% url "users:index-school" %}">Liste des établissements</a></p>
<p><a href="{% url "users:index-listright" %}">Liste des droits</a></p>
{% if is_bureau %}
<p><a href="{% url "users:del-right" %}">Retirer un droit</a></p>
{% endif %}
......
......@@ -16,10 +16,14 @@ urlpatterns = [
url(r'^add_school/$', views.add_school, name='add-school'),
url(r'^edit_school/(?P<schoolid>[0-9]+)$', views.edit_school, name='edit-school'),
url(r'^del_school/$', views.del_school, name='del-school'),
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'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
url(r'^index_ban/$', views.index_ban, name='index-ban'),
url(r'^index_white/$', views.index_white, name='index-white'),
url(r'^index_school/$', views.index_school, name='index-school'),
url(r'^index_listright/$', views.index_listright, name='index-listright'),
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'),
......@@ -27,6 +31,7 @@ urlpatterns = [
url(r'^history/(?P<object>ban)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>whitelist)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>school)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>listright)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^$', views.index, name='index'),
]
......
......@@ -16,8 +16,8 @@ from django.db import transaction
from reversion import revisions as reversion
from users.models import User, Right, Ban, Whitelist, School, ListRight, Request
from users.models import DelRightForm, BanForm, WhitelistForm, DelSchoolForm
from users.models import InfoForm, BaseInfoForm, StateForm, RightForm, SchoolForm
from users.models import DelRightForm, BanForm, WhitelistForm, DelSchoolForm, DelListRightForm, NewListRightForm
from users.models import InfoForm, BaseInfoForm, StateForm, RightForm, SchoolForm, ListRightForm
from cotisations.models import Facture
from machines.models import Machine, Interface
from users.forms import PassForm, ResetPasswordForm
......@@ -93,7 +93,7 @@ def new_user(request):
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)
return redirect("/users/profil/" + user.id)
return redirect("/users/profil/" + str(user.id))
return form({'userform': user}, 'users/user.html', request)
@login_required
......@@ -327,6 +327,57 @@ def del_school(request):
return redirect("/users/index_school/")
return form({'userform': school}, 'users/user.html', request)
@login_required
@permission_required('bureau')
def add_listright(request):
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):
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):
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():