mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-09-10 08:13:12 +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", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("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 © 2021 Hugo Levy-Falk
|
||||
#
|
||||
# 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
|
||||
|
@ -21,6 +22,7 @@ from django.core.management.base import BaseCommand, CommandError
|
|||
from django.conf import settings
|
||||
|
||||
from users.models import User, ListRight
|
||||
from ldap_sync.models import synchronise_user, synchronise_serviceuser, synchronise_usergroup
|
||||
|
||||
|
||||
def split_lines(lines):
|
||||
|
@ -89,9 +91,9 @@ def flush_ldap(binddn, bindpass, server, usersdn, groupsdn):
|
|||
def sync_ldap():
|
||||
"""Syncrhonize the whole LDAP with the DB."""
|
||||
for u in User.objects.all():
|
||||
u.ldap_sync()
|
||||
synchronise_user(sender=User, instance=u)
|
||||
for lr in ListRight.objects.all():
|
||||
lr.ldap_sync()
|
||||
synchronise_usergroup(sender=ListRight, instance=lr)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Lara Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
# Copyright © 2020 Hugo Levy-Falk
|
||||
#
|
||||
# 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
|
||||
|
@ -19,6 +20,7 @@
|
|||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from users.models import User
|
||||
from ldap_sync.models import synchronise_user
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
@ -36,5 +38,5 @@ class Command(BaseCommand):
|
|||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
for usr in User.objects.all():
|
||||
usr.ldap_sync(mac_refresh=options["full"])
|
||||
for user in User.objects.all():
|
||||
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", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -106,6 +106,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -116,6 +116,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -79,12 +79,12 @@ def context_optionnal_apps(request):
|
|||
optionnal_templates_navbar_user_list = [
|
||||
app.views.navbar_user()
|
||||
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 = [
|
||||
app.views.navbar_logout()
|
||||
for app in optionnal_apps
|
||||
if hasattr(app.views, "navbar_logout")
|
||||
if hasattr(app, "views") and hasattr(app.views, "navbar_logout")
|
||||
]
|
||||
return {
|
||||
"optionnal_templates_navbar_user_list": optionnal_templates_navbar_user_list,
|
||||
|
|
|
@ -111,6 +111,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -112,6 +112,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -46,10 +46,6 @@ from .models import (
|
|||
Ban,
|
||||
Whitelist,
|
||||
Request,
|
||||
LdapUser,
|
||||
LdapServiceUser,
|
||||
LdapServiceUserGroup,
|
||||
LdapUserGroup,
|
||||
)
|
||||
from .forms import (
|
||||
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):
|
||||
"""School Admin view and management.
|
||||
|
||||
|
@ -338,10 +283,6 @@ class ServiceUserAdmin(VersionAdmin, BaseUserAdmin):
|
|||
admin.site.register(Adherent, AdherentAdmin)
|
||||
admin.site.register(Club, ClubAdmin)
|
||||
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(ListRight, ListRightAdmin)
|
||||
admin.site.register(ListShell, ListShellAdmin)
|
||||
|
|
|
@ -113,6 +113,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("cotisations", "0003_auto_20160702_1448"),
|
||||
|
|
|
@ -114,6 +114,7 @@ class Migration(migrations.Migration):
|
|||
("users", "0093_user_profile_image"),
|
||||
("users", "0094_remove_user_profile_image"),
|
||||
("users", "0095_user_theme"),
|
||||
("users", "0096_auto_20210110_1811"),
|
||||
("cotisations", "0001_initial"),
|
||||
("cotisations", "0002_remove_facture_article"),
|
||||
("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)
|
||||
* Rights (Groups and ListRight)
|
||||
* 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
|
||||
|
||||
import ldapdb.models
|
||||
import ldapdb.models.fields
|
||||
|
||||
from re2o.settings import LDAP, GID_RANGES, UID_RANGES
|
||||
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 OptionalMachine, MailMessageOption
|
||||
|
||||
from users import signals
|
||||
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
import sys
|
||||
|
@ -1042,7 +1034,7 @@ class User(
|
|||
"""
|
||||
cls.mass_disable_email(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):
|
||||
"""Method, archive user by unassigning ips.
|
||||
|
@ -1072,7 +1064,7 @@ class User(
|
|||
|
||||
def full_archive(self):
|
||||
"""Method, full archive an user by unassigning ips, deleting data
|
||||
and ldap deletion.
|
||||
and authentication deletion.
|
||||
|
||||
Parameters:
|
||||
self (user instance): user to full archive.
|
||||
|
@ -1080,7 +1072,7 @@ class User(
|
|||
"""
|
||||
self.archive()
|
||||
self.delete_data()
|
||||
self.ldap_del()
|
||||
signals.remove.send(sender=User, instance=self)
|
||||
|
||||
@classmethod
|
||||
def mass_full_archive(cls, users_list):
|
||||
|
@ -1102,14 +1094,14 @@ class User(
|
|||
|
||||
def unarchive(self):
|
||||
"""Method, unarchive an user by assigning ips, and recreating
|
||||
ldap user associated.
|
||||
authentication user associated.
|
||||
|
||||
Parameters:
|
||||
self (user instance): user to unarchive.
|
||||
|
||||
"""
|
||||
self.assign_ips()
|
||||
self.ldap_sync()
|
||||
signals.synchronise.send(sender=self.__class__, instance=self)
|
||||
|
||||
def state_sync(self):
|
||||
"""Master Method, call unarchive, full_archive or archive method
|
||||
|
@ -1135,109 +1127,6 @@ class User(
|
|||
):
|
||||
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 ######
|
||||
|
||||
def notif_inscription(self, request=None):
|
||||
|
@ -2195,7 +2084,7 @@ class Club(User):
|
|||
@receiver(post_save, sender=User)
|
||||
def user_post_save(**kwargs):
|
||||
"""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)
|
||||
|
||||
"""
|
||||
|
@ -2207,8 +2096,7 @@ def user_post_save(**kwargs):
|
|||
user.notif_inscription(user.request)
|
||||
user.set_active()
|
||||
user.state_sync()
|
||||
user.ldap_sync(
|
||||
base=True, access_refresh=True, mac_refresh=False, group_refresh=True
|
||||
signals.synchronise.send(sender=User, instance=user, base=True, access_refresh=True, mac_refresh=False, group_refresh=True
|
||||
)
|
||||
regen("mailing")
|
||||
|
||||
|
@ -2216,14 +2104,13 @@ def user_post_save(**kwargs):
|
|||
@receiver(m2m_changed, sender=User.groups.through)
|
||||
def user_group_relation_changed(**kwargs):
|
||||
"""Django signal, used for User Groups change (related models).
|
||||
Sync ldap, with calling group_refresh.
|
||||
Sync authentication, with calling group_refresh.
|
||||
|
||||
"""
|
||||
action = kwargs["action"]
|
||||
if action in ("post_add", "post_remove", "post_clear"):
|
||||
user = kwargs["instance"]
|
||||
user.ldap_sync(
|
||||
base=False, access_refresh=False, mac_refresh=False, group_refresh=True
|
||||
signals.synchronise.send(sender=User, instance=user, 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)
|
||||
def user_post_delete(**kwargs):
|
||||
"""Django signal, post delete operations on Adherent, Club and User.
|
||||
Delete user in ldap.
|
||||
Delete user in authentication.
|
||||
|
||||
"""
|
||||
user = kwargs["instance"]
|
||||
user.ldap_del()
|
||||
signals.remove.send(sender=User, instance=user)
|
||||
regen("mailing")
|
||||
|
||||
|
||||
class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||
"""A class representing a serviceuser (it is considered as a user
|
||||
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.
|
||||
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.
|
||||
|
||||
Attributes:
|
||||
|
@ -2293,65 +2180,6 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
|||
"""
|
||||
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):
|
||||
return self.pseudo
|
||||
|
||||
|
@ -2359,21 +2187,21 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
|||
@receiver(post_save, sender=ServiceUser)
|
||||
def service_user_post_save(**kwargs):
|
||||
"""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.ldap_sync()
|
||||
signals.synchronise.send(sender=ServiceUser, instance=service_user)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=ServiceUser)
|
||||
def service_user_post_delete(**kwargs):
|
||||
"""Django signal, post delete operations on ServiceUser.
|
||||
Delete service user in ldap.
|
||||
Delete service user in authentication.
|
||||
|
||||
"""
|
||||
service_user = kwargs["instance"]
|
||||
service_user.ldap_del()
|
||||
signals.remove.send(sender=ServiceUser, instance=service_user)
|
||||
|
||||
|
||||
class School(RevMixin, AclMixin, models.Model):
|
||||
|
@ -2448,58 +2276,25 @@ class ListRight(RevMixin, AclMixin, Group):
|
|||
def __str__(self):
|
||||
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)
|
||||
def listright_post_save(**kwargs):
|
||||
"""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.ldap_sync()
|
||||
signals.synchronise.send(sender=ListRight, instance=right)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=ListRight)
|
||||
def listright_post_delete(**kwargs):
|
||||
"""Django signal, post delete operations on ListRight/Group objects.
|
||||
Delete group in ldap.
|
||||
Delete group in authentication.
|
||||
|
||||
"""
|
||||
right = kwargs["instance"]
|
||||
right.ldap_del()
|
||||
signals.remove.send(sender=ListRight, instance=right)
|
||||
|
||||
|
||||
class ListShell(RevMixin, AclMixin, models.Model):
|
||||
|
@ -2649,13 +2444,13 @@ class Ban(RevMixin, AclMixin, models.Model):
|
|||
@receiver(post_save, sender=Ban)
|
||||
def ban_post_save(**kwargs):
|
||||
"""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"]
|
||||
is_created = kwargs["created"]
|
||||
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")
|
||||
if is_created:
|
||||
ban.notif_ban(ban.request)
|
||||
|
@ -2669,11 +2464,11 @@ def ban_post_save(**kwargs):
|
|||
@receiver(post_delete, sender=Ban)
|
||||
def ban_post_delete(**kwargs):
|
||||
"""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.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("dhcp")
|
||||
regen("mac_ip_list")
|
||||
|
@ -2740,12 +2535,12 @@ class Whitelist(RevMixin, AclMixin, models.Model):
|
|||
@receiver(post_save, sender=Whitelist)
|
||||
def whitelist_post_save(**kwargs):
|
||||
"""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"]
|
||||
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"]
|
||||
regen("mailing")
|
||||
if is_created:
|
||||
|
@ -2759,11 +2554,11 @@ def whitelist_post_save(**kwargs):
|
|||
@receiver(post_delete, sender=Whitelist)
|
||||
def whitelist_post_delete(**kwargs):
|
||||
"""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.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("dhcp")
|
||||
regen("mac_ip_list")
|
||||
|
@ -2996,171 +2791,3 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
|
|||
raise ValidationError(reason)
|
||||
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