8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-23 23:43:47 +00:00

Gestion des services users par re2o

This commit is contained in:
Gabriel Detraz 2017-06-18 14:59:53 +02:00 committed by root
parent f1dab01fc5
commit 2607dc99e5
16 changed files with 433 additions and 15 deletions

View file

@ -219,7 +219,7 @@ olcObjectClasses: {6}( 2.5.6.8 NAME 'organizationalRole' DESC 'RFC2256: an o
street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOffic
eName $ ou $ st $ l $ description ) )
olcObjectClasses: {7}( 2.5.6.9 NAME 'groupOfNames' DESC 'RFC2256: a group of
names (DNs)' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategor
names (DNs)' SUP top STRUCTURAL MUST ( cn ) MAY ( member $ businessCategor
y $ seeAlso $ owner $ ou $ o $ description ) )
olcObjectClasses: {8}( 2.5.6.10 NAME 'residentialPerson' DESC 'RFC2256: an r
esidential person' SUP person STRUCTURAL MUST l MAY ( businessCategory $ x1

View file

@ -25,7 +25,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, ServiceUser, School, Right, ListRight, ListShell, Ban, Whitelist, Request, LdapUser, LdapServiceUser, LdapUserGroup
from .models import User, ServiceUser, School, Right, ListRight, ListShell, Ban, Whitelist, Request, LdapUser, LdapServiceUser, LdapServiceUserGroup, LdapUserGroup
from .forms import UserChangeForm, UserCreationForm, ServiceUserChangeForm, ServiceUserCreationForm
@ -57,6 +57,10 @@ class LdapUserGroupAdmin(admin.ModelAdmin):
list_display = ('name','members','gid')
search_fields = ('name',)
class LdapServiceUserGroupAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name',)
class SchoolAdmin(VersionAdmin):
list_display = ('name',)
@ -116,10 +120,10 @@ class ServiceUserAdmin(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',)
list_display = ('pseudo', 'access_group')
list_filter = ()
fieldsets = (
(None, {'fields': ('pseudo', 'password')}),
(None, {'fields': ('pseudo', 'password', 'access_group')}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
@ -138,6 +142,7 @@ admin.site.register(ServiceUser, ServiceUserAdmin)
admin.site.register(LdapUser, LdapUserAdmin)
admin.site.register(LdapUserGroup, LdapUserGroupAdmin)
admin.site.register(LdapServiceUser, LdapServiceUserAdmin)
admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin)
admin.site.register(School, SchoolAdmin)
admin.site.register(Right, RightAdmin)
admin.site.register(ListRight, ListRightAdmin)

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0042_auto_20161126_2028'),
]
operations = [
migrations.AlterField(
model_name='ldapserviceuser',
name='dn',
field=models.CharField(max_length=200),
),
migrations.AlterField(
model_name='ldapuser',
name='dn',
field=models.CharField(max_length=200),
),
migrations.AlterField(
model_name='ldapusergroup',
name='dn',
field=models.CharField(max_length=200),
),
]

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0043_auto_20161224_1156'),
]
operations = [
migrations.AddField(
model_name='user',
name='ssh_public_key',
field=models.CharField(max_length=2047, null=True, blank=True),
),
]

View file

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0043_ban_state'),
('users', '0044_user_ssh_public_key'),
]
operations = [
]

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-06-17 12:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0045_merge'),
]
operations = [
migrations.RemoveField(
model_name='user',
name='ssh_public_key',
),
migrations.AlterField(
model_name='ban',
name='state',
field=models.IntegerField(choices=[(0, 'HARD (aucun accès)'), (1, 'SOFT (accès local seulement)'), (2, 'BRIDAGE (bridage du débit)')], default=0),
),
migrations.AlterField(
model_name='ldapserviceuser',
name='dn',
field=models.CharField(max_length=200, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='ldapuser',
name='dn',
field=models.CharField(max_length=200, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='ldapusergroup',
name='dn',
field=models.CharField(max_length=200, primary_key=True, serialize=False),
),
]

View file

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-06-17 23:56
from __future__ import unicode_literals
from django.db import migrations, models
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0046_auto_20170617_1433'),
]
operations = [
migrations.CreateModel(
name='LdapServiceUserGroup',
fields=[
('dn', models.CharField(max_length=200, primary_key=True, serialize=False)),
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, serialize=False)),
('members', ldapdb.models.fields.ListField(blank=True, db_column='member')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='serviceuser',
name='access_group',
field=models.IntegerField(choices=[(0, 'auth'), (1, 'readonly'), (2, 'usermgmt')], default=1),
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-06-18 00:10
from __future__ import unicode_literals
from django.db import migrations
import ldapdb.models.fields
class Migration(migrations.Migration):
dependencies = [
('users', '0047_auto_20170618_0156'),
]
operations = [
migrations.AlterField(
model_name='ldapserviceusergroup',
name='name',
field=ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False),
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-06-18 12:24
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0048_auto_20170618_0210'),
]
operations = [
migrations.AlterField(
model_name='serviceuser',
name='access_group',
field=models.CharField(choices=[('auth', 'auth'), ('readonly', 'readonly'), ('usermgmt', 'usermgmt')], default='readonly', max_length=32),
),
]

View file

@ -38,6 +38,7 @@ import datetime
from django.utils import timezone
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.core.validators import MinLengthValidator
from topologie.models import Room
from cotisations.models import Cotisation, Facture, Vente
from machines.models import Interface, Machine
@ -373,9 +374,17 @@ def user_post_delete(sender, **kwargs):
user.ldap_del()
class ServiceUser(AbstractBaseUser):
readonly = 'readonly'
ACCESS = (
('auth', 'auth'),
('readonly', 'readonly'),
('usermgmt', 'usermgmt'),
)
PRETTY_NAME = "Utilisateurs de service"
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator])
access_group = models.CharField(choices=ACCESS, default=readonly, max_length=32)
USERNAME_FIELD = 'pseudo'
@ -386,8 +395,9 @@ class ServiceUser(AbstractBaseUser):
user_ldap = LdapServiceUser.objects.get(name=self.pseudo)
except LdapServiceUser.DoesNotExist:
user_ldap = LdapServiceUser(name=self.pseudo)
user_ldap.user_password = self.password
user_ldap.user_password = self.password[:6] + self.password[7:]
user_ldap.save()
self.serviceuser_group_sync()
def ldap_del(self):
try:
@ -395,6 +405,15 @@ class ServiceUser(AbstractBaseUser):
user_ldap.delete()
except LdapUser.DoesNotExist:
pass
self.serviceuser_group_sync()
def serviceuser_group_sync(self):
try:
group = LdapServiceUserGroup.objects.get(name=self.access_group)
except:
group = LdapServiceUserGroup(name=self.access_group)
group.members = [serviceuser.dn for serviceuser in LdapServiceUser.objects.filter(name__in=[user.pseudo for user in ServiceUser.objects.filter(access_group=self.access_group)])]
group.save()
def __str__(self):
return self.pseudo
@ -402,12 +421,12 @@ class ServiceUser(AbstractBaseUser):
@receiver(post_save, sender=ServiceUser)
def service_user_post_save(sender, **kwargs):
service_user = kwargs['instance']
# service_user.ldap_sync()
service_user.ldap_sync()
@receiver(post_delete, sender=ServiceUser)
def service_user_post_delete(sender, **kwargs):
service_user = kwargs['instance']
# service_user.ldap_del()
service_user.ldap_del()
class Right(models.Model):
PRETTY_NAME = "Droits affectés à des users"
@ -624,6 +643,24 @@ class LdapServiceUser(ldapdb.models.Model):
name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
user_password = ldapdb.models.fields.CharField(db_column='userPassword', max_length=200, blank=True, null=True)
def __str__(self):
return self.name
class LdapServiceUserGroup(ldapdb.models.Model):
"""
Class for representing an LDAP userservice entry.
"""
# LDAP meta-data
base_dn = LDAP['base_userservicegroup_dn']
object_classes = ['groupOfNames']
# attributes
name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
members = ldapdb.models.fields.ListField(db_column='member', blank=True)
def __str__(self):
return self.name
class BaseInfoForm(ModelForm):
def __init__(self, *args, **kwargs):
super(BaseInfoForm, self).__init__(*args, **kwargs)
@ -678,14 +715,15 @@ class PasswordForm(ModelForm):
fields = ['password', 'pwd_ntlm']
class ServiceUserForm(ModelForm):
password = forms.CharField(label=u'Nouveau mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput, required=False)
class Meta:
model = ServiceUser
fields = ('pseudo','password')
fields = ('pseudo','access_group')
class ServicePasswordForm(ModelForm):
class Meta:
model = ServiceUser
fields = ('password',)
class EditServiceUserForm(ServiceUserForm):
class Meta(ServiceUserForm.Meta):
fields = ['access_group']
class StateForm(ModelForm):
class Meta:

View file

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

View file

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

View file

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

View file

@ -50,7 +50,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i class="glyphicon glyphicon-list"></i>
Droits
</a>
{% if is_bureau %}
<a class="list-group-item list-group-item-info" href="{% url "users:index-serviceusers" %}">
<i class="glyphicon glyphicon-list"></i>
Gérer les service users
</a>
{% if is_bureau %}
<a class="list-group-item list-group-item-danger" href="{% url "users:del-right" %}">
<i class="glyphicon glyphicon-trash"></i>
Retirer un droit

View file

@ -29,6 +29,9 @@ urlpatterns = [
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'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'),
url(r'^edit_serviceuser/(?P<userid>[0-9]+)$', views.edit_serviceuser, name='edit-serviceuser'),
url(r'^del_serviceuser/(?P<userid>[0-9]+)$', views.del_serviceuser, name='del-serviceuser'),
url(r'^add_ban/(?P<userid>[0-9]+)$', views.add_ban, name='add-ban'),
url(r'^edit_ban/(?P<banid>[0-9]+)$', views.edit_ban, name='edit-ban'),
url(r'^add_whitelist/(?P<userid>[0-9]+)$', views.add_whitelist, name='add-whitelist'),
@ -46,6 +49,7 @@ urlpatterns = [
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'^index_serviceusers/$', views.index_serviceusers, name='index-serviceusers'),
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'),
@ -55,6 +59,7 @@ urlpatterns = [
url(r'^history/(?P<object>whitelist)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>school)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>listright)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>serviceuser)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^$', views.index, name='index'),
]

View file

@ -38,9 +38,9 @@ from django.db import transaction
from reversion.models import Version
from reversion import revisions as reversion
from users.models import User, Right, Ban, Whitelist, School, ListRight, Request
from users.models import User, Right, Ban, Whitelist, School, ListRight, Request, ServiceUser
from users.models import DelRightForm, BanForm, WhitelistForm, DelSchoolForm, DelListRightForm, NewListRightForm
from users.models import EditInfoForm, InfoForm, BaseInfoForm, StateForm, RightForm, SchoolForm, ListRightForm
from users.models import EditInfoForm, InfoForm, BaseInfoForm, StateForm, RightForm, SchoolForm, EditServiceUserForm, ServiceUserForm, ListRightForm
from cotisations.models import Facture
from machines.models import Machine, Interface
from users.forms import MassArchiveForm, PassForm, ResetPasswordForm
@ -213,6 +213,61 @@ def password(request, userid):
return password_change_action(u_form, user, request)
return form({'userform': u_form}, 'users/user.html', request)
@login_required
@permission_required('infra')
def new_serviceuser(request):
""" Vue de création d'un nouvel utilisateur service"""
user = ServiceUserForm(request.POST or None)
if user.is_valid():
user_object = user.save(commit=False)
with transaction.atomic(), reversion.create_revision():
user_object.set_password(user.cleaned_data['password'])
user_object.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "L'utilisateur %s a été crée" % user_object.pseudo)
return redirect("/users/index_serviceusers/")
return form({'userform': user}, 'users/user.html', request)
@login_required
@permission_required('infra')
def edit_serviceuser(request, userid):
""" Edite un utilisateur à partir de son id,
si l'id est différent de request.user, vérifie la possession du droit cableur """
try:
user = ServiceUser.objects.get(pk=userid)
except ServiceUser.DoesNotExist:
messages.error(request, "Utilisateur inexistant")
return redirect("/users/")
user = EditServiceUserForm(request.POST or None, instance=user)
if user.is_valid():
user_object = user.save(commit=False)
with transaction.atomic(), reversion.create_revision():
if user.cleaned_data['password']:
user_object.set_password(user.cleaned_data['password'])
user_object.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/index_serviceusers")
return form({'userform': user}, 'users/user.html', request)
@login_required
@permission_required('infra')
def del_serviceuser(request, userid):
try:
user = ServiceUser.objects.get(pk=userid)
except ServiceUser.DoesNotExist:
messages.error(request, u"Utilisateur inexistant" )
return redirect("/users/")
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
user.delete()
reversion.set_user(request.user)
messages.success(request, "L'user a été détruite")
return redirect("/users/index_serviceusers/")
return form({'objet': user, 'objet_name': 'serviceuser'}, 'users/delete.html', request)
@login_required
@permission_required('bureau')
def add_right(request, userid):
@ -534,6 +589,13 @@ def index_listright(request):
listright_list = ListRight.objects.order_by('listright')
return render(request, 'users/index_listright.html', {'listright_list':listright_list})
@login_required
@permission_required('cableur')
def index_serviceusers(request):
""" Affiche les users de services (pour les accès ldap)"""
serviceusers_list = ServiceUser.objects.order_by('pseudo')
return render(request, 'users/index_serviceusers.html', {'serviceusers_list':serviceusers_list})
@login_required
def history(request, object, id):
""" Affichage de l'historique : (acl, argument)
@ -551,6 +613,12 @@ def history(request, object, id):
if not request.user.has_perms(('cableur',)) and object_instance != request.user:
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id))
elif object == 'serviceuser' and request.user.has_perms(('cableur',)):
try:
object_instance = ServiceUser.objects.get(pk=id)
except ServiceUser.DoesNotExist:
messages.error(request, "User service inexistant")
return redirect("/users/")
elif object == 'ban':
try:
object_instance = Ban.objects.get(pk=id)