Parcourir la source

Member save method don't save in database if LDAP fail
Write test for this behavior

Fabs il y a 11 ans
Parent
commit
2a9a5e9077
2 fichiers modifiés avec 84 ajouts et 60 suppressions
  1. 64 56
      coin/members/models.py
  2. 20 4
      coin/members/tests.py

+ 64 - 56
coin/members/models.py

@@ -8,6 +8,7 @@ import unicodedata
 import string
 import string
 import datetime
 import datetime
 from django.db import models
 from django.db import models
+from django.db import transaction
 from django.db.models import Q
 from django.db.models import Q
 from django.db.models.signals import post_save, pre_save, post_delete
 from django.db.models.signals import post_save, pre_save, post_delete
 from django.dispatch import receiver
 from django.dispatch import receiver
@@ -84,20 +85,74 @@ class Member(models.Model):
             Q(subscription_date__lte=date),
             Q(subscription_date__lte=date),
             Q(resign_date__isnull=True) | Q(resign_date__gte=date))
             Q(resign_date__isnull=True) | Q(resign_date__gte=date))
 
 
+    def get_automatic_ldap_cn(self):
+        """
+        Calcul le login / ldap_cn automatiquement en fonction du nom et du prénom
+        """
+        # Première lettre de chaque partie du prénom
+        first_name_letters = ''.join(
+            [c[0] for c in self.first_name.split('-')]
+        )
+        # Concaténer avec nom de famille
+        ldap_cn = ('%s%s' % (first_name_letters, self.last_name))
+        # Remplacer ou enlever les caractères non ascii
+        ldap_cn = unicodedata.normalize('NFD', ldap_cn)\
+            .encode('ascii', 'ignore')
+        # Enlever ponctuation et espace
+        ldap_cn = ldap_cn.translate(None, string.punctuation + ' ')
+        # En minuscule
+        ldap_cn = ldap_cn.lower()
+
+        return ldap_cn
+
+    def sync_with_ldap(self, creation):
+        """
+        Update LDAP data when a member is saved
+        """
+        if not creation:
+            ldap_user = LdapUser.objects.get(pk=self.ldap_cn)
+
+        if creation:
+            max_uidNumber = LdapUser.objects.order_by('-uidNumber')[0].uidNumber
+
+            ldap_user = LdapUser()
+            ldap_user.pk = self.ldap_cn
+            ldap_user.uid = self.ldap_cn
+            ldap_user.nick_name = self.ldap_cn
+            ldap_user.uidNumber = max_uidNumber + 1
+
+        ldap_user.last_name = self.last_name
+        ldap_user.first_name = self.first_name
+        ldap_user.save()
+
+        if creation:
+            ldap_group = LdapGroup.objects.get(pk='coin')
+            ldap_group.members.append(ldap_user.pk)
+            ldap_group.save()
+
+    @transaction.commit_manually
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
-        create = (self.pk == None)
+        # Détermine si on est dans une création ou une sauvegarde
+        creation = (self.pk == None)
 
 
-        # If not ldap_cn defined and creation
-        if not self.ldap_cn and create:
-            self.ldap_cn = get_ldap_cn(self.first_name, self.last_name)
+        # Si pas de ldap_cn défint et création de l'objet, alors génère un
+        # ldap_cn depuis le nom et prénom
+        if not self.ldap_cn and creation:
+            self.ldap_cn = self.get_automatic_ldap_cn()
 
 
-        # Save in ldap and in database
+        # Sauvegarde en base de donnée (mais sans commit, cf decorator)
+        super(Member, self).save(*args, **kwargs)
+
+        # Sauvegarde dans le LDAP
+        # Si la sauvegarde LDAP échoue, Rollback la sauvegarde en base, sinon
+        # commit
         try:
         try:
-            sync_member_with_ldap_user(self, create)
-            super(Member, self).save(*args, **kwargs)
+            self.sync_with_ldap(creation)
         except:
         except:
-            pass
-
+            transaction.rollback()
+            raise
+        else:
+            transaction.commit()
 
 
     class Meta:
     class Meta:
         verbose_name = 'membre'
         verbose_name = 'membre'
@@ -217,53 +272,6 @@ def define_display_name(sender, instance, **kwargs):
                                            instance.last_name)
                                            instance.last_name)
 
 
 
 
-def get_ldap_cn(first_name, last_name):
-    """
-    Calcul le login / ldap_cn automatiquement en fonction du nom et du prénom
-    """
-    # Première lettre de chaque partie du prénom
-    first_name_letters = ''.join(
-        [c[0] for c in first_name.split('-')]
-    )
-    # Concaténer avec nom de famille
-    ldap_cn = ('%s%s' % (first_name_letters, last_name))
-    # Remplacer ou enlever les caractères non ascii
-    ldap_cn = unicodedata.normalize('NFD', ldap_cn)\
-        .encode('ascii', 'ignore')
-    # Enlever ponctuation et espace
-    ldap_cn = ldap_cn.translate(None, string.punctuation + ' ')
-    # En minuscule
-    ldap_cn = ldap_cn.lower()
-
-    return ldap_cn
-
-
-def sync_member_with_ldap_user(member, created):
-    """
-    Update LDAP data when a member is saved
-    """
-    if not created:
-        ldap_user = LdapUser.objects.get(pk=member.ldap_cn)
-
-    if created:
-        max_uidNumber = LdapUser.objects.order_by('-uidNumber')[0].uidNumber
-
-        ldap_user = LdapUser()
-        ldap_user.pk = member.ldap_cn
-        ldap_user.uid = member.ldap_cn
-        ldap_user.nick_name = member.ldap_cn
-        ldap_user.uidNumber = max_uidNumber + 1
-
-    ldap_user.last_name = member.last_name
-    ldap_user.first_name = member.first_name
-    ldap_user.save()
-
-    if created:
-        ldap_group = LdapGroup.objects.get(pk='coin')
-        ldap_group.members.append(ldap_user.pk)
-        ldap_group.save()
-
-
 @receiver(post_delete, sender=Member)
 @receiver(post_delete, sender=Member)
 def remove_ldap_user_from_coin_group_when_deleting_member(sender,
 def remove_ldap_user_from_coin_group_when_deleting_member(sender,
                                                                  instance,
                                                                  instance,

+ 20 - 4
coin/members/tests.py

@@ -1,9 +1,12 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 import os
 import os
+from django import db
 from django.test import TestCase, Client
 from django.test import TestCase, Client
 from django.contrib.auth.models import User
 from django.contrib.auth.models import User
 from coin.members.models import Member, LdapUser, LdapGroup
 from coin.members.models import Member, LdapUser, LdapGroup
 import logging
 import logging
+import ldapdb
+from pprint import pprint
 
 
 
 
 class MemberTests(TestCase):
 class MemberTests(TestCase):
@@ -159,14 +162,27 @@ class MemberTests(TestCase):
     def test_when_saving_member_and_ldap_fail_dont_save(self):
     def test_when_saving_member_and_ldap_fail_dont_save(self):
         """
         """
         Test que lors de la sauvegarde d'un membre et que la sauvegarde en LDAP
         Test que lors de la sauvegarde d'un membre et que la sauvegarde en LDAP
-        échoue, rien n'est sauvegardé en base
+        échoue (ici mauvais mot de passe), rien n'est sauvegardé en base
         """
         """
-        first_name = u'Gérard'
-        last_name = u'Majax'
+        # Fait échouer le LDAP en définissant un mauvais mot de passe
+        for dbconnection in db.connections.all():
+            if (type(dbconnection) is
+                ldapdb.backends.ldap.base.DatabaseWrapper):
+                dbconnection.settings_dict['PASSWORD'] = 'wrong password test'
+
+        # Créé un membre
+        first_name = u'Du'
+        last_name = u'Pont'
         ldap_cn = MemberTestsUtils.get_random_ldap_cn()
         ldap_cn = MemberTestsUtils.get_random_ldap_cn()
         member = Member(first_name = first_name, last_name = last_name, ldap_cn = ldap_cn)
         member = Member(first_name = first_name, last_name = last_name, ldap_cn = ldap_cn)
-        member.save()
         
         
+        # Le sauvegarde en base de donnée
+        # Le save devrait renvoyer une exception parceque le LDAP échoue
+        self.assertRaises(Exception, member.save)
+        
+        # On s'assure, malgré l'exception, que le membre n'est pas en base
+        with self.assertRaises(Member.DoesNotExist):
+            Member.objects.get(ldap_cn=ldap_cn)
 
 
 
 
 class MemberAdminTests(TestCase):
 class MemberAdminTests(TestCase):