mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-24 20:33:11 +00:00
Merge branch 'feature-move-ldap-to-separate-app' into 'dev'
Feature: move ldap to separate app See merge request re2o/re2o!589
This commit is contained in:
commit
4407d92a0c
27 changed files with 632 additions and 983 deletions
|
@ -112,6 +112,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
0
ldap_sync/__init__.py
Normal file
0
ldap_sync/__init__.py
Normal file
64
ldap_sync/admin.py
Normal file
64
ldap_sync/admin.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
LdapUser,
|
||||||
|
LdapServiceUser,
|
||||||
|
LdapServiceUserGroup,
|
||||||
|
LdapUserGroup,
|
||||||
|
)
|
||||||
|
|
||||||
|
class LdapUserAdmin(admin.ModelAdmin):
|
||||||
|
"""LdapUser Admin view. Can't change password, manage
|
||||||
|
by User General model.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
Django ModelAdmin: Apply on django ModelAdmin
|
||||||
|
|
||||||
|
"""
|
||||||
|
list_display = ("name", "uidNumber", "login_shell")
|
||||||
|
exclude = ("user_password", "sambat_nt_password")
|
||||||
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
class LdapServiceUserAdmin(admin.ModelAdmin):
|
||||||
|
"""LdapServiceUser Admin view. Can't change password, manage
|
||||||
|
by User General model.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
Django ModelAdmin: Apply on django ModelAdmin
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
list_display = ("name",)
|
||||||
|
exclude = ("user_password",)
|
||||||
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
class LdapUserGroupAdmin(admin.ModelAdmin):
|
||||||
|
"""LdapUserGroup Admin view.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
Django ModelAdmin: Apply on django ModelAdmin
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
list_display = ("name", "members", "gid")
|
||||||
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
class LdapServiceUserGroupAdmin(admin.ModelAdmin):
|
||||||
|
"""LdapServiceUserGroup Admin view.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
Django ModelAdmin: Apply on django ModelAdmin
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
list_display = ("name",)
|
||||||
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(LdapUser, LdapUserAdmin)
|
||||||
|
admin.site.register(LdapUserGroup, LdapUserGroupAdmin)
|
||||||
|
admin.site.register(LdapServiceUser, LdapServiceUserAdmin)
|
||||||
|
admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin)
|
5
ldap_sync/apps.py
Normal file
5
ldap_sync/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class LdapSyncConfig(AppConfig):
|
||||||
|
name = 'ldap_sync'
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright © 2018 Maël Kervella
|
# Copyright © 2018 Maël Kervella
|
||||||
|
# Copyright © 2021 Hugo Levy-Falk
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,6 +22,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from users.models import User, ListRight
|
from users.models import User, ListRight
|
||||||
|
from ldap_sync.models import synchronise_user, synchronise_serviceuser, synchronise_usergroup
|
||||||
|
|
||||||
|
|
||||||
def split_lines(lines):
|
def split_lines(lines):
|
||||||
|
@ -89,9 +91,9 @@ def flush_ldap(binddn, bindpass, server, usersdn, groupsdn):
|
||||||
def sync_ldap():
|
def sync_ldap():
|
||||||
"""Syncrhonize the whole LDAP with the DB."""
|
"""Syncrhonize the whole LDAP with the DB."""
|
||||||
for u in User.objects.all():
|
for u in User.objects.all():
|
||||||
u.ldap_sync()
|
synchronise_user(sender=User, instance=u)
|
||||||
for lr in ListRight.objects.all():
|
for lr in ListRight.objects.all():
|
||||||
lr.ldap_sync()
|
synchronise_usergroup(sender=ListRight, instance=lr)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
|
@ -1,6 +1,7 @@
|
||||||
# Copyright © 2017 Gabriel Détraz
|
# Copyright © 2017 Gabriel Détraz
|
||||||
# Copyright © 2017 Lara Kermarec
|
# Copyright © 2017 Lara Kermarec
|
||||||
# Copyright © 2017 Augustin Lemesle
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
# Copyright © 2020 Hugo Levy-Falk
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
from ldap_sync.models import synchronise_user
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
@ -36,5 +38,5 @@ class Command(BaseCommand):
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
for usr in User.objects.all():
|
for user in User.objects.all():
|
||||||
usr.ldap_sync(mac_refresh=options["full"])
|
synchronise_user(sender=User, instance=user)
|
108
ldap_sync/migrations/0001_initial.py
Normal file
108
ldap_sync/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.29 on 2021-01-10 16:59
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
#from django.conf import settings
|
||||||
|
import ldapdb.models.fields
|
||||||
|
|
||||||
|
#from ldap_sync.management.commands.ldap_rebuild import flush_ldap, sync_ldap
|
||||||
|
|
||||||
|
#def rebuild_ldap(apps, schema_editor):
|
||||||
|
# usersdn = settings.LDAP["base_user_dn"]
|
||||||
|
# groupsdn = settings.LDAP["base_usergroup_dn"]
|
||||||
|
# binddn = settings.DATABASES["ldap"]["USER"]
|
||||||
|
# bindpass = settings.DATABASES["ldap"]["PASSWORD"]
|
||||||
|
# server = settings.DATABASES["ldap"]["NAME"]
|
||||||
|
# flush_ldap(binddn, bindpass, server, usersdn, groupsdn)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_foreign_keys')
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LdapServiceUser',
|
||||||
|
fields=[
|
||||||
|
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
||||||
|
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
||||||
|
('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LdapServiceUserGroup',
|
||||||
|
fields=[
|
||||||
|
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
||||||
|
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
||||||
|
('members', ldapdb.models.fields.ListField(blank=True, db_column='member')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LdapUser',
|
||||||
|
fields=[
|
||||||
|
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
||||||
|
('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')),
|
||||||
|
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
||||||
|
('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)),
|
||||||
|
('login_shell', ldapdb.models.fields.CharField(blank=True, db_column='loginShell', max_length=200, null=True)),
|
||||||
|
('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(blank=True, db_column='displayName', max_length=200, null=True)),
|
||||||
|
('dialupAccess', ldapdb.models.fields.CharField(db_column='dialupAccess', max_length=200)),
|
||||||
|
('sambaSID', ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)),
|
||||||
|
('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)),
|
||||||
|
('sambat_nt_password', ldapdb.models.fields.CharField(blank=True, db_column='sambaNTPassword', max_length=200, null=True)),
|
||||||
|
('macs', ldapdb.models.fields.ListField(blank=True, db_column='radiusCallingStationId', max_length=200, null=True)),
|
||||||
|
('shadowexpire', ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LdapUserGroup',
|
||||||
|
fields=[
|
||||||
|
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
||||||
|
('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')),
|
||||||
|
('members', ldapdb.models.fields.ListField(blank=True, db_column='memberUid')),
|
||||||
|
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ldapserviceuser',
|
||||||
|
name='dn',
|
||||||
|
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ldapserviceusergroup',
|
||||||
|
name='dn',
|
||||||
|
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ldapuser',
|
||||||
|
name='dn',
|
||||||
|
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ldapusergroup',
|
||||||
|
name='dn',
|
||||||
|
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
]
|
0
ldap_sync/migrations/__init__.py
Normal file
0
ldap_sync/migrations/__init__.py
Normal file
334
ldap_sync/models.py
Normal file
334
ldap_sync/models.py
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
|
||||||
|
import ldapdb.models
|
||||||
|
import ldapdb.models.fields
|
||||||
|
|
||||||
|
import users.signals
|
||||||
|
import users.models
|
||||||
|
|
||||||
|
import machines.models
|
||||||
|
|
||||||
|
class LdapUser(ldapdb.models.Model):
|
||||||
|
"""A class representing a LdapUser in LDAP, its LDAP conterpart.
|
||||||
|
Synced from re2o django User model, (User django models),
|
||||||
|
with a copy of its attributes/fields into LDAP, so this class is a mirror
|
||||||
|
of the classic django User model.
|
||||||
|
|
||||||
|
The basedn userdn is specified in settings.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: The name of this User
|
||||||
|
uid: The uid (login) for the unix user
|
||||||
|
uidNumber: Linux uid number
|
||||||
|
gid: The default gid number for this user
|
||||||
|
sn: The user "str" pseudo
|
||||||
|
login_shell: Linux shell for the user
|
||||||
|
mail: Email address contact for this user
|
||||||
|
display_name: Pretty display name for this user
|
||||||
|
dialupAccess: Boolean, True for valid membership
|
||||||
|
sambaSID: Identical id as uidNumber
|
||||||
|
user_password: SSHA hashed password of user
|
||||||
|
samba_nt_password: NTLM hashed password of user
|
||||||
|
macs: Multivalued mac address
|
||||||
|
shadowexpire: Set it to 0 to block access for this user and disabled
|
||||||
|
account
|
||||||
|
"""
|
||||||
|
|
||||||
|
# LDAP meta-data
|
||||||
|
base_dn = settings.LDAP["base_user_dn"]
|
||||||
|
object_classes = [
|
||||||
|
"inetOrgPerson",
|
||||||
|
"top",
|
||||||
|
"posixAccount",
|
||||||
|
"sambaSamAccount",
|
||||||
|
"radiusprofile",
|
||||||
|
"shadowAccount",
|
||||||
|
]
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
login_shell = ldapdb.models.fields.CharField(
|
||||||
|
db_column="loginShell", max_length=200, blank=True, null=True
|
||||||
|
)
|
||||||
|
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, blank=True, null=True
|
||||||
|
)
|
||||||
|
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
|
||||||
|
)
|
||||||
|
shadowexpire = ldapdb.models.fields.CharField(
|
||||||
|
db_column="shadowExpire", 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)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(users.signals.synchronise, sender=users.models.User)
|
||||||
|
def synchronise_user(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Synchronise an User to the LDAP.
|
||||||
|
Args:
|
||||||
|
* sender : The model class.
|
||||||
|
* instance : The actual instance being synchronised.
|
||||||
|
* base : Default `True`. When `True`, synchronise basic attributes.
|
||||||
|
* access_refresh : Default `True`. When `True`, synchronise the access time.
|
||||||
|
* mac_refresh : Default `True`. When True, synchronise the list of mac addresses.
|
||||||
|
* group_refresh: Default `False`. When `True` synchronise the groups of the instance.
|
||||||
|
"""
|
||||||
|
base=kwargs.get('base', True)
|
||||||
|
access_refresh=kwargs.get('access_refresh', True)
|
||||||
|
mac_refresh=kwargs.get('mac_refresh', True )
|
||||||
|
group_refresh=kwargs.get('group_refresh', False)
|
||||||
|
|
||||||
|
user=kwargs["instance"]
|
||||||
|
|
||||||
|
if sys.version_info[0] >= 3 and (
|
||||||
|
user.state == user.STATE_ACTIVE
|
||||||
|
or user.state == user.STATE_ARCHIVE
|
||||||
|
or user.state == user.STATE_DISABLED
|
||||||
|
):
|
||||||
|
user.refresh_from_db()
|
||||||
|
try:
|
||||||
|
user_ldap = LdapUser.objects.get(uidNumber=user.uid_number)
|
||||||
|
except LdapUser.DoesNotExist:
|
||||||
|
user_ldap = LdapUser(uidNumber=user.uid_number)
|
||||||
|
base = True
|
||||||
|
access_refresh = True
|
||||||
|
mac_refresh = True
|
||||||
|
if base:
|
||||||
|
user_ldap.name = user.pseudo
|
||||||
|
user_ldap.sn = user.pseudo
|
||||||
|
user_ldap.dialupAccess = str(user.has_access())
|
||||||
|
user_ldap.home_directory = user.home_directory
|
||||||
|
user_ldap.mail = user.get_mail
|
||||||
|
user_ldap.given_name = (
|
||||||
|
user.surname.lower() + "_" + user.name.lower()[:3]
|
||||||
|
)
|
||||||
|
user_ldap.gid = settings.LDAP["user_gid"]
|
||||||
|
if "{SSHA}" in user.password or "{SMD5}" in user.password:
|
||||||
|
# We remove the extra $ added at import from ldap
|
||||||
|
user_ldap.user_password = user.password[:6] + user.password[7:]
|
||||||
|
elif "{crypt}" in user.password:
|
||||||
|
# depending on the length, we need to remove or not a $
|
||||||
|
if len(user.password) == 41:
|
||||||
|
user_ldap.user_password = user.password
|
||||||
|
else:
|
||||||
|
user_ldap.user_password = user.password[:7] + user.password[8:]
|
||||||
|
|
||||||
|
user_ldap.sambat_nt_password = user.pwd_ntlm.upper()
|
||||||
|
if user.get_shell:
|
||||||
|
user_ldap.login_shell = str(user.get_shell)
|
||||||
|
user_ldap.shadowexpire = user.get_shadow_expire
|
||||||
|
if access_refresh:
|
||||||
|
user_ldap.dialupAccess = str(user.has_access())
|
||||||
|
if mac_refresh:
|
||||||
|
user_ldap.macs = [
|
||||||
|
str(mac)
|
||||||
|
for mac in machines.models.Interface.objects.filter(machine__user=user)
|
||||||
|
.values_list("mac_address", flat=True)
|
||||||
|
.distinct()
|
||||||
|
]
|
||||||
|
if group_refresh:
|
||||||
|
# Need to refresh all groups because we don't know which groups
|
||||||
|
# were updated during edition of groups and the user may no longer
|
||||||
|
# be part of the updated group (case of group removal)
|
||||||
|
for group in Group.objects.all():
|
||||||
|
if hasattr(group, "listright"):
|
||||||
|
synchronise_usergroup(users.models.ListRight, instance=group.listright)
|
||||||
|
user_ldap.save()
|
||||||
|
|
||||||
|
@receiver(users.signals.remove, sender=users.models.User)
|
||||||
|
def remove_user(sender, **kwargs):
|
||||||
|
user = kwargs["instance"]
|
||||||
|
try:
|
||||||
|
user_ldap = LdapUser.objects.get(name=user.pseudo)
|
||||||
|
user_ldap.delete()
|
||||||
|
except LdapUser.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@receiver(users.signals.remove_mass, sender=users.models.User)
|
||||||
|
def remove_users(sender, **kwargs):
|
||||||
|
queryset_users = kwargs["queryset"]
|
||||||
|
LdapUser.objects.filter(
|
||||||
|
name__in=list(queryset_users.values_list("pseudo", flat=True))
|
||||||
|
).delete()
|
||||||
|
|
||||||
|
|
||||||
|
class LdapUserGroup(ldapdb.models.Model):
|
||||||
|
"""A class representing a LdapUserGroup in LDAP, its LDAP conterpart.
|
||||||
|
Synced from UserGroup, (ListRight/Group django models),
|
||||||
|
with a copy of its attributes/fields into LDAP, so this class is a mirror
|
||||||
|
of the classic django ListRight model.
|
||||||
|
|
||||||
|
The basedn usergroupdn is specified in settings.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: The name of this LdapUserGroup
|
||||||
|
gid: The gid number for this unix group
|
||||||
|
members: Users dn members of this LdapUserGroup
|
||||||
|
"""
|
||||||
|
|
||||||
|
# LDAP meta-data
|
||||||
|
base_dn = settings.LDAP["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
|
||||||
|
|
||||||
|
@receiver(users.signals.synchronise, sender=users.models.ListRight)
|
||||||
|
def synchronise_usergroup(sender, **kwargs):
|
||||||
|
group = kwargs["instance"]
|
||||||
|
try:
|
||||||
|
group_ldap = LdapUserGroup.objects.get(gid=group.gid)
|
||||||
|
except LdapUserGroup.DoesNotExist:
|
||||||
|
group_ldap = LdapUserGroup(gid=group.gid)
|
||||||
|
group_ldap.name = group.unix_name
|
||||||
|
group_ldap.members = [user.pseudo for user in group.user_set.all()]
|
||||||
|
group_ldap.save()
|
||||||
|
|
||||||
|
@receiver(users.signals.remove, sender=users.models.ListRight)
|
||||||
|
def remove_usergroup(sender, **kwargs):
|
||||||
|
group = kwargs["instance"]
|
||||||
|
try:
|
||||||
|
group_ldap = LdapUserGroup.objects.get(gid=group.gid)
|
||||||
|
group_ldap.delete()
|
||||||
|
except LdapUserGroup.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LdapServiceUser(ldapdb.models.Model):
|
||||||
|
"""A class representing a ServiceUser in LDAP, its LDAP conterpart.
|
||||||
|
Synced from ServiceUser, with a copy of its attributes/fields into LDAP,
|
||||||
|
so this class is a mirror of the classic django ServiceUser model.
|
||||||
|
|
||||||
|
The basedn userservicedn is specified in settings.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: The name of this ServiceUser
|
||||||
|
user_password: The SSHA hashed password of this ServiceUser
|
||||||
|
"""
|
||||||
|
|
||||||
|
# LDAP meta-data
|
||||||
|
base_dn = settings.LDAP["base_userservice_dn"]
|
||||||
|
object_classes = ["applicationProcess", "simpleSecurityObject"]
|
||||||
|
|
||||||
|
# attributes
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def synchronise_serviceuser_group(serviceuser):
|
||||||
|
try:
|
||||||
|
group = LdapServiceUserGroup.objects.get(name=serviceuser.access_group)
|
||||||
|
except:
|
||||||
|
group = LdapServiceUserGroup(name=serviceuser.access_group)
|
||||||
|
group.members = list(
|
||||||
|
LdapServiceUser.objects.filter(
|
||||||
|
name__in=[
|
||||||
|
user.pseudo
|
||||||
|
for user in users.models.ServiceUser.objects.filter(
|
||||||
|
access_group=serviceuser.access_group
|
||||||
|
)
|
||||||
|
]
|
||||||
|
).values_list("dn", flat=True)
|
||||||
|
)
|
||||||
|
group.save()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(users.signals.synchronise, sender=users.models.ServiceUser)
|
||||||
|
def synchronise_serviceuser(sender, **kwargs):
|
||||||
|
user = kwargs["instance"]
|
||||||
|
try:
|
||||||
|
user_ldap = LdapServiceUser.objects.get(name=user.pseudo)
|
||||||
|
except LdapServiceUser.DoesNotExist:
|
||||||
|
user_ldap = LdapServiceUser(name=user.pseudo)
|
||||||
|
user_ldap.user_password = user.password[:6] + user.password[7:]
|
||||||
|
user_ldap.save()
|
||||||
|
synchronise_serviceuser_group(user)
|
||||||
|
|
||||||
|
@receiver(users.signals.remove, sender=users.models.ServiceUser)
|
||||||
|
def remove_serviceuser(sender, **kwargs):
|
||||||
|
user = kwargs["instance"]
|
||||||
|
try:
|
||||||
|
user_ldap = LdapServiceUser.objects.get(name=user.pseudo)
|
||||||
|
user_ldap.delete()
|
||||||
|
except LdapUser.DoesNotExist:
|
||||||
|
pass
|
||||||
|
synchronise_serviceuser_group(user)
|
||||||
|
|
||||||
|
|
||||||
|
class LdapServiceUserGroup(ldapdb.models.Model):
|
||||||
|
"""A class representing a ServiceUserGroup in LDAP, its LDAP conterpart.
|
||||||
|
Synced from ServiceUserGroup, with a copy of its attributes/fields into LDAP,
|
||||||
|
so this class is a mirror of the classic django ServiceUserGroup model.
|
||||||
|
|
||||||
|
The basedn userservicegroupdn is specified in settings.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: The name of this ServiceUserGroup
|
||||||
|
members: ServiceUsers dn members of this ServiceUserGroup
|
||||||
|
"""
|
||||||
|
|
||||||
|
# LDAP meta-data
|
||||||
|
base_dn = settings.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
|
||||||
|
|
3
ldap_sync/tests.py
Normal file
3
ldap_sync/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
4
ldap_sync/urls.py
Normal file
4
ldap_sync/urls.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from django.conf.urls import url
|
||||||
|
from .import views
|
||||||
|
|
||||||
|
urlpatterns = []
|
3
ldap_sync/views.py
Normal file
3
ldap_sync/views.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -106,6 +106,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -116,6 +116,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -79,12 +79,12 @@ def context_optionnal_apps(request):
|
||||||
optionnal_templates_navbar_user_list = [
|
optionnal_templates_navbar_user_list = [
|
||||||
app.views.navbar_user()
|
app.views.navbar_user()
|
||||||
for app in optionnal_apps
|
for app in optionnal_apps
|
||||||
if hasattr(app.views, "navbar_user")
|
if hasattr(app, "views") and hasattr(app.views, "navbar_user")
|
||||||
]
|
]
|
||||||
optionnal_templates_navbar_logout_list = [
|
optionnal_templates_navbar_logout_list = [
|
||||||
app.views.navbar_logout()
|
app.views.navbar_logout()
|
||||||
for app in optionnal_apps
|
for app in optionnal_apps
|
||||||
if hasattr(app.views, "navbar_logout")
|
if hasattr(app, "views") and hasattr(app.views, "navbar_logout")
|
||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
"optionnal_templates_navbar_user_list": optionnal_templates_navbar_user_list,
|
"optionnal_templates_navbar_user_list": optionnal_templates_navbar_user_list,
|
||||||
|
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -112,6 +112,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -46,10 +46,6 @@ from .models import (
|
||||||
Ban,
|
Ban,
|
||||||
Whitelist,
|
Whitelist,
|
||||||
Request,
|
Request,
|
||||||
LdapUser,
|
|
||||||
LdapServiceUser,
|
|
||||||
LdapServiceUserGroup,
|
|
||||||
LdapUserGroup,
|
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
UserAdminForm,
|
UserAdminForm,
|
||||||
|
@ -57,57 +53,6 @@ from .forms import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LdapUserAdmin(admin.ModelAdmin):
|
|
||||||
"""LdapUser Admin view. Can't change password, manage
|
|
||||||
by User General model.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
Django ModelAdmin: Apply on django ModelAdmin
|
|
||||||
|
|
||||||
"""
|
|
||||||
list_display = ("name", "uidNumber", "login_shell")
|
|
||||||
exclude = ("user_password", "sambat_nt_password")
|
|
||||||
search_fields = ("name",)
|
|
||||||
|
|
||||||
|
|
||||||
class LdapServiceUserAdmin(admin.ModelAdmin):
|
|
||||||
"""LdapServiceUser Admin view. Can't change password, manage
|
|
||||||
by User General model.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
Django ModelAdmin: Apply on django ModelAdmin
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
list_display = ("name",)
|
|
||||||
exclude = ("user_password",)
|
|
||||||
search_fields = ("name",)
|
|
||||||
|
|
||||||
|
|
||||||
class LdapUserGroupAdmin(admin.ModelAdmin):
|
|
||||||
"""LdapUserGroup Admin view.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
Django ModelAdmin: Apply on django ModelAdmin
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
list_display = ("name", "members", "gid")
|
|
||||||
search_fields = ("name",)
|
|
||||||
|
|
||||||
|
|
||||||
class LdapServiceUserGroupAdmin(admin.ModelAdmin):
|
|
||||||
"""LdapServiceUserGroup Admin view.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
Django ModelAdmin: Apply on django ModelAdmin
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
list_display = ("name",)
|
|
||||||
search_fields = ("name",)
|
|
||||||
|
|
||||||
|
|
||||||
class SchoolAdmin(VersionAdmin):
|
class SchoolAdmin(VersionAdmin):
|
||||||
"""School Admin view and management.
|
"""School Admin view and management.
|
||||||
|
|
||||||
|
@ -338,10 +283,6 @@ class ServiceUserAdmin(VersionAdmin, BaseUserAdmin):
|
||||||
admin.site.register(Adherent, AdherentAdmin)
|
admin.site.register(Adherent, AdherentAdmin)
|
||||||
admin.site.register(Club, ClubAdmin)
|
admin.site.register(Club, ClubAdmin)
|
||||||
admin.site.register(ServiceUser, ServiceUserAdmin)
|
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(School, SchoolAdmin)
|
||||||
admin.site.register(ListRight, ListRightAdmin)
|
admin.site.register(ListRight, ListRightAdmin)
|
||||||
admin.site.register(ListShell, ListShellAdmin)
|
admin.site.register(ListShell, ListShellAdmin)
|
||||||
|
|
|
@ -113,6 +113,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -114,6 +114,7 @@ class Migration(migrations.Migration):
|
||||||
("users", "0093_user_profile_image"),
|
("users", "0093_user_profile_image"),
|
||||||
("users", "0094_remove_user_profile_image"),
|
("users", "0094_remove_user_profile_image"),
|
||||||
("users", "0095_user_theme"),
|
("users", "0095_user_theme"),
|
||||||
|
("users", "0096_auto_20210110_1811"),
|
||||||
("cotisations", "0001_initial"),
|
("cotisations", "0001_initial"),
|
||||||
("cotisations", "0002_remove_facture_article"),
|
("cotisations", "0002_remove_facture_article"),
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
("cotisations", "0003_auto_20160702_1448"),
|
||||||
|
|
|
@ -1,514 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.29 on 2020-12-30 17:47
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
import ldapdb.models.fields
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('users', '0002_foreign_keys'),
|
|
||||||
]
|
|
||||||
|
|
||||||
replaces = [
|
|
||||||
("users", "0001_initial"),
|
|
||||||
("users", "0002_auto_20160630_2301"),
|
|
||||||
("users", "0003_listrights_rights"),
|
|
||||||
("users", "0004_auto_20160701_2312"),
|
|
||||||
("users", "0005_auto_20160702_0006"),
|
|
||||||
("users", "0006_ban"),
|
|
||||||
("users", "0007_auto_20160702_2322"),
|
|
||||||
("users", "0008_user_registered"),
|
|
||||||
("users", "0009_user_room"),
|
|
||||||
("users", "0010_auto_20160703_1226"),
|
|
||||||
("users", "0011_auto_20160703_1227"),
|
|
||||||
("users", "0012_auto_20160703_1230"),
|
|
||||||
("users", "0013_auto_20160704_1547"),
|
|
||||||
("users", "0014_auto_20160704_1548"),
|
|
||||||
("users", "0015_whitelist"),
|
|
||||||
("users", "0016_auto_20160706_1220"),
|
|
||||||
("users", "0017_auto_20160707_0105"),
|
|
||||||
("users", "0018_auto_20160707_0115"),
|
|
||||||
("users", "0019_auto_20160708_1633"),
|
|
||||||
("users", "0020_request"),
|
|
||||||
("users", "0021_ldapuser"),
|
|
||||||
("users", "0022_ldapuser_sambasid"),
|
|
||||||
("users", "0023_auto_20160724_1908"),
|
|
||||||
("users", "0024_remove_ldapuser_mac_list"),
|
|
||||||
("users", "0025_listshell"),
|
|
||||||
("users", "0026_user_shell"),
|
|
||||||
("users", "0027_auto_20160726_0216"),
|
|
||||||
("users", "0028_auto_20160726_0227"),
|
|
||||||
("users", "0029_auto_20160726_0229"),
|
|
||||||
("users", "0030_auto_20160726_0357"),
|
|
||||||
("users", "0031_auto_20160726_0359"),
|
|
||||||
("users", "0032_auto_20160727_2122"),
|
|
||||||
("users", "0033_remove_ldapuser_loginshell"),
|
|
||||||
("users", "0034_auto_20161018_0037"),
|
|
||||||
("users", "0035_auto_20161018_0046"),
|
|
||||||
("users", "0036_auto_20161022_2146"),
|
|
||||||
("users", "0037_auto_20161028_1906"),
|
|
||||||
("users", "0038_auto_20161031_0258"),
|
|
||||||
("users", "0039_auto_20161119_0033"),
|
|
||||||
("users", "0040_auto_20161119_1709"),
|
|
||||||
("users", "0041_listright_details"),
|
|
||||||
("users", "0042_auto_20161126_2028"),
|
|
||||||
("users", "0043_auto_20161224_1156"),
|
|
||||||
("users", "0043_ban_state"),
|
|
||||||
("users", "0044_user_ssh_public_key"),
|
|
||||||
("users", "0045_merge"),
|
|
||||||
("users", "0046_auto_20170617_1433"),
|
|
||||||
("users", "0047_auto_20170618_0156"),
|
|
||||||
("users", "0048_auto_20170618_0210"),
|
|
||||||
("users", "0049_auto_20170618_1424"),
|
|
||||||
("users", "0050_serviceuser_comment"),
|
|
||||||
("users", "0051_user_telephone"),
|
|
||||||
("users", "0052_ldapuser_shadowexpire"),
|
|
||||||
("users", "0053_auto_20170626_2105"),
|
|
||||||
("users", "0054_auto_20170626_2219"),
|
|
||||||
("users", "0055_auto_20171003_0556"),
|
|
||||||
("users", "0056_auto_20171015_2033"),
|
|
||||||
("users", "0057_auto_20171023_0301"),
|
|
||||||
("users", "0058_auto_20171025_0154"),
|
|
||||||
("users", "0059_auto_20171025_1854"),
|
|
||||||
("users", "0060_auto_20171120_0317"),
|
|
||||||
("users", "0061_auto_20171230_2033"),
|
|
||||||
("users", "0062_auto_20171231_0056"),
|
|
||||||
("users", "0063_auto_20171231_0140"),
|
|
||||||
("users", "0064_auto_20171231_0150"),
|
|
||||||
("users", "0065_auto_20171231_2053"),
|
|
||||||
("users", "0066_grouppermissions"),
|
|
||||||
("users", "0067_serveurpermission"),
|
|
||||||
("users", "0068_auto_20180107_2245"),
|
|
||||||
("users", "0069_club_mailing"),
|
|
||||||
("users", "0070_auto_20180324_1906"),
|
|
||||||
("users", "0071_auto_20180415_1252"),
|
|
||||||
("users", "0072_auto_20180426_2021"),
|
|
||||||
("users", "0073_auto_20180629_1614"),
|
|
||||||
("users", "0074_auto_20180810_2104"),
|
|
||||||
("users", "0074_auto_20180814_1059"),
|
|
||||||
("users", "0075_merge_20180815_2202"),
|
|
||||||
("users", "0076_auto_20180818_1321"),
|
|
||||||
("users", "0077_auto_20180824_1750"),
|
|
||||||
("users", "0078_auto_20181011_1405"),
|
|
||||||
("users", "0079_auto_20181228_2039"),
|
|
||||||
("users", "0080_auto_20190108_1726"),
|
|
||||||
("users", "0081_auto_20190317_0302"),
|
|
||||||
("users", "0082_auto_20190908_1338"),
|
|
||||||
("users", "0083_user_shortcuts_enabled"),
|
|
||||||
("users", "0084_auto_20191120_0159"),
|
|
||||||
("users", "0085_user_email_state"),
|
|
||||||
("users", "0086_user_email_change_date"),
|
|
||||||
("users", "0087_request_email"),
|
|
||||||
("users", "0088_auto_20200417_2312"),
|
|
||||||
("users", "0089_auto_20200418_0112"),
|
|
||||||
("users", "0090_auto_20200421_1825"),
|
|
||||||
("users", "0091_auto_20200423_1256"),
|
|
||||||
("users", "0092_auto_20200502_0057"),
|
|
||||||
("users", "0093_user_profile_image"),
|
|
||||||
("users", "0094_remove_user_profile_image"),
|
|
||||||
("users", "0095_user_theme"),
|
|
||||||
("cotisations", "0001_initial"),
|
|
||||||
("cotisations", "0002_remove_facture_article"),
|
|
||||||
("cotisations", "0003_auto_20160702_1448"),
|
|
||||||
("cotisations", "0004_auto_20160702_1528"),
|
|
||||||
("cotisations", "0005_auto_20160702_1532"),
|
|
||||||
("cotisations", "0006_auto_20160702_1534"),
|
|
||||||
("cotisations", "0007_auto_20160702_1543"),
|
|
||||||
("cotisations", "0008_auto_20160702_1614"),
|
|
||||||
("cotisations", "0009_remove_cotisation_user"),
|
|
||||||
("cotisations", "0010_auto_20160702_1840"),
|
|
||||||
("cotisations", "0011_auto_20160702_1911"),
|
|
||||||
("cotisations", "0012_auto_20160704_0118"),
|
|
||||||
("cotisations", "0013_auto_20160711_2240"),
|
|
||||||
("cotisations", "0014_auto_20160712_0245"),
|
|
||||||
("cotisations", "0015_auto_20160714_2142"),
|
|
||||||
("cotisations", "0016_auto_20160715_0110"),
|
|
||||||
("cotisations", "0017_auto_20170718_2329"),
|
|
||||||
("cotisations", "0018_paiement_type_paiement"),
|
|
||||||
("cotisations", "0019_auto_20170819_0055"),
|
|
||||||
("cotisations", "0020_auto_20170819_0057"),
|
|
||||||
("cotisations", "0021_auto_20170819_0104"),
|
|
||||||
("cotisations", "0022_auto_20170824_0128"),
|
|
||||||
("cotisations", "0023_auto_20170902_1303"),
|
|
||||||
("cotisations", "0024_auto_20171015_2033"),
|
|
||||||
("cotisations", "0025_article_type_user"),
|
|
||||||
("cotisations", "0026_auto_20171028_0126"),
|
|
||||||
("cotisations", "0027_auto_20171029_1156"),
|
|
||||||
("cotisations", "0028_auto_20171231_0007"),
|
|
||||||
("cotisations", "0029_auto_20180414_2056"),
|
|
||||||
("cotisations", "0030_custom_payment"),
|
|
||||||
("cotisations", "0031_comnpaypayment_production"),
|
|
||||||
("cotisations", "0032_custom_invoice"),
|
|
||||||
("cotisations", "0033_auto_20180818_1319"),
|
|
||||||
("cotisations", "0034_auto_20180831_1532"),
|
|
||||||
("cotisations", "0035_notepayment"),
|
|
||||||
("cotisations", "0036_custominvoice_remark"),
|
|
||||||
("cotisations", "0037_costestimate"),
|
|
||||||
("cotisations", "0038_auto_20181231_1657"),
|
|
||||||
("cotisations", "0039_freepayment"),
|
|
||||||
("cotisations", "0040_auto_20191002_2335"),
|
|
||||||
("cotisations", "0041_auto_20191103_2131"),
|
|
||||||
("cotisations", "0042_auto_20191120_0159"),
|
|
||||||
("cotisations", "0043_separation_membership_connection_p1"),
|
|
||||||
("cotisations", "0044_separation_membership_connection_p2"),
|
|
||||||
("cotisations", "0045_separation_membership_connection_p3"),
|
|
||||||
("cotisations", "0046_article_need_membership"),
|
|
||||||
("cotisations", "0047_article_need_membership_init"),
|
|
||||||
("cotisations", "0048_auto_20201017_0018"),
|
|
||||||
("cotisations", "0049_auto_20201102_2305"),
|
|
||||||
("cotisations", "0050_auto_20201102_2342"),
|
|
||||||
("cotisations", "0051_auto_20201228_1636"),
|
|
||||||
("machines", "0001_initial"),
|
|
||||||
("machines", "0002_auto_20160703_1444"),
|
|
||||||
("machines", "0003_auto_20160703_1450"),
|
|
||||||
("machines", "0004_auto_20160703_1451"),
|
|
||||||
("machines", "0005_auto_20160703_1523"),
|
|
||||||
("machines", "0006_auto_20160703_1813"),
|
|
||||||
("machines", "0007_auto_20160703_1816"),
|
|
||||||
("machines", "0008_remove_interface_ipv6"),
|
|
||||||
("machines", "0009_auto_20160703_2358"),
|
|
||||||
("machines", "0010_auto_20160704_0104"),
|
|
||||||
("machines", "0011_auto_20160704_0105"),
|
|
||||||
("machines", "0012_auto_20160704_0118"),
|
|
||||||
("machines", "0013_auto_20160705_1014"),
|
|
||||||
("machines", "0014_auto_20160706_1220"),
|
|
||||||
("machines", "0015_auto_20160707_0105"),
|
|
||||||
("machines", "0016_auto_20160708_1633"),
|
|
||||||
("machines", "0017_auto_20160708_1645"),
|
|
||||||
("machines", "0018_auto_20160708_1813"),
|
|
||||||
("machines", "0019_auto_20160718_1141"),
|
|
||||||
("machines", "0020_auto_20160718_1849"),
|
|
||||||
("machines", "0021_auto_20161006_1943"),
|
|
||||||
("machines", "0022_auto_20161011_1829"),
|
|
||||||
("machines", "0023_iplist_ip_type"),
|
|
||||||
("machines", "0024_machinetype_need_infra"),
|
|
||||||
("machines", "0025_auto_20161023_0038"),
|
|
||||||
("machines", "0026_auto_20161026_1348"),
|
|
||||||
("machines", "0027_alias"),
|
|
||||||
("machines", "0028_iptype_domaine_ip"),
|
|
||||||
("machines", "0029_iptype_domaine_range"),
|
|
||||||
("machines", "0030_auto_20161118_1730"),
|
|
||||||
("machines", "0031_auto_20161119_1709"),
|
|
||||||
("machines", "0032_auto_20161119_1850"),
|
|
||||||
("machines", "0033_extension_need_infra"),
|
|
||||||
("machines", "0034_iplist_need_infra"),
|
|
||||||
("machines", "0035_auto_20161224_1201"),
|
|
||||||
("machines", "0036_auto_20161224_1204"),
|
|
||||||
("machines", "0037_domain_cname"),
|
|
||||||
("machines", "0038_auto_20161224_1721"),
|
|
||||||
("machines", "0039_auto_20161224_1732"),
|
|
||||||
("machines", "0040_remove_interface_dns"),
|
|
||||||
("machines", "0041_remove_ns_interface"),
|
|
||||||
("machines", "0042_ns_ns"),
|
|
||||||
("machines", "0043_auto_20170721_0350"),
|
|
||||||
("machines", "0044_auto_20170808_0233"),
|
|
||||||
("machines", "0045_auto_20170808_0348"),
|
|
||||||
("machines", "0046_auto_20170808_1423"),
|
|
||||||
("machines", "0047_auto_20170809_0606"),
|
|
||||||
("machines", "0048_auto_20170823_2315"),
|
|
||||||
("machines", "0049_vlan"),
|
|
||||||
("machines", "0050_auto_20170826_0022"),
|
|
||||||
("machines", "0051_iptype_vlan"),
|
|
||||||
("machines", "0052_auto_20170828_2322"),
|
|
||||||
("machines", "0053_text"),
|
|
||||||
("machines", "0054_text_zone"),
|
|
||||||
("machines", "0055_nas"),
|
|
||||||
("machines", "0056_nas_port_access_mode"),
|
|
||||||
("machines", "0057_nas_autocapture_mac"),
|
|
||||||
("machines", "0058_auto_20171002_0350"),
|
|
||||||
("machines", "0059_iptype_prefix_v6"),
|
|
||||||
("machines", "0060_iptype_ouverture_ports"),
|
|
||||||
("machines", "0061_auto_20171015_2033"),
|
|
||||||
("machines", "0062_extension_origin_v6"),
|
|
||||||
("machines", "0063_auto_20171020_0040"),
|
|
||||||
("machines", "0064_auto_20171115_0253"),
|
|
||||||
("machines", "0065_auto_20171115_1514"),
|
|
||||||
("machines", "0066_srv"),
|
|
||||||
("machines", "0067_auto_20171116_0152"),
|
|
||||||
("machines", "0068_auto_20171116_0252"),
|
|
||||||
("machines", "0069_auto_20171116_0822"),
|
|
||||||
("machines", "0070_auto_20171231_1947"),
|
|
||||||
("machines", "0071_auto_20171231_2100"),
|
|
||||||
("machines", "0072_auto_20180108_1822"),
|
|
||||||
("machines", "0073_auto_20180128_2203"),
|
|
||||||
("machines", "0074_auto_20180129_0352"),
|
|
||||||
("machines", "0075_auto_20180130_0052"),
|
|
||||||
("machines", "0076_auto_20180130_1623"),
|
|
||||||
("machines", "0077_auto_20180409_2243"),
|
|
||||||
("machines", "0078_auto_20180415_1252"),
|
|
||||||
("machines", "0079_auto_20180416_0107"),
|
|
||||||
("machines", "0080_auto_20180502_2334"),
|
|
||||||
("machines", "0081_auto_20180521_1413"),
|
|
||||||
("machines", "0082_auto_20180525_2209"),
|
|
||||||
("machines", "0083_remove_duplicate_rights"),
|
|
||||||
("machines", "0084_dname"),
|
|
||||||
("machines", "0085_sshfingerprint"),
|
|
||||||
("machines", "0086_role"),
|
|
||||||
("machines", "0087_dnssec"),
|
|
||||||
("machines", "0088_iptype_prefix_v6_length"),
|
|
||||||
("machines", "0089_auto_20180805_1148"),
|
|
||||||
("machines", "0090_auto_20180805_1459"),
|
|
||||||
("machines", "0091_auto_20180806_2310"),
|
|
||||||
("machines", "0092_auto_20180807_0926"),
|
|
||||||
("machines", "0093_auto_20180807_1115"),
|
|
||||||
("machines", "0094_auto_20180815_1918"),
|
|
||||||
("machines", "0095_auto_20180919_2225"),
|
|
||||||
("machines", "0096_auto_20181013_1417"),
|
|
||||||
("machines", "0097_extension_dnssec"),
|
|
||||||
("machines", "0098_auto_20190102_1745"),
|
|
||||||
("machines", "0099_role_recursive_dns"),
|
|
||||||
("machines", "0100_auto_20190102_1753"),
|
|
||||||
("machines", "0101_auto_20190108_1623"),
|
|
||||||
("machines", "0102_auto_20190303_1611"),
|
|
||||||
("machines", "0103_auto_20191002_2222"),
|
|
||||||
("machines", "0104_auto_20191002_2231"),
|
|
||||||
("machines", "0105_dname_ttl"),
|
|
||||||
("machines", "0106_auto_20191120_0159"),
|
|
||||||
("machines", "0107_fix_lowercase_domain"),
|
|
||||||
("machines", "0108_ipv6list_active"),
|
|
||||||
("preferences", "0001_initial"),
|
|
||||||
("preferences", "0002_auto_20170625_1923"),
|
|
||||||
("preferences", "0003_optionaluser_solde_negatif"),
|
|
||||||
("preferences", "0004_assooption_services"),
|
|
||||||
("preferences", "0005_auto_20170824_0139"),
|
|
||||||
("preferences", "0006_auto_20170824_0143"),
|
|
||||||
("preferences", "0007_auto_20170824_2056"),
|
|
||||||
("preferences", "0008_auto_20170824_2122"),
|
|
||||||
("preferences", "0009_assooption_utilisateur_asso"),
|
|
||||||
("preferences", "0010_auto_20170825_0459"),
|
|
||||||
("preferences", "0011_auto_20170825_2307"),
|
|
||||||
("preferences", "0012_generaloption_req_expire_hrs"),
|
|
||||||
("preferences", "0013_generaloption_site_name"),
|
|
||||||
("preferences", "0014_generaloption_email_from"),
|
|
||||||
("preferences", "0015_optionaltopologie_radius_general_policy"),
|
|
||||||
("preferences", "0016_auto_20170902_1520"),
|
|
||||||
("preferences", "0017_mailmessageoption"),
|
|
||||||
("preferences", "0018_optionaltopologie_mac_autocapture"),
|
|
||||||
("preferences", "0019_remove_optionaltopologie_mac_autocapture"),
|
|
||||||
("preferences", "0020_optionalmachine_ipv6"),
|
|
||||||
("preferences", "0021_auto_20171015_1741"),
|
|
||||||
("preferences", "0022_auto_20171015_1758"),
|
|
||||||
("preferences", "0023_auto_20171015_2033"),
|
|
||||||
("preferences", "0024_optionaluser_all_can_create"),
|
|
||||||
("preferences", "0025_auto_20171231_2142"),
|
|
||||||
("preferences", "0025_generaloption_general_message"),
|
|
||||||
("preferences", "0026_auto_20171216_0401"),
|
|
||||||
("preferences", "0027_merge_20180106_2019"),
|
|
||||||
("preferences", "0028_assooption_description"),
|
|
||||||
("preferences", "0028_auto_20180111_1129"),
|
|
||||||
("preferences", "0028_auto_20180128_2203"),
|
|
||||||
("preferences", "0029_auto_20180111_1134"),
|
|
||||||
("preferences", "0029_auto_20180318_0213"),
|
|
||||||
("preferences", "0029_auto_20180318_1005"),
|
|
||||||
("preferences", "0030_auto_20180111_2346"),
|
|
||||||
("preferences", "0030_merge_20180320_1419"),
|
|
||||||
("preferences", "0031_auto_20180323_0218"),
|
|
||||||
("preferences", "0031_optionaluser_self_adhesion"),
|
|
||||||
("preferences", "0032_optionaluser_min_online_payment"),
|
|
||||||
("preferences", "0032_optionaluser_shell_default"),
|
|
||||||
("preferences", "0033_accueiloption"),
|
|
||||||
("preferences", "0033_generaloption_gtu_sum_up"),
|
|
||||||
("preferences", "0034_auto_20180114_2025"),
|
|
||||||
("preferences", "0034_auto_20180416_1120"),
|
|
||||||
("preferences", "0035_auto_20180114_2132"),
|
|
||||||
("preferences", "0035_optionaluser_allow_self_subscription"),
|
|
||||||
("preferences", "0036_auto_20180114_2141"),
|
|
||||||
("preferences", "0037_auto_20180114_2156"),
|
|
||||||
("preferences", "0038_auto_20180114_2209"),
|
|
||||||
("preferences", "0039_auto_20180115_0003"),
|
|
||||||
("preferences", "0040_auto_20180129_1745"),
|
|
||||||
("preferences", "0041_merge_20180130_0052"),
|
|
||||||
("preferences", "0042_auto_20180222_1743"),
|
|
||||||
("preferences", "0043_optionalmachine_create_machine"),
|
|
||||||
("preferences", "0044_remove_payment_pass"),
|
|
||||||
("preferences", "0045_remove_unused_payment_fields"),
|
|
||||||
("preferences", "0046_optionaluser_mail_extension"),
|
|
||||||
("preferences", "0047_mailcontact"),
|
|
||||||
("preferences", "0048_auto_20180811_1515"),
|
|
||||||
("preferences", "0049_optionaluser_self_change_shell"),
|
|
||||||
("preferences", "0050_auto_20180818_1329"),
|
|
||||||
("preferences", "0051_auto_20180919_2225"),
|
|
||||||
("preferences", "0052_optionaluser_delete_notyetactive"),
|
|
||||||
("preferences", "0053_optionaluser_self_change_room"),
|
|
||||||
("preferences", "0055_generaloption_main_site_url"),
|
|
||||||
("preferences", "0056_1_radiusoption"),
|
|
||||||
("preferences", "0056_2_radiusoption"),
|
|
||||||
("preferences", "0056_3_radiusoption"),
|
|
||||||
("preferences", "0056_4_radiusoption"),
|
|
||||||
("preferences", "0057_optionaluser_all_users_active"),
|
|
||||||
("preferences", "0058_auto_20190108_1650"),
|
|
||||||
("preferences", "0059_auto_20190120_1739"),
|
|
||||||
("preferences", "0060_auto_20190712_1821"),
|
|
||||||
("preferences", "0061_optionaluser_allow_archived_connexion"),
|
|
||||||
("preferences", "0062_auto_20190910_1909"),
|
|
||||||
("preferences", "0063_mandate"),
|
|
||||||
("preferences", "0064_auto_20191008_1335"),
|
|
||||||
("preferences", "0065_auto_20191010_1227"),
|
|
||||||
("preferences", "0066_optionalmachine_default_dns_ttl"),
|
|
||||||
("preferences", "0067_auto_20191120_0159"),
|
|
||||||
("preferences", "0068_optionaluser_allow_set_password_during_user_creation"),
|
|
||||||
("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"),
|
|
||||||
("preferences", "0070_auto_20200419_0225"),
|
|
||||||
("preferences", "0071_optionaluser_self_change_pseudo"),
|
|
||||||
("topologie", "0001_initial"),
|
|
||||||
("topologie", "0002_auto_20160703_1118"),
|
|
||||||
("topologie", "0003_room"),
|
|
||||||
("topologie", "0004_auto_20160703_1122"),
|
|
||||||
("topologie", "0005_auto_20160703_1123"),
|
|
||||||
("topologie", "0006_auto_20160703_1129"),
|
|
||||||
("topologie", "0007_auto_20160703_1148"),
|
|
||||||
("topologie", "0008_port_room"),
|
|
||||||
("topologie", "0009_auto_20160703_1200"),
|
|
||||||
("topologie", "0010_auto_20160704_2148"),
|
|
||||||
("topologie", "0011_auto_20160704_2153"),
|
|
||||||
("topologie", "0012_port_machine_interface"),
|
|
||||||
("topologie", "0013_port_related"),
|
|
||||||
("topologie", "0014_auto_20160706_1238"),
|
|
||||||
("topologie", "0015_auto_20160706_1452"),
|
|
||||||
("topologie", "0016_auto_20160706_1531"),
|
|
||||||
("topologie", "0017_auto_20160718_1141"),
|
|
||||||
("topologie", "0018_room_details"),
|
|
||||||
("topologie", "0019_auto_20161026_1348"),
|
|
||||||
("topologie", "0020_auto_20161119_0033"),
|
|
||||||
("topologie", "0021_port_radius"),
|
|
||||||
("topologie", "0022_auto_20161211_1622"),
|
|
||||||
("topologie", "0023_auto_20170817_1654"),
|
|
||||||
("topologie", "0023_auto_20170826_1530"),
|
|
||||||
("topologie", "0024_auto_20170818_1021"),
|
|
||||||
("topologie", "0024_auto_20170826_1800"),
|
|
||||||
("topologie", "0025_merge_20170902_1242"),
|
|
||||||
("topologie", "0026_auto_20170902_1245"),
|
|
||||||
("topologie", "0027_auto_20170905_1442"),
|
|
||||||
("topologie", "0028_auto_20170913_1503"),
|
|
||||||
("topologie", "0029_auto_20171002_0334"),
|
|
||||||
("topologie", "0030_auto_20171004_0235"),
|
|
||||||
("topologie", "0031_auto_20171015_2033"),
|
|
||||||
("topologie", "0032_auto_20171026_0338"),
|
|
||||||
("topologie", "0033_auto_20171231_1743"),
|
|
||||||
("topologie", "0034_borne"),
|
|
||||||
("topologie", "0035_auto_20180324_0023"),
|
|
||||||
("topologie", "0036_transferborne"),
|
|
||||||
("topologie", "0037_auto_20180325_0127"),
|
|
||||||
("topologie", "0038_transfersw"),
|
|
||||||
("topologie", "0039_port_new_switch"),
|
|
||||||
("topologie", "0040_transferports"),
|
|
||||||
("topologie", "0041_transferportsw"),
|
|
||||||
("topologie", "0042_transferswitch"),
|
|
||||||
("topologie", "0043_renamenewswitch"),
|
|
||||||
("topologie", "0044_auto_20180326_0002"),
|
|
||||||
("topologie", "0045_auto_20180326_0123"),
|
|
||||||
("topologie", "0046_auto_20180326_0129"),
|
|
||||||
("topologie", "0047_ap_machine"),
|
|
||||||
("topologie", "0048_ap_machine"),
|
|
||||||
("topologie", "0049_switchs_machine"),
|
|
||||||
("topologie", "0050_port_new_switch"),
|
|
||||||
("topologie", "0051_switchs_machine"),
|
|
||||||
("topologie", "0052_transferports"),
|
|
||||||
("topologie", "0053_finalsw"),
|
|
||||||
("topologie", "0054_auto_20180326_1742"),
|
|
||||||
("topologie", "0055_auto_20180329_0431"),
|
|
||||||
("topologie", "0056_building_switchbay"),
|
|
||||||
("topologie", "0057_auto_20180408_0316"),
|
|
||||||
("topologie", "0058_remove_switch_location"),
|
|
||||||
("topologie", "0059_auto_20180415_2249"),
|
|
||||||
("topologie", "0060_server"),
|
|
||||||
("topologie", "0061_portprofile"),
|
|
||||||
("topologie", "0062_auto_20180815_1918"),
|
|
||||||
("topologie", "0063_auto_20180919_2225"),
|
|
||||||
("topologie", "0064_switch_automatic_provision"),
|
|
||||||
("topologie", "0065_auto_20180927_1836"),
|
|
||||||
("topologie", "0066_modelswitch_commercial_name"),
|
|
||||||
("topologie", "0067_auto_20181230_1819"),
|
|
||||||
("topologie", "0068_auto_20190102_1758"),
|
|
||||||
("topologie", "0069_auto_20190108_1439"),
|
|
||||||
("topologie", "0070_auto_20190218_1743"),
|
|
||||||
("topologie", "0071_auto_20190218_1936"),
|
|
||||||
("topologie", "0072_auto_20190720_2318"),
|
|
||||||
("topologie", "0073_auto_20191120_0159"),
|
|
||||||
("topologie", "0074_auto_20200419_1640"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='LdapServiceUser',
|
|
||||||
fields=[
|
|
||||||
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
|
||||||
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
|
||||||
('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='LdapServiceUserGroup',
|
|
||||||
fields=[
|
|
||||||
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
|
||||||
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
|
||||||
('members', ldapdb.models.fields.ListField(blank=True, db_column='member')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='LdapUser',
|
|
||||||
fields=[
|
|
||||||
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
|
||||||
('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')),
|
|
||||||
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
|
||||||
('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)),
|
|
||||||
('login_shell', ldapdb.models.fields.CharField(blank=True, db_column='loginShell', max_length=200, null=True)),
|
|
||||||
('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(blank=True, db_column='displayName', max_length=200, null=True)),
|
|
||||||
('dialupAccess', ldapdb.models.fields.CharField(db_column='dialupAccess', max_length=200)),
|
|
||||||
('sambaSID', ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)),
|
|
||||||
('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)),
|
|
||||||
('sambat_nt_password', ldapdb.models.fields.CharField(blank=True, db_column='sambaNTPassword', max_length=200, null=True)),
|
|
||||||
('macs', ldapdb.models.fields.ListField(blank=True, db_column='radiusCallingStationId', max_length=200, null=True)),
|
|
||||||
('shadowexpire', ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='LdapUserGroup',
|
|
||||||
fields=[
|
|
||||||
('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)),
|
|
||||||
('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')),
|
|
||||||
('members', ldapdb.models.fields.ListField(blank=True, db_column='memberUid')),
|
|
||||||
('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='ldapserviceuser',
|
|
||||||
name='dn',
|
|
||||||
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='ldapserviceusergroup',
|
|
||||||
name='dn',
|
|
||||||
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='ldapuser',
|
|
||||||
name='dn',
|
|
||||||
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='ldapusergroup',
|
|
||||||
name='dn',
|
|
||||||
field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False),
|
|
||||||
),
|
|
||||||
]
|
|
27
users/migrations/0096_auto_20210110_1811.py
Normal file
27
users/migrations/0096_auto_20210110_1811.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.29 on 2021-01-10 17:11
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0095_user_theme'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='LdapServiceUser',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='LdapServiceUserGroup',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='LdapUser',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='LdapUserGroup',
|
||||||
|
),
|
||||||
|
]
|
435
users/models.py
435
users/models.py
|
@ -39,14 +39,6 @@ Here are defined the following django models :
|
||||||
* Schools (teaching structures)
|
* Schools (teaching structures)
|
||||||
* Rights (Groups and ListRight)
|
* Rights (Groups and ListRight)
|
||||||
* ServiceUser (for ldap connexions)
|
* ServiceUser (for ldap connexions)
|
||||||
|
|
||||||
Also define django-ldapdb models :
|
|
||||||
* LdapUser
|
|
||||||
* LdapGroup
|
|
||||||
* LdapServiceUser
|
|
||||||
|
|
||||||
These objects are sync from django regular models as auxiliary models from
|
|
||||||
sql data into ldap.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,8 +74,6 @@ from django.core.files.uploadedfile import InMemoryUploadedFile
|
||||||
|
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
import ldapdb.models
|
|
||||||
import ldapdb.models.fields
|
|
||||||
|
|
||||||
from re2o.settings import LDAP, GID_RANGES, UID_RANGES
|
from re2o.settings import LDAP, GID_RANGES, UID_RANGES
|
||||||
from re2o.field_permissions import FieldPermissionModelMixin
|
from re2o.field_permissions import FieldPermissionModelMixin
|
||||||
|
@ -96,6 +86,8 @@ from machines.models import Domain, Interface, Machine, regen
|
||||||
from preferences.models import GeneralOption, AssoOption, OptionalUser
|
from preferences.models import GeneralOption, AssoOption, OptionalUser
|
||||||
from preferences.models import OptionalMachine, MailMessageOption
|
from preferences.models import OptionalMachine, MailMessageOption
|
||||||
|
|
||||||
|
from users import signals
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import sys
|
import sys
|
||||||
|
@ -1042,7 +1034,7 @@ class User(
|
||||||
"""
|
"""
|
||||||
cls.mass_disable_email(queryset_users)
|
cls.mass_disable_email(queryset_users)
|
||||||
Machine.mass_delete(Machine.objects.filter(user__in=queryset_users))
|
Machine.mass_delete(Machine.objects.filter(user__in=queryset_users))
|
||||||
cls.ldap_delete_users(queryset_users)
|
signals.remove_mass.send(sender=cls, queryset=queryset_users)
|
||||||
|
|
||||||
def archive(self):
|
def archive(self):
|
||||||
"""Method, archive user by unassigning ips.
|
"""Method, archive user by unassigning ips.
|
||||||
|
@ -1072,7 +1064,7 @@ class User(
|
||||||
|
|
||||||
def full_archive(self):
|
def full_archive(self):
|
||||||
"""Method, full archive an user by unassigning ips, deleting data
|
"""Method, full archive an user by unassigning ips, deleting data
|
||||||
and ldap deletion.
|
and authentication deletion.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
self (user instance): user to full archive.
|
self (user instance): user to full archive.
|
||||||
|
@ -1080,7 +1072,7 @@ class User(
|
||||||
"""
|
"""
|
||||||
self.archive()
|
self.archive()
|
||||||
self.delete_data()
|
self.delete_data()
|
||||||
self.ldap_del()
|
signals.remove.send(sender=User, instance=self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mass_full_archive(cls, users_list):
|
def mass_full_archive(cls, users_list):
|
||||||
|
@ -1102,14 +1094,14 @@ class User(
|
||||||
|
|
||||||
def unarchive(self):
|
def unarchive(self):
|
||||||
"""Method, unarchive an user by assigning ips, and recreating
|
"""Method, unarchive an user by assigning ips, and recreating
|
||||||
ldap user associated.
|
authentication user associated.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
self (user instance): user to unarchive.
|
self (user instance): user to unarchive.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.assign_ips()
|
self.assign_ips()
|
||||||
self.ldap_sync()
|
signals.synchronise.send(sender=self.__class__, instance=self)
|
||||||
|
|
||||||
def state_sync(self):
|
def state_sync(self):
|
||||||
"""Master Method, call unarchive, full_archive or archive method
|
"""Master Method, call unarchive, full_archive or archive method
|
||||||
|
@ -1135,109 +1127,6 @@ class User(
|
||||||
):
|
):
|
||||||
self.full_archive()
|
self.full_archive()
|
||||||
|
|
||||||
def ldap_sync(
|
|
||||||
self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False
|
|
||||||
):
|
|
||||||
"""Method ldap_sync, sync in ldap with self user attributes.
|
|
||||||
Each User instance is copy into ldap, via a LdapUser virtual objects.
|
|
||||||
This method performs a copy of several attributes (name, surname, mail,
|
|
||||||
hashed SSHA password, ntlm password, shell, homedirectory).
|
|
||||||
|
|
||||||
Update, or create if needed a ldap entry related with the User instance.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (user instance): user to sync in ldap.
|
|
||||||
base (boolean): Default true, if base is true, perform a basic
|
|
||||||
sync of basic attributes.
|
|
||||||
access_refresh (boolean): Default true, if access_refresh is true,
|
|
||||||
update the dialup_access attributes based on has_access (is this user
|
|
||||||
has a valid internet access).
|
|
||||||
mac_refresh (boolean): Default true, if mac_refresh, update the mac_address
|
|
||||||
list of the user.
|
|
||||||
group_refresh (boolean): Default False, if true, update the groups membership
|
|
||||||
of this user. Onerous option, call ldap_sync() on every groups of the user.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if sys.version_info[0] >= 3 and (
|
|
||||||
self.state == self.STATE_ACTIVE
|
|
||||||
or self.state == self.STATE_ARCHIVE
|
|
||||||
or self.state == self.STATE_DISABLED
|
|
||||||
):
|
|
||||||
self.refresh_from_db()
|
|
||||||
try:
|
|
||||||
user_ldap = LdapUser.objects.get(uidNumber=self.uid_number)
|
|
||||||
except LdapUser.DoesNotExist:
|
|
||||||
user_ldap = LdapUser(uidNumber=self.uid_number)
|
|
||||||
base = True
|
|
||||||
access_refresh = True
|
|
||||||
mac_refresh = True
|
|
||||||
if base:
|
|
||||||
user_ldap.name = self.pseudo
|
|
||||||
user_ldap.sn = self.pseudo
|
|
||||||
user_ldap.dialupAccess = str(self.has_access())
|
|
||||||
user_ldap.home_directory = self.home_directory
|
|
||||||
user_ldap.mail = self.get_mail
|
|
||||||
user_ldap.given_name = (
|
|
||||||
self.surname.lower() + "_" + self.name.lower()[:3]
|
|
||||||
)
|
|
||||||
user_ldap.gid = LDAP["user_gid"]
|
|
||||||
if "{SSHA}" in self.password or "{SMD5}" in self.password:
|
|
||||||
# We remove the extra $ added at import from ldap
|
|
||||||
user_ldap.user_password = self.password[:6] + self.password[7:]
|
|
||||||
elif "{crypt}" in self.password:
|
|
||||||
# depending on the length, we need to remove or not a $
|
|
||||||
if len(self.password) == 41:
|
|
||||||
user_ldap.user_password = self.password
|
|
||||||
else:
|
|
||||||
user_ldap.user_password = self.password[:7] + self.password[8:]
|
|
||||||
|
|
||||||
user_ldap.sambat_nt_password = self.pwd_ntlm.upper()
|
|
||||||
if self.get_shell:
|
|
||||||
user_ldap.login_shell = str(self.get_shell)
|
|
||||||
user_ldap.shadowexpire = self.get_shadow_expire
|
|
||||||
if access_refresh:
|
|
||||||
user_ldap.dialupAccess = str(self.has_access())
|
|
||||||
if mac_refresh:
|
|
||||||
user_ldap.macs = [
|
|
||||||
str(mac)
|
|
||||||
for mac in Interface.objects.filter(machine__user=self)
|
|
||||||
.values_list("mac_address", flat=True)
|
|
||||||
.distinct()
|
|
||||||
]
|
|
||||||
if group_refresh:
|
|
||||||
# Need to refresh all groups because we don't know which groups
|
|
||||||
# were updated during edition of groups and the user may no longer
|
|
||||||
# be part of the updated group (case of group removal)
|
|
||||||
for group in Group.objects.all():
|
|
||||||
if hasattr(group, "listright"):
|
|
||||||
group.listright.ldap_sync()
|
|
||||||
user_ldap.save()
|
|
||||||
|
|
||||||
def ldap_del(self):
|
|
||||||
"""Method, delete an user in ldap.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (user instance): user to delete in Ldap.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
user_ldap = LdapUser.objects.get(name=self.pseudo)
|
|
||||||
user_ldap.delete()
|
|
||||||
except LdapUser.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def ldap_delete_users(cls, queryset_users):
|
|
||||||
"""Class method, delete several users in ldap (queryset).
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
queryset_users (list of users queryset): users to delete
|
|
||||||
in ldap.
|
|
||||||
"""
|
|
||||||
LdapUser.objects.filter(
|
|
||||||
name__in=list(queryset_users.values_list("pseudo", flat=True))
|
|
||||||
)
|
|
||||||
|
|
||||||
###### Send mail functions ######
|
###### Send mail functions ######
|
||||||
|
|
||||||
def notif_inscription(self, request=None):
|
def notif_inscription(self, request=None):
|
||||||
|
@ -2195,7 +2084,7 @@ class Club(User):
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def user_post_save(**kwargs):
|
def user_post_save(**kwargs):
|
||||||
"""Django signal, post save operations on Adherent, Club and User.
|
"""Django signal, post save operations on Adherent, Club and User.
|
||||||
Sync pseudo, sync ldap, create mailalias and send welcome email if needed
|
Sync pseudo, sync authentication, create mailalias and send welcome email if needed
|
||||||
(new user)
|
(new user)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -2207,8 +2096,7 @@ def user_post_save(**kwargs):
|
||||||
user.notif_inscription(user.request)
|
user.notif_inscription(user.request)
|
||||||
user.set_active()
|
user.set_active()
|
||||||
user.state_sync()
|
user.state_sync()
|
||||||
user.ldap_sync(
|
signals.synchronise.send(sender=User, instance=user, base=True, access_refresh=True, mac_refresh=False, group_refresh=True
|
||||||
base=True, access_refresh=True, mac_refresh=False, group_refresh=True
|
|
||||||
)
|
)
|
||||||
regen("mailing")
|
regen("mailing")
|
||||||
|
|
||||||
|
@ -2216,14 +2104,13 @@ def user_post_save(**kwargs):
|
||||||
@receiver(m2m_changed, sender=User.groups.through)
|
@receiver(m2m_changed, sender=User.groups.through)
|
||||||
def user_group_relation_changed(**kwargs):
|
def user_group_relation_changed(**kwargs):
|
||||||
"""Django signal, used for User Groups change (related models).
|
"""Django signal, used for User Groups change (related models).
|
||||||
Sync ldap, with calling group_refresh.
|
Sync authentication, with calling group_refresh.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
action = kwargs["action"]
|
action = kwargs["action"]
|
||||||
if action in ("post_add", "post_remove", "post_clear"):
|
if action in ("post_add", "post_remove", "post_clear"):
|
||||||
user = kwargs["instance"]
|
user = kwargs["instance"]
|
||||||
user.ldap_sync(
|
signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=False, mac_refresh=False, group_refresh=True
|
||||||
base=False, access_refresh=False, mac_refresh=False, group_refresh=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2232,20 +2119,20 @@ def user_group_relation_changed(**kwargs):
|
||||||
@receiver(post_delete, sender=User)
|
@receiver(post_delete, sender=User)
|
||||||
def user_post_delete(**kwargs):
|
def user_post_delete(**kwargs):
|
||||||
"""Django signal, post delete operations on Adherent, Club and User.
|
"""Django signal, post delete operations on Adherent, Club and User.
|
||||||
Delete user in ldap.
|
Delete user in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
user = kwargs["instance"]
|
user = kwargs["instance"]
|
||||||
user.ldap_del()
|
signals.remove.send(sender=User, instance=user)
|
||||||
regen("mailing")
|
regen("mailing")
|
||||||
|
|
||||||
|
|
||||||
class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||||
"""A class representing a serviceuser (it is considered as a user
|
"""A class representing a serviceuser (it is considered as a user
|
||||||
with special informations).
|
with special informations).
|
||||||
The serviceuser is a special user used with special access to ldap tree. It is
|
The serviceuser is a special user used with special access to authentication tree. It is
|
||||||
its only usefullness, and service user can't connect to re2o.
|
its only usefullness, and service user can't connect to re2o.
|
||||||
Each service connected to ldap for auth (ex dokuwiki, owncloud, etc) should
|
Each service connected to authentication for auth (ex dokuwiki, owncloud, etc) should
|
||||||
have a different service user with special acl (readonly, auth) and password.
|
have a different service user with special acl (readonly, auth) and password.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
@ -2293,65 +2180,6 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
return self.pseudo
|
return self.pseudo
|
||||||
|
|
||||||
def ldap_sync(self):
|
|
||||||
"""Method ldap_sync, sync the serviceuser in ldap with its attributes.
|
|
||||||
Each ServiceUser instance is copy into ldap, via a LdapServiceUser virtual object.
|
|
||||||
This method performs a copy of several attributes (pseudo, access).
|
|
||||||
|
|
||||||
Update, or create if needed a mirror ldap entry related with the ServiceUserinstance.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (serviceuser instance): ServiceUser to sync in ldap.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
user_ldap = LdapServiceUser.objects.get(name=self.pseudo)
|
|
||||||
except LdapServiceUser.DoesNotExist:
|
|
||||||
user_ldap = LdapServiceUser(name=self.pseudo)
|
|
||||||
user_ldap.user_password = self.password[:6] + self.password[7:]
|
|
||||||
user_ldap.save()
|
|
||||||
self.serviceuser_group_sync()
|
|
||||||
|
|
||||||
def ldap_del(self):
|
|
||||||
"""Method, delete an ServiceUser in ldap.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (ServiceUser instance): serviceuser to delete in Ldap.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
user_ldap = LdapServiceUser.objects.get(name=self.pseudo)
|
|
||||||
user_ldap.delete()
|
|
||||||
except LdapUser.DoesNotExist:
|
|
||||||
pass
|
|
||||||
self.serviceuser_group_sync()
|
|
||||||
|
|
||||||
def serviceuser_group_sync(self):
|
|
||||||
"""Method, update serviceuser group sync in ldap.
|
|
||||||
In LDAP, Acl depends on the ldapgroup (readonly, auth, or usermgt),
|
|
||||||
so the ldap group need to be synced with the accessgroup field on ServiceUser.
|
|
||||||
Called by ldap_sync and ldap_del.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (ServiceUser instance): serviceuser to update groups in LDAP.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
group = LdapServiceUserGroup.objects.get(name=self.access_group)
|
|
||||||
except:
|
|
||||||
group = LdapServiceUserGroup(name=self.access_group)
|
|
||||||
group.members = list(
|
|
||||||
LdapServiceUser.objects.filter(
|
|
||||||
name__in=[
|
|
||||||
user.pseudo
|
|
||||||
for user in ServiceUser.objects.filter(
|
|
||||||
access_group=self.access_group
|
|
||||||
)
|
|
||||||
]
|
|
||||||
).values_list("dn", flat=True)
|
|
||||||
)
|
|
||||||
group.save()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.pseudo
|
return self.pseudo
|
||||||
|
|
||||||
|
@ -2359,21 +2187,21 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||||
@receiver(post_save, sender=ServiceUser)
|
@receiver(post_save, sender=ServiceUser)
|
||||||
def service_user_post_save(**kwargs):
|
def service_user_post_save(**kwargs):
|
||||||
"""Django signal, post save operations on ServiceUser.
|
"""Django signal, post save operations on ServiceUser.
|
||||||
Sync or create serviceuser in ldap.
|
Sync or create serviceuser in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
service_user = kwargs["instance"]
|
service_user = kwargs["instance"]
|
||||||
service_user.ldap_sync()
|
signals.synchronise.send(sender=ServiceUser, instance=service_user)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=ServiceUser)
|
@receiver(post_delete, sender=ServiceUser)
|
||||||
def service_user_post_delete(**kwargs):
|
def service_user_post_delete(**kwargs):
|
||||||
"""Django signal, post delete operations on ServiceUser.
|
"""Django signal, post delete operations on ServiceUser.
|
||||||
Delete service user in ldap.
|
Delete service user in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
service_user = kwargs["instance"]
|
service_user = kwargs["instance"]
|
||||||
service_user.ldap_del()
|
signals.remove.send(sender=ServiceUser, instance=service_user)
|
||||||
|
|
||||||
|
|
||||||
class School(RevMixin, AclMixin, models.Model):
|
class School(RevMixin, AclMixin, models.Model):
|
||||||
|
@ -2448,58 +2276,25 @@ class ListRight(RevMixin, AclMixin, Group):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def ldap_sync(self):
|
|
||||||
"""Method ldap_sync, sync the listright/group in ldap with its listright attributes.
|
|
||||||
Each ListRight/Group instance is copy into ldap, via a LdapUserGroup virtual objects.
|
|
||||||
This method performs a copy of several attributes (name, members, gid, etc).
|
|
||||||
The primary key is the gid, and should never change.
|
|
||||||
|
|
||||||
Update, or create if needed a ldap entry related with the ListRight/Group instance.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (listright instance): ListRight/Group to sync in ldap.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
group_ldap = LdapUserGroup.objects.get(gid=self.gid)
|
|
||||||
except LdapUserGroup.DoesNotExist:
|
|
||||||
group_ldap = LdapUserGroup(gid=self.gid)
|
|
||||||
group_ldap.name = self.unix_name
|
|
||||||
group_ldap.members = [user.pseudo for user in self.user_set.all()]
|
|
||||||
group_ldap.save()
|
|
||||||
|
|
||||||
def ldap_del(self):
|
|
||||||
"""Method, delete an ListRight/Group in ldap.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self (listright/Group instance): group to delete in Ldap.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
group_ldap = LdapUserGroup.objects.get(gid=self.gid)
|
|
||||||
group_ldap.delete()
|
|
||||||
except LdapUserGroup.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=ListRight)
|
@receiver(post_save, sender=ListRight)
|
||||||
def listright_post_save(**kwargs):
|
def listright_post_save(**kwargs):
|
||||||
"""Django signal, post save operations on ListRight/Group objects.
|
"""Django signal, post save operations on ListRight/Group objects.
|
||||||
Sync or create group in ldap.
|
Sync or create group in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
right = kwargs["instance"]
|
right = kwargs["instance"]
|
||||||
right.ldap_sync()
|
signals.synchronise.send(sender=ListRight, instance=right)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=ListRight)
|
@receiver(post_delete, sender=ListRight)
|
||||||
def listright_post_delete(**kwargs):
|
def listright_post_delete(**kwargs):
|
||||||
"""Django signal, post delete operations on ListRight/Group objects.
|
"""Django signal, post delete operations on ListRight/Group objects.
|
||||||
Delete group in ldap.
|
Delete group in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
right = kwargs["instance"]
|
right = kwargs["instance"]
|
||||||
right.ldap_del()
|
signals.remove.send(sender=ListRight, instance=right)
|
||||||
|
|
||||||
|
|
||||||
class ListShell(RevMixin, AclMixin, models.Model):
|
class ListShell(RevMixin, AclMixin, models.Model):
|
||||||
|
@ -2649,13 +2444,13 @@ class Ban(RevMixin, AclMixin, models.Model):
|
||||||
@receiver(post_save, sender=Ban)
|
@receiver(post_save, sender=Ban)
|
||||||
def ban_post_save(**kwargs):
|
def ban_post_save(**kwargs):
|
||||||
"""Django signal, post save operations on Ban objects.
|
"""Django signal, post save operations on Ban objects.
|
||||||
Sync user's access state in ldap, call email notification if needed.
|
Sync user's access state in authentication, call email notification if needed.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ban = kwargs["instance"]
|
ban = kwargs["instance"]
|
||||||
is_created = kwargs["created"]
|
is_created = kwargs["created"]
|
||||||
user = ban.user
|
user = ban.user
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False)
|
||||||
regen("mailing")
|
regen("mailing")
|
||||||
if is_created:
|
if is_created:
|
||||||
ban.notif_ban(ban.request)
|
ban.notif_ban(ban.request)
|
||||||
|
@ -2669,11 +2464,11 @@ def ban_post_save(**kwargs):
|
||||||
@receiver(post_delete, sender=Ban)
|
@receiver(post_delete, sender=Ban)
|
||||||
def ban_post_delete(**kwargs):
|
def ban_post_delete(**kwargs):
|
||||||
"""Django signal, post delete operations on Ban objects.
|
"""Django signal, post delete operations on Ban objects.
|
||||||
Sync user's access state in ldap.
|
Sync user's access state in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
user = kwargs["instance"].user
|
user = kwargs["instance"].user
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False)
|
||||||
regen("mailing")
|
regen("mailing")
|
||||||
regen("dhcp")
|
regen("dhcp")
|
||||||
regen("mac_ip_list")
|
regen("mac_ip_list")
|
||||||
|
@ -2740,12 +2535,12 @@ class Whitelist(RevMixin, AclMixin, models.Model):
|
||||||
@receiver(post_save, sender=Whitelist)
|
@receiver(post_save, sender=Whitelist)
|
||||||
def whitelist_post_save(**kwargs):
|
def whitelist_post_save(**kwargs):
|
||||||
"""Django signal, post save operations on Whitelist objects.
|
"""Django signal, post save operations on Whitelist objects.
|
||||||
Sync user's access state in ldap.
|
Sync user's access state in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
whitelist = kwargs["instance"]
|
whitelist = kwargs["instance"]
|
||||||
user = whitelist.user
|
user = whitelist.user
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False)
|
||||||
is_created = kwargs["created"]
|
is_created = kwargs["created"]
|
||||||
regen("mailing")
|
regen("mailing")
|
||||||
if is_created:
|
if is_created:
|
||||||
|
@ -2759,11 +2554,11 @@ def whitelist_post_save(**kwargs):
|
||||||
@receiver(post_delete, sender=Whitelist)
|
@receiver(post_delete, sender=Whitelist)
|
||||||
def whitelist_post_delete(**kwargs):
|
def whitelist_post_delete(**kwargs):
|
||||||
"""Django signal, post delete operations on Whitelist objects.
|
"""Django signal, post delete operations on Whitelist objects.
|
||||||
Sync user's access state in ldap.
|
Sync user's access state in authentication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
user = kwargs["instance"].user
|
user = kwargs["instance"].user
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False)
|
||||||
regen("mailing")
|
regen("mailing")
|
||||||
regen("dhcp")
|
regen("dhcp")
|
||||||
regen("mac_ip_list")
|
regen("mac_ip_list")
|
||||||
|
@ -2996,171 +2791,3 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
|
||||||
raise ValidationError(reason)
|
raise ValidationError(reason)
|
||||||
super(EMailAddress, self).clean(*args, **kwargs)
|
super(EMailAddress, self).clean(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class LdapUser(ldapdb.models.Model):
|
|
||||||
"""A class representing a LdapUser in LDAP, its LDAP conterpart.
|
|
||||||
Synced from re2o django User model, (User django models),
|
|
||||||
with a copy of its attributes/fields into LDAP, so this class is a mirror
|
|
||||||
of the classic django User model.
|
|
||||||
|
|
||||||
The basedn userdn is specified in settings.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
name: The name of this User
|
|
||||||
uid: The uid (login) for the unix user
|
|
||||||
uidNumber: Linux uid number
|
|
||||||
gid: The default gid number for this user
|
|
||||||
sn: The user "str" pseudo
|
|
||||||
login_shell: Linux shell for the user
|
|
||||||
mail: Email address contact for this user
|
|
||||||
display_name: Pretty display name for this user
|
|
||||||
dialupAccess: Boolean, True for valid membership
|
|
||||||
sambaSID: Identical id as uidNumber
|
|
||||||
user_password: SSHA hashed password of user
|
|
||||||
samba_nt_password: NTLM hashed password of user
|
|
||||||
macs: Multivalued mac address
|
|
||||||
shadowexpire: Set it to 0 to block access for this user and disabled
|
|
||||||
account
|
|
||||||
"""
|
|
||||||
|
|
||||||
# LDAP meta-data
|
|
||||||
base_dn = LDAP["base_user_dn"]
|
|
||||||
object_classes = [
|
|
||||||
"inetOrgPerson",
|
|
||||||
"top",
|
|
||||||
"posixAccount",
|
|
||||||
"sambaSamAccount",
|
|
||||||
"radiusprofile",
|
|
||||||
"shadowAccount",
|
|
||||||
]
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
login_shell = ldapdb.models.fields.CharField(
|
|
||||||
db_column="loginShell", max_length=200, blank=True, null=True
|
|
||||||
)
|
|
||||||
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, blank=True, null=True
|
|
||||||
)
|
|
||||||
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
|
|
||||||
)
|
|
||||||
shadowexpire = ldapdb.models.fields.CharField(
|
|
||||||
db_column="shadowExpire", 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):
|
|
||||||
"""A class representing a LdapUserGroup in LDAP, its LDAP conterpart.
|
|
||||||
Synced from UserGroup, (ListRight/Group django models),
|
|
||||||
with a copy of its attributes/fields into LDAP, so this class is a mirror
|
|
||||||
of the classic django ListRight model.
|
|
||||||
|
|
||||||
The basedn usergroupdn is specified in settings.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
name: The name of this LdapUserGroup
|
|
||||||
gid: The gid number for this unix group
|
|
||||||
members: Users dn members of this LdapUserGroup
|
|
||||||
"""
|
|
||||||
|
|
||||||
# LDAP meta-data
|
|
||||||
base_dn = LDAP["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 LdapServiceUser(ldapdb.models.Model):
|
|
||||||
"""A class representing a ServiceUser in LDAP, its LDAP conterpart.
|
|
||||||
Synced from ServiceUser, with a copy of its attributes/fields into LDAP,
|
|
||||||
so this class is a mirror of the classic django ServiceUser model.
|
|
||||||
|
|
||||||
The basedn userservicedn is specified in settings.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
name: The name of this ServiceUser
|
|
||||||
user_password: The SSHA hashed password of this ServiceUser
|
|
||||||
"""
|
|
||||||
|
|
||||||
# LDAP meta-data
|
|
||||||
base_dn = LDAP["base_userservice_dn"]
|
|
||||||
object_classes = ["applicationProcess", "simpleSecurityObject"]
|
|
||||||
|
|
||||||
# attributes
|
|
||||||
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):
|
|
||||||
"""A class representing a ServiceUserGroup in LDAP, its LDAP conterpart.
|
|
||||||
Synced from ServiceUserGroup, with a copy of its attributes/fields into LDAP,
|
|
||||||
so this class is a mirror of the classic django ServiceUserGroup model.
|
|
||||||
|
|
||||||
The basedn userservicegroupdn is specified in settings.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
name: The name of this ServiceUserGroup
|
|
||||||
members: ServiceUsers dn members of this ServiceUserGroup
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
33
users/signals.py
Normal file
33
users/signals.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
A set of signals used by users. Various classes in users emit these signals to signal the need to sync or
|
||||||
|
remove an object from optionnal authentication backends, e.g. LDAP.
|
||||||
|
|
||||||
|
* `users.signals.synchronise`:
|
||||||
|
Expresses the need for an instance of a users class to be synchronised. `sender` and `instance` are
|
||||||
|
always set. It is up to the receiver to ensure the others are set correctly if they make sense.
|
||||||
|
Arguments:
|
||||||
|
* `sender` : The model class.
|
||||||
|
* `instance` : The actual instance being synchronised.
|
||||||
|
* `base` : Default `True`. When `True`, synchronise basic attributes.
|
||||||
|
* `access_refresh` : Default `True`. When `True`, synchronise the access time.
|
||||||
|
* `mac_refresh` : Default `True`. When True, synchronise the list of mac addresses.
|
||||||
|
* `group_refresh`: Default `False`. When `True` synchronise the groups of the instance.
|
||||||
|
* `users.signals.remove`:
|
||||||
|
Expresses the need for an instance of a users class to be removed.
|
||||||
|
Arguments:
|
||||||
|
* `sender` : The model class.
|
||||||
|
* `instance` : The actual instance being removed.
|
||||||
|
* `users.signals.remove_mass`:
|
||||||
|
Same as `users.signals.remove` except it removes a queryset. For now it is only used by `users.models.User`.
|
||||||
|
Arguments:
|
||||||
|
* `sender` : The model class.
|
||||||
|
* `queryset` : The actual instances being removed.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import django.dispatch
|
||||||
|
|
||||||
|
synchronise = django.dispatch.Signal(providing_args=["sender", "instance", "base", "access_refresh", "mac_refresh", "group_refresh"])
|
||||||
|
remove = django.dispatch.Signal(providing_args=["sender", "instance"])
|
||||||
|
remove_mass = django.dispatch.Signal(providing_args=["sender", "queryset"])
|
||||||
|
|
Loading…
Reference in a new issue