Repository: zestedesavoir/zds-site

Issue:
Inscription, sécurité mdp
Manque de sécu sur le mot de passe : on peut mettre son login, pas de limite min de taille apparemment, et on n'est pas obligé de mixer plusieurs types parmi minuscules, majuscules, chiffres et caractères spéciaux



Discussion:
J'ai horreur des sites qui me forcent des restrictions sur le mot de passe
que je veux mettre.

2014-05-16 15:35 GMT+01:00 Eskimon notifications@github.com:

> Manque de sécu sur le mot de passe : on peut mettre son login, pas de
> limite min de taille apparemment, et on n'est pas obligé de mixer plusieurs
> types parmi minuscules, majuscules, chiffres et caractères spéciaux
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/Taluu/ZesteDeSavoir/issues/541
> .

Je ne fais que rapporter les propos de mascotte sur ce point... Mais c'est vrai que pas bridé au moins la taille et la copie du pseudo lui-même c'est un peu juste non ? 

Moi je suis partisans de laisser l'utilisateur mettre ce qu'il veut. On
peut effectivement limiter la "taille" du mdp et vérifier si mdp != pseudo,
apres le reste je pense surtout que c'est pas notre "business"; si le
membre veut mettre "password" comme mot de passe, c'est son problème. Ya
pas vraiment de données importantes a securiser deriere ce mot de passe,
donc forcer des P@sS\/\/0rDs du gerne j'aime pas l’idée.

2014-05-16 15:44 GMT+01:00 Eskimon notifications@github.com:

> Je ne fais que rapporter les propos de mascotte sur ce point... Mais c'est
> vrai que pas bridé au moins la taille et la copie du pseudo lui-même c'est
> un peu juste non ?
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/Taluu/ZesteDeSavoir/issues/541#issuecomment-43339358
> .

Tu rigoles mais en ce moment mon mot de passe au boulot est : p@Ss2014

Au boulot, chez nous sur les réseaux internes la quasi totalité des mots de passe c'est root/root, admin/p@ssw0rd et compagnie...

Voici la branche qui va bien https://github.com/Taluu/ZesteDeSavoir/tree/fix_541

J'ai presqur fini (en bridant simplement la longeur du mot de passe a 6 car. et que le mot de passe soit différent du pseudo).
Il reste un truc que je vois pas comment faire...
Lorsque l'utilisateur veut réinitialiser son mot de passe, il est redirigé vers une page contenant les deux champs. Ca ca va. Par contre dans le formulaire on a pas l'utilisateur et je vois pas comment aller le chercher (pour véirfier mdp != pseudo)...
Si quelqu'un sait comment faire, qu'il le fasse et la pull request sera prête...

@Eskimon: Ton lien est mort (404). La branche a été mergée et l'issue résolue ?

Erf faudra voir avec @firm1 qui a fait un gros refactoring dans l'apres midi il me semble :s

Je pense qu'il faudra que tu repousses ta branche après le feu vert de @firm1 (il y a un soucis sur l'historique du projet et des bugs apparus suite à l'allègement du dépôt).

Erf ca ca tombe mal car j'ai viré pour resynchro endébut de soirée comme demandé par firm1... BOn ca va qu'il y avait pas trop de changement...

J'en suis tristement désolé, j'ai du retravailler sur le nettoyage du dépot. C'est vraiment dommage que ta branche soit perdue.

Pourrais-tu nous refaire une correction ?

Ouai c'est pas un probleme c'etait pas trop dur non plus donc ca ira ^^ (par contre il restera toujours le point "Comment recuperer l'utilisateur sur la page de reinit. du mot de passe)

Bon voila, j'ai repoussé [une branche](https://github.com/Taluu/ZesteDeSavoir/tree/fix_541) avec le début du fix...
Il reste une seule chose à faire, faire en sorte que le mdp soit différent du pseudo lors de l'action "mot de passe oublié". C'est le dernier test, écris en commentaire à la fin du code (le hasard fait bien les choses c'est à la fin du fichier uniquement modifié dans les commits). Le code marchera quand il y aura une variable "username" de peupler avec le pseudo.

J'ai fait en sorte que la taille minimum du mdp soit reglable via une simple constante au début du fichier (et c'est cette constante qui s'affiche dans les chaines de caractères d'avertissements).

Bref, plus qu'une bricole à faire et le code sera prêt pour une PR.

Bon bah j'ai complété et mergé tes commits. 

N'hésite pas a aller voir comment j'ai fait, c'est typiquement le genre de chose qu'il faudrait savoir faire pour d'autres issues.

PS : bon boulot

> PS : bon boulot

Merci :)

> N'hésite pas a aller voir comment j'ai fait, c'est typiquement le genre de chose qu'il faudrait savoir faire pour d'autres issues.

J'y compte bien !!


Resolving patch:
diff --git a/zds/gallery/models.py b/zds/gallery/models.py
--- a/zds/gallery/models.py
+++ b/zds/gallery/models.py
@@ -114,7 +114,7 @@ def get_absolute_url(self):
         return '{0}/{1}'.format(settings.MEDIA_URL, self.physical)
 
     def get_extension(self):
-        return os.path.splitext(self.nom_physique)[1]
+        return os.path.splitext(self.physical.name)[1][1:]
 
     def save(
         self,
@@ -186,6 +186,12 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
     if instance.physical:
         if os.path.isfile(instance.physical.path):
             os.remove(instance.physical.path)
+    if instance.medium:
+        if os.path.isfile(instance.medium.path):
+            os.remove(instance.medium.path)
+    if instance.thumb:
+        if os.path.isfile(instance.thumb.path):
+            os.remove(instance.thumb.path)
 
 
 class Gallery(models.Model):
@@ -209,6 +215,7 @@ def get_absolute_url(self):
         return reverse('zds.gallery.views.gallery_details',
                        args=[self.pk, self.slug])
 
+    # TODO rename function to get_users_galleries
     def get_users(self):
         return UserGallery.objects.all()\
             .filter(gallery=self)
diff --git a/zds/gallery/views.py b/zds/gallery/views.py
--- a/zds/gallery/views.py
+++ b/zds/gallery/views.py
@@ -156,17 +156,6 @@ def modify_gallery(request):
     return redirect(gallery.get_absolute_url())
 
 
-@can_write_and_read_now
-@login_required
-def del_image(request, gal_pk):
-    gal = get_object_or_404(Gallery, pk=gal_pk)
-    if request.method == "POST":
-        liste = request.POST.getlist("items")
-        Image.objects.filter(pk__in=liste).delete()
-        return redirect(gal.get_absolute_url())
-    return redirect(gal.get_absolute_url())
-
-
 @can_write_and_read_now
 @login_required
 def edit_image(request, gal_pk, img_pk):
@@ -174,6 +163,19 @@ def edit_image(request, gal_pk, img_pk):
 
     gal = get_object_or_404(Gallery, pk=gal_pk)
     img = get_object_or_404(Image, pk=img_pk)
+
+    # check if user can edit image
+    try:
+        permission = UserGallery.objects.get(user=request.user, gallery=gal)
+        if permission.mode != 'W':
+            raise PermissionDenied
+    except:
+        raise PermissionDenied
+
+    # check if the image belong to the gallery
+    if img.gallery != gal:
+        raise PermissionDenied
+
     if request.method == "POST":
         form = ImageForm(request.POST, request.FILES)
         if form.is_valid() and request.FILES["physical"].size < settings.IMAGE_MAX_SIZE:
@@ -221,11 +223,14 @@ def modify_image(request):
     except:
         raise PermissionDenied
     if "delete" in request.POST:
-        img = get_object_or_404(Image, pk=request.POST["image"])
-        img.delete()
+        try:
+            img = Image.objects.get(pk=request.POST["image"], gallery=gal)
+            img.delete()
+        except:
+            pass
     elif "delete_multi" in request.POST:
         l = request.POST.getlist("items")
-        Image.objects.filter(pk__in=l).delete()
+        Image.objects.filter(pk__in=l, gallery=gal).delete()
     return redirect(gal.get_absolute_url())
 
 
@@ -235,6 +240,15 @@ def new_image(request, gal_pk):
     """Creates a new image."""
 
     gal = get_object_or_404(Gallery, pk=gal_pk)
+
+    # check if the user can upload new image in this gallery
+    try:
+        gal_mode = UserGallery.objects.get(gallery=gal, user=request.user)
+        if gal_mode.mode != 'W':
+            raise PermissionDenied
+    except:
+        raise PermissionDenied
+
     if request.method == "POST":
         form = ImageForm(request.POST, request.FILES)
         if form.is_valid() and request.FILES["physical"].size \
@@ -258,4 +272,4 @@ def new_image(request, gal_pk):
     else:
         form = ImageForm()  # A empty, unbound form
         return render_template("gallery/image/new.html", {"form": form,
-                                                          "gallery": gal})
\ No newline at end of file
+                                                          "gallery": gal})
diff --git a/zds/member/forms.py b/zds/member/forms.py
--- a/zds/member/forms.py
+++ b/zds/member/forms.py
@@ -102,6 +102,7 @@ class RegisterForm(forms.Form):
     password = forms.CharField(
         label='Mot de passe',
         max_length=MAX_PASSWORD_LENGTH,
+        min_length=MIN_PASSWORD_LENGTH,
         required=True,
         widget=forms.PasswordInput
     )
@@ -109,6 +110,7 @@ class RegisterForm(forms.Form):
     password_confirm = forms.CharField(
         label='Confirmation du mot de passe',
         max_length=MAX_PASSWORD_LENGTH,
+        min_length=MIN_PASSWORD_LENGTH,
         required=True,
         widget=forms.PasswordInput
     )
@@ -138,7 +140,7 @@ def clean(self):
 
         if not password_confirm == password:
             msg = u'Les mots de passe sont différents'
-            self._errors['password'] = self.error_class([''])
+            self._errors['password'] = self.error_class([msg])
             self._errors['password_confirm'] = self.error_class([msg])
 
             if 'password' in cleaned_data:
@@ -147,15 +149,6 @@ def clean(self):
             if 'password_confirm' in cleaned_data:
                 del cleaned_data['password_confirm']
 
-        # Check that the password is at least MIN_PASSWORD_LENGTH
-        if len(password) < MIN_PASSWORD_LENGTH:
-            msg = u'Le mot de passe doit faire au moins {0} caractères'.format(MIN_PASSWORD_LENGTH)
-            self._errors['password'] = self.error_class([msg])
-            if 'password' in cleaned_data:
-                del cleaned_data['password']
-            if 'password_confirm' in cleaned_data:
-                del cleaned_data['password_confirm']
-
         # Check that the user doesn't exist yet
         username = cleaned_data.get('username')
         if User.objects.filter(username=username).count() > 0:
@@ -172,14 +165,15 @@ def clean(self):
                 del cleaned_data['password_confirm']
 
         email = cleaned_data.get('email')
-        # Chech if email provider is authorized
-        with open(os.path.join(SITE_ROOT,
-                               'forbidden_email_providers.txt'), 'r') as fh:
-            for provider in fh:
-                if provider.strip() in email:
-                    msg = u'Utilisez un autre fournisseur d\'adresses mail.'
-                    self._errors['email'] = self.error_class([msg])
-                    break
+        if email:
+            # Chech if email provider is authorized
+            with open(os.path.join(SITE_ROOT,
+                                   'forbidden_email_providers.txt'), 'r') as fh:
+                for provider in fh:
+                    if provider.strip() in email:
+                        msg = u'Utilisez un autre fournisseur d\'adresses mail.'
+                        self._errors['email'] = self.error_class([msg])
+                        break
 
         # Check that the email is unique
         if User.objects.filter(email=email).count() > 0:
@@ -565,4 +559,4 @@ def clean(self):
             if 'password_confirm' in cleaned_data:
                 del cleaned_data['password_confirm']
 
-        return cleaned_data
\ No newline at end of file
+        return cleaned_data


Test patch:
diff --git a/zds/gallery/tests.py b/zds/gallery/tests.py
deleted file mode 100644
--- a/zds/gallery/tests.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# coding: utf-8
-
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.test import TestCase
-
-from zds.gallery.factories import ImageFactory, GalleryFactory, UserGalleryFactory
-from zds.gallery.models import Image, Gallery
-from zds.member.factories import ProfileFactory
-from zds.utils import slugify
-
-import os
-
-
-class GalleryTests(TestCase):
-
-    def setUp(self):
-
-        self.profile = ProfileFactory()
-
-        login_check = self.client.login(
-            username=self.profile.user.username,
-            password='hostel77')
-        self.assertEqual(login_check, True)
-
-    def test_create_gallery(self):
-        """To test user create gallery."""
-
-        self.assertEqual(Gallery.objects.count(), 0)
-
-        result = self.client.post(
-            reverse('zds.gallery.views.new_gallery'),
-            {'title': u"Ma galerie super trop cool",
-             'subtitle': u"La galerie de ma galerie est ma galerie"},
-            follow=True)
-        self.assertEqual(result.status_code, 200)
-
-        self.assertEqual(Gallery.objects.count(), 1)
-
-    def test_create_gallery_empty_title(self):
-        """To test user create gallery with an empty title."""
-
-        self.assertEqual(Gallery.objects.count(), 0)
-
-        result = self.client.post(
-            reverse('zds.gallery.views.new_gallery'),
-            {'subtitle': u"La galerie de ma galerie est ma galerie"},
-            follow=True)
-        self.assertEqual(result.status_code, 200)
-
-        self.assertEqual(Gallery.objects.count(), 0)
-
-    def test_url_gallery(self):
-        """To test url of gallery."""
-
-        gallery1 = GalleryFactory()
-        gallery2 = GalleryFactory()
-        GalleryFactory()
-        UserGalleryFactory(user=self.profile.user, gallery=gallery2)
-
-        result = self.client.get(
-            reverse(
-                'zds.gallery.views.gallery_details',
-                args=[
-                    gallery2.pk,
-                    slugify(gallery2.title)]),
-            follow=True)
-
-        self.assertEqual(result.status_code, 200)
-
-        result = self.client.get(
-            reverse(
-                'zds.gallery.views.gallery_details',
-                args=[
-                    gallery1.pk,
-                    slugify(gallery1.title)]),
-            follow=True)
-
-        self.assertEqual(result.status_code, 403)
-
-    def test_url_list_gallery(self):
-        """To test url list of galleries."""
-
-        GalleryFactory()
-        gallery2 = GalleryFactory()
-        GalleryFactory()
-        UserGalleryFactory(user=self.profile.user, gallery=gallery2)
-
-        result = self.client.get(
-            reverse(
-                'zds.gallery.views.gallery_list'),
-            follow=True)
-
-        self.assertEqual(result.status_code, 200)
-
-
-class ImageTests(TestCase):
-
-    def setUp(self):
-
-        self.profile = ProfileFactory()
-        self.gallery = GalleryFactory()
-        self.ug = UserGalleryFactory(user=self.profile.user, gallery=self.gallery)
-
-        login_check = self.client.login(
-            username=self.profile.user.username,
-            password='hostel77')
-        self.assertEqual(login_check, True)
-
-    def test_create_image(self):
-        """To test user create image in gallery."""
-
-        self.assertEqual(Image.objects.count(), 0)
-
-        result = self.client.post(
-            reverse('zds.gallery.views.new_image',
-                    args=[self.gallery.pk]),
-            {'title': u"Mon image super trop cool",
-             'legend': u"Légende de ma galerie",
-             'physical': open(
-                       os.path.join(
-                           settings.SITE_ROOT,
-                           'fixtures',
-                           'logo.png',
-                       ), 'r')},
-            follow=True)
-        self.assertEqual(result.status_code, 200)
-
-        self.assertEqual(Image.objects.count(), 1)
-
-    def test_url_image(self):
-        """To test url of image."""
-        image1 = ImageFactory(gallery=self.gallery)
-        result = self.client.get(
-            reverse(
-                'zds.gallery.views.edit_image',
-                args=[
-                    self.gallery.pk,
-                    image1.pk]),
-            follow=False)
-
-        self.assertEqual(result.status_code, 200)
-        self.assertEqual(Image.objects.count(), 1)
diff --git a/zds/gallery/tests/__init__.py b/zds/gallery/tests/__init__.py
new file mode 100644
diff --git a/zds/gallery/tests/tests_forms.py b/zds/gallery/tests/tests_forms.py
new file mode 100644
--- /dev/null
+++ b/zds/gallery/tests/tests_forms.py
@@ -0,0 +1,91 @@
+# coding: utf-8
+
+import os
+
+from django.test import TestCase
+from django.core.files.uploadedfile import SimpleUploadedFile
+
+from zds.gallery.forms import GalleryForm, UserGalleryForm, ImageForm, ImageAsAvatarForm
+from zds.member.factories import ProfileFactory
+from zds import settings
+
+
+class GalleryFormTest(TestCase):
+
+    def test_valid_gallery_form(self):
+        data = {
+            'title': 'Test Title',
+            'subtitle': 'Test Subtitle'
+        }
+        form = GalleryForm(data=data)
+
+        self.assertTrue(form.is_valid())
+
+    def test_invalid_gallery_form_empty_title(self):
+        """ Test when title contains only whitespace """
+        data = {
+            'title': ' ',
+            'subtitle': 'Test Subtitle'
+        }
+        form = GalleryForm(data=data)
+
+        self.assertFalse(form.is_valid())
+
+    def test_invalid_gallery_form_no_title(self):
+        data = {
+            'subtitle': 'Test Subtitle'
+        }
+        form = GalleryForm(data=data)
+
+        self.assertFalse(form.is_valid())
+
+
+class UserGalleryFormTest(TestCase):
+
+    def setUp(self):
+        self.profile = ProfileFactory()
+
+    def test_valid_user_gallery_form(self):
+        data = {
+            'user': self.profile,
+            'mode': 'R'
+        }
+        form = UserGalleryForm(data=data)
+
+        self.assertTrue(form.is_valid())
+
+    def test_invalid_user_gallery_form_noexist_user(self):
+        data = {
+            'user': 'hello',
+            'mode': 'W'
+        }
+        form = UserGalleryForm(data=data)
+
+        self.assertFalse(form.is_valid())
+
+
+class ImageFormTest(TestCase):
+
+    def test_valid_image_form(self):
+        upload_file = open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r')
+
+        data = {
+            'title': 'Test Image',
+            'legend': 'Test Legend',
+        }
+
+        files = {
+            'physical': SimpleUploadedFile(upload_file.name, upload_file.read())
+        }
+        form = ImageForm(data, files)
+
+        self.assertTrue(form.is_valid())
+        upload_file.close()
+
+
+class ImageAsAvatarFormTest(TestCase):
+
+    def test_image_as_avatar_form(self):
+        form = ImageAsAvatarForm()
+
+        self.assertFalse(form.is_bound)
diff --git a/zds/gallery/tests/tests_models.py b/zds/gallery/tests/tests_models.py
new file mode 100644
--- /dev/null
+++ b/zds/gallery/tests/tests_models.py
@@ -0,0 +1,124 @@
+# coding: utf-8
+
+import os
+
+from django.test import TestCase
+from django.core.urlresolvers import reverse
+
+from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory
+from zds.member.factories import ProfileFactory
+from zds import settings
+
+
+class UserGalleryTest(TestCase):
+
+    def setUp(self):
+        self.profile = ProfileFactory()
+        self.gallery = GalleryFactory()
+        self.image1 = ImageFactory(gallery=self.gallery)
+        self.image2 = ImageFactory(gallery=self.gallery)
+        self.user_gallery = UserGalleryFactory(user=self.profile.user, gallery=self.gallery)
+
+    def tearDown(self):
+        self.image1.delete()
+        self.image2.delete()
+        self.user_gallery.delete()
+        self.gallery.delete()
+
+    def test_unicode(self):
+        result = u'Galerie "{0}" envoye par {1}'.format(self.gallery, self.profile.user)
+
+        self.assertEqual(result, self.user_gallery.__unicode__())
+
+    def test_is_write(self):
+        self.user_gallery.mode = 'W'
+
+        self.assertTrue(self.user_gallery.is_write())
+        self.assertFalse(self.user_gallery.is_read())
+
+    def test_is_read(self):
+        self.user_gallery.mode = 'R'
+
+        self.assertFalse(self.user_gallery.is_write())
+        self.assertTrue(self.user_gallery.is_read())
+
+    def test_get_images(self):
+        self.assertEqual(2, len(self.user_gallery.get_images()))
+
+        self.assertEqual(self.image1, self.user_gallery.get_images()[0])
+        self.assertEqual(self.image2, self.user_gallery.get_images()[1])
+
+    def test_get_gallery(self):
+        gallery_results = self.user_gallery.get_gallery(self.profile.user)
+
+        self.assertEqual(1, len(gallery_results))
+        self.assertEqual(self.gallery, gallery_results[0])
+
+
+class ImageTest(TestCase):
+
+    def setUp(self):
+        self.gallery = GalleryFactory()
+        self.image = ImageFactory(gallery=self.gallery)
+
+    def tearDown(self):
+        self.image.delete()
+        self.gallery.delete()
+
+    def test_unicode(self):
+        self.assertEqual(self.image.slug, self.image.__unicode__())
+
+    def test_get_absolute_url(self):
+        absolute_url = u'{0}/{1}'.format(settings.MEDIA_URL, self.image.physical)
+
+        self.assertEqual(absolute_url, self.image.get_absolute_url())
+
+    def test_get_extension(self):
+        self.assertEqual('jpg', self.image.get_extension())
+
+    def test_save_image(self):
+        test_image = ImageFactory(gallery=self.gallery)
+        self.assertTrue(os.path.isfile(test_image.physical.path))
+        self.assertTrue(os.path.isfile(test_image.medium.path))
+        self.assertTrue(os.path.isfile(test_image.thumb.path))
+
+        test_image.delete()
+        self.assertFalse(os.path.isfile(test_image.physical.path))
+        self.assertFalse(os.path.isfile(test_image.medium.path))
+        self.assertFalse(os.path.isfile(test_image.thumb.path))
+
+
+class GalleryTest(TestCase):
+
+    def setUp(self):
+        self.profile = ProfileFactory()
+        self.gallery = GalleryFactory()
+        self.image1 = ImageFactory(gallery=self.gallery)
+        self.image2 = ImageFactory(gallery=self.gallery)
+        self.user_gallery = UserGalleryFactory(user=self.profile.user, gallery=self.gallery)
+
+    def tearDown(self):
+        self.image1.delete()
+        self.image2.delete()
+        self.user_gallery.delete()
+        self.gallery.delete()
+
+    def test_unicode(self):
+        self.assertEqual(self.gallery.title, self.gallery.__unicode__())
+
+    def test_get_absolute_url(self):
+        absolute_url = reverse('zds.gallery.views.gallery_details',
+                args=[self.gallery.pk, self.gallery.slug])
+        self.assertEqual(absolute_url, self.gallery.get_absolute_url())
+
+    def test_get_users(self):
+        self.assertEqual(1, len(self.gallery.get_users()))
+        self.assertEqual(self.user_gallery, self.gallery.get_users()[0])
+
+    def test_get_images(self):
+        self.assertEqual(2, len(self.gallery.get_images()))
+        self.assertEqual(self.image1, self.gallery.get_images()[0])
+        self.assertEqual(self.image2, self.gallery.get_images()[1])
+
+    def test_get_last_image(self):
+        self.assertEqual(self.image2, self.gallery.get_last_image())
diff --git a/zds/gallery/tests/tests_views.py b/zds/gallery/tests/tests_views.py
new file mode 100644
--- /dev/null
+++ b/zds/gallery/tests/tests_views.py
@@ -0,0 +1,618 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import urllib
+import os
+
+from django.test import TestCase
+from django.core.urlresolvers import reverse
+
+from zds.member.factories import ProfileFactory
+from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory
+from zds.gallery.models import Gallery, UserGallery, Image
+from zds import settings
+
+
+class GalleryListViewTest(TestCase):
+
+    def test_denies_anonymous(self):
+        response = self.client.get(reverse('zds.gallery.views.gallery_list'), follow=True)
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view')
+                + '?next=' + urllib.quote(reverse('zds.gallery.views.gallery_list'), ''))
+
+    def test_list_galeries_belong_to_member(self):
+        profile = ProfileFactory()
+        gallery = GalleryFactory()
+        GalleryFactory()
+        UserGalleryFactory(user=profile.user, gallery=gallery)
+
+        login_check = self.client.login(
+                username=profile.user.username,
+                password='hostel77'
+        )
+        self.assertTrue(login_check)
+
+        response = self.client.get(reverse('zds.gallery.views.gallery_list'), follow=True)
+        self.assertEqual(200, response.status_code)
+
+        self.assertEqual(1, len(response.context['galleries']))
+        self.assertEqual(UserGallery.objects.filter(user=profile.user)[0], response.context['galleries'][0])
+
+
+class GalleryDetailViewTest(TestCase):
+
+    def setUp(self):
+        self.profile1 = ProfileFactory()
+        self.profile2 = ProfileFactory()
+
+    def test_denies_anonymous(self):
+        response = self.client.get(reverse('zds.gallery.views.gallery_details',
+            args=['89', 'test-gallery']), follow=True)
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view')
+                + '?next=' + urllib.quote(reverse('zds.gallery.views.gallery_details',
+                    args=['89', 'test-gallery']), ''))
+
+    def test_fail_gallery_no_exist(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+        response = self.client.get(reverse('zds.gallery.views.gallery_details',
+            args=['89', 'test-gallery']), follow=True)
+
+        self.assertEqual(404, response.status_code)
+
+    def test_fail_gallery_details_no_permission(self):
+        """ fail when a user has no permission at all """
+        gallery = GalleryFactory()
+        UserGalleryFactory(gallery=gallery, user=self.profile1.user)
+
+        login_check = self.client.login(username=self.profile2.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.get(reverse('zds.gallery.views.gallery_details',
+            args=[gallery.pk, gallery.slug]))
+        self.assertEqual(403, response.status_code)
+
+    def test_success_gallery_details_permission_authorized(self):
+        gallery = GalleryFactory()
+        UserGalleryFactory(gallery=gallery, user=self.profile1.user)
+        UserGalleryFactory(gallery=gallery, user=self.profile2.user)
+
+        login_check = self.client.login(username=self.profile2.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.get(reverse('zds.gallery.views.gallery_details',
+            args=[gallery.pk, gallery.slug]))
+        self.assertEqual(200, response.status_code)
+
+
+class NewGalleryViewTest(TestCase):
+
+    def setUp(self):
+        self.profile1 = ProfileFactory()
+
+    def test_denies_anonymous(self):
+        response = self.client.get(reverse('zds.gallery.views.new_gallery'), follow=True)
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view') +
+                '?next=' +
+                urllib.quote(reverse('zds.gallery.views.new_gallery'), ''))
+
+        response = self.client.post(reverse('zds.gallery.views.new_gallery'), follow=True)
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view') +
+                '?next=' +
+                urllib.quote(reverse('zds.gallery.views.new_gallery'), ''))
+
+    def test_access_member(self):
+        """ just verify with get request that everythings is ok """
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.get(reverse('zds.gallery.views.new_gallery'))
+        self.assertEqual(200, response.status_code)
+
+    def test_fail_new_gallery_with_missing_params(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+        self.assertEqual(0, Gallery.objects.count())
+
+        response = self.client.post(reverse('zds.gallery.views.new_gallery'), {
+            'subtitle': 'test'})
+        self.assertEqual(200, response.status_code)
+        self.assertTrue(response.context['form'].errors)
+        self.assertEqual(0, Gallery.objects.count())
+
+    def test_success_new_gallery(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+        self.assertEqual(0, Gallery.objects.count())
+
+        response = self.client.post(reverse('zds.gallery.views.new_gallery'), {
+            'title': 'test title', 'subtitle': 'test subtitle'}, follow=True)
+
+        self.assertEqual(200, response.status_code)
+        self.assertEqual(1, Gallery.objects.count())
+
+        user_gallery = UserGallery.objects.filter(user=self.profile1.user)
+        self.assertEqual(1, user_gallery.count())
+        self.assertEqual('test title', user_gallery[0].gallery.title)
+        self.assertEqual('test subtitle', user_gallery[0].gallery.subtitle)
+        self.assertEqual('W', user_gallery[0].mode)
+
+
+class ModifyGalleryViewTest(TestCase):
+
+    def setUp(self):
+        self.profile1 = ProfileFactory()
+        self.profile2 = ProfileFactory()
+        self.profile3 = ProfileFactory()
+        self.gallery1 = GalleryFactory()
+        self.gallery2 = GalleryFactory()
+        self.image1 = ImageFactory(gallery=self.gallery1)
+        self.image2 = ImageFactory(gallery=self.gallery1)
+        self.image3 = ImageFactory(gallery=self.gallery2)
+        self.user_gallery1 = UserGalleryFactory(user=self.profile1.user, gallery=self.gallery1)
+        self.user_gallery2 = UserGalleryFactory(user=self.profile1.user, gallery=self.gallery2)
+        self.user_gallery3 = UserGalleryFactory(user=self.profile2.user, gallery=self.gallery1, mode='R')
+
+    def tearDown(self):
+        self.image1.delete()
+        self.image2.delete()
+        self.image3.delete()
+
+    def test_fail_delete_multi_read_permission(self):
+        """ when user wants to delete a list of galleries just with a read permission """
+        login_check = self.client.login(username=self.profile2.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        self.assertEqual(2, Gallery.objects.all().count())
+        self.assertEqual(3, UserGallery.objects.all().count())
+        self.assertEqual(3, Image.objects.all().count())
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'delete_multi': '',
+                    'items': [self.gallery1.pk]
+                },
+                follow=True
+        )
+        self.assertEqual(403, response.status_code)
+
+        self.assertEqual(2, Gallery.objects.all().count())
+        self.assertEqual(3, UserGallery.objects.all().count())
+        self.assertEqual(3, Image.objects.all().count())
+
+    def test_success_delete_multi_write_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        self.assertEqual(2, Gallery.objects.all().count())
+        self.assertEqual(3, UserGallery.objects.all().count())
+        self.assertEqual(3, Image.objects.all().count())
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'delete_multi': '',
+                    'items': [self.gallery1.pk, self.gallery2.pk]
+                },
+                follow=True
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertEqual(0, Gallery.objects.all().count())
+        self.assertEqual(0, UserGallery.objects.all().count())
+        self.assertEqual(0, Image.objects.all().count())
+
+    def test_fail_add_user_with_read_permission(self):
+        login_check = self.client.login(username=self.profile2.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        # gallery nonexistent
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'adduser': '',
+                    'gallery': 89,
+                }
+        )
+        self.assertEqual(404, response.status_code)
+
+        # try to add an user with write permission
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'adduser': '',
+                    'gallery': self.gallery1.pk,
+                    'user': self.profile2.user.username,
+                    'mode': 'W',
+                }
+        )
+        self.assertEqual(403, response.status_code)
+
+    def test_fail_add_user_already_has_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        # Same permission : read
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'adduser': '',
+                    'gallery': self.gallery1.pk,
+                    'user': self.profile2.user.username,
+                    'mode': 'R',
+                },
+                follow=True
+        )
+        self.assertEqual(200, response.status_code)
+        permissions = UserGallery.objects.filter(user=self.profile2.user, gallery=self.gallery1)
+        self.assertEqual(1, len(permissions))
+        self.assertEqual('R', permissions[0].mode)
+
+        # try to add write permission to an user
+        # who has already an read permission
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'adduser': '',
+                    'gallery': self.gallery1.pk,
+                    'user': self.profile2.user.username,
+                    'mode': 'W',
+                },
+                follow=True
+        )
+        self.assertEqual(200, response.status_code)
+        permissions = UserGallery.objects.filter(user=self.profile2.user, gallery=self.gallery1)
+        self.assertEqual(1, len(permissions))
+        self.assertEqual('R', permissions[0].mode)
+
+    def test_success_add_user_read_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'adduser': '',
+                    'gallery': self.gallery1.pk,
+                    'user': self.profile3.user.username,
+                    'mode': 'R',
+                },
+                follow=True
+        )
+        self.assertEqual(200, response.status_code)
+        permissions = UserGallery.objects.filter(user=self.profile3.user, gallery=self.gallery1)
+        self.assertEqual(1, len(permissions))
+        self.assertEqual('R', permissions[0].mode)
+
+    def test_success_add_user_write_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_gallery'),
+                {
+                    'adduser': '',
+                    'gallery': self.gallery1.pk,
+                    'user': self.profile3.user.username,
+                    'mode': 'W',
+                },
+                follow=True
+        )
+        self.assertEqual(200, response.status_code)
+        permissions = UserGallery.objects.filter(user=self.profile3.user, gallery=self.gallery1)
+        self.assertEqual(1, len(permissions))
+        self.assertEqual('W', permissions[0].mode)
+
+
+class EditImageViewTest(TestCase):
+
+    def setUp(self):
+        self.gallery = GalleryFactory()
+        self.image = ImageFactory(gallery=self.gallery)
+        self.profile1 = ProfileFactory()
+        self.profile2 = ProfileFactory()
+        self.profile3 = ProfileFactory()
+        self.user_gallery1 = UserGalleryFactory(user=self.profile1.user, gallery=self.gallery, mode='W')
+        self.user_gallery2 = UserGalleryFactory(user=self.profile2.user, gallery=self.gallery, mode='R')
+
+    def tearDown(self):
+        self.image.delete()
+
+    def test_denies_anonymous(self):
+        response = self.client.get(reverse(
+            'zds.gallery.views.edit_image',
+            args=[15, 156]
+            ),
+            follow=True
+        )
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view')
+                + '?next=' + urllib.quote(reverse('zds.gallery.views.edit_image', args=[15, 156]), ''))
+
+    def test_fail_member_no_permission_can_edit_image(self):
+        login_check = self.client.login(username=self.profile3.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        with open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r') as fp:
+
+            self.client.post(
+                    reverse(
+                        'zds.gallery.views.edit_image',
+                        args=[self.gallery.pk, self.image.pk]
+                    ),
+                    {
+                        'title': 'modify with no perms',
+                        'legend': 'test legend',
+                        'slug': 'test-slug',
+                        'physical': fp
+                    },
+                    follow=True
+            )
+
+        image_test = Image.objects.get(pk=self.image.pk)
+        self.assertNotEqual('modify with no perms', image_test.title)
+        image_test.delete()
+
+    def test_success_member_edit_image(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        with open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r') as fp:
+
+            response = self.client.post(
+                    reverse(
+                        'zds.gallery.views.edit_image',
+                        args=[self.gallery.pk, self.image.pk]
+                    ),
+                    {
+                        'title': 'edit title',
+                        'legend': 'dit legend',
+                        'slug': 'edit-slug',
+                        'physical': fp
+                    },
+                    follow=True
+            )
+
+        self.assertEqual(200, response.status_code)
+        image_test = Image.objects.get(pk=self.image.pk)
+        self.assertEqual('edit title', image_test.title)
+        image_test.delete()
+
+    def test_access_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.get(reverse(
+            'zds.gallery.views.edit_image',
+            args=[self.gallery.pk, self.image.pk]
+        ))
+
+        self.assertEqual(200, response.status_code)
+
+
+class ModifyImageTest(TestCase):
+
+    def setUp(self):
+        self.profile1 = ProfileFactory()
+        self.profile2 = ProfileFactory()
+        self.profile3 = ProfileFactory()
+        self.gallery1 = GalleryFactory()
+        self.gallery2 = GalleryFactory()
+        self.image1 = ImageFactory(gallery=self.gallery1)
+        self.image2 = ImageFactory(gallery=self.gallery1)
+        self.image3 = ImageFactory(gallery=self.gallery2)
+        self.user_gallery1 = UserGalleryFactory(user=self.profile1.user, gallery=self.gallery1)
+        self.user_gallery2 = UserGalleryFactory(user=self.profile1.user, gallery=self.gallery2)
+        self.user_gallery3 = UserGalleryFactory(user=self.profile2.user, gallery=self.gallery1, mode='R')
+
+    def tearDown(self):
+        self.image1.delete()
+        self.image2.delete()
+        self.image3.delete()
+
+    def test_denies_anonymous(self):
+        response = self.client.get(reverse('zds.gallery.views.modify_image'), follow=True)
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view')
+                + '?next=' + urllib.quote(reverse('zds.gallery.views.modify_image'), ''))
+
+    def test_fail_modify_image_with_no_permission(self):
+        login_check = self.client.login(username=self.profile3.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_image'),
+                {
+                    'gallery': self.gallery1.pk,
+                },
+                follow=True,
+        )
+        self.assertTrue(403, response.status_code)
+
+    def test_delete_image_from_other_user(self):
+        """ if user try to remove images from another user without permission"""
+        profile4 = ProfileFactory()
+        gallery4 = GalleryFactory()
+        image4 = ImageFactory(gallery=gallery4)
+        UserGalleryFactory(user=profile4.user, gallery=gallery4)
+        self.assertEqual(1, Image.objects.filter(pk=image4.pk).count())
+
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        self.client.post(
+                reverse('zds.gallery.views.modify_image'),
+                {
+                    'gallery': self.gallery1.pk,
+                    'delete': '',
+                    'image': image4.pk
+                },
+                follow=True,
+        )
+
+        self.assertEqual(1, Image.objects.filter(pk=image4.pk).count())
+        image4.delete()
+
+    def test_success_delete_image_write_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_image'),
+                {
+                    'gallery': self.gallery1.pk,
+                    'delete': '',
+                    'image': self.image1.pk
+                },
+                follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+
+        self.assertEqual(0, Image.objects.filter(pk=self.image1.pk).count())
+
+    def test_success_delete_list_images_write_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_image'),
+                {
+                    'gallery': self.gallery1.pk,
+                    'delete_multi': '',
+                    'items': [self.image1.pk, self.image2.pk]
+                },
+                follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+
+        self.assertEqual(0, Image.objects.filter(pk=self.image1.pk).count())
+        self.assertEqual(0, Image.objects.filter(pk=self.image2.pk).count())
+
+    def test_fail_delete_image_read_permission(self):
+        login_check = self.client.login(username=self.profile2.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        response = self.client.post(
+                reverse('zds.gallery.views.modify_image'),
+                {
+                    'gallery': self.gallery1.pk,
+                    'delete': '',
+                    'image': self.image1.pk
+                },
+                follow=True,
+        )
+        self.assertEqual(403, response.status_code)
+
+        self.assertEqual(1, Image.objects.filter(pk=self.image1.pk).count())
+
+
+class NewImageViewTest(TestCase):
+
+    def setUp(self):
+        self.gallery = GalleryFactory()
+        self.profile1 = ProfileFactory()
+        self.profile2 = ProfileFactory()
+        self.profile3 = ProfileFactory()
+        self.user_gallery1 = UserGalleryFactory(user=self.profile1.user, gallery=self.gallery, mode='W')
+        self.user_gallery2 = UserGalleryFactory(user=self.profile2.user, gallery=self.gallery, mode='R')
+
+    def test_denies_anonymous(self):
+        response = self.client.get(reverse('zds.gallery.views.new_image', args=[1]), follow=True)
+        self.assertRedirects(response,
+                reverse('zds.member.views.login_view')
+                + '?next=' + urllib.quote(reverse('zds.gallery.views.new_image', args=[1]), ''))
+
+    def test_success_new_image_write_permission(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+        self.assertEqual(0, len(self.gallery.get_images()))
+
+        with open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r') as fp:
+            response = self.client.post(
+                    reverse(
+                        'zds.gallery.views.new_image',
+                        args=[self.gallery.pk]
+                    ),
+                    {
+                        'title': 'Test title',
+                        'legend': 'Test legend',
+                        'slug': 'test-slug',
+                        'physical': fp
+                    },
+                    follow=True
+            )
+
+        self.assertEqual(200, response.status_code)
+        self.assertEqual(1, len(self.gallery.get_images()))
+        self.gallery.get_images()[0].delete()
+
+    def test_fail_new_image_with_read_permission(self):
+        login_check = self.client.login(username=self.profile2.user.username, password='hostel77')
+        self.assertTrue(login_check)
+        self.assertEqual(0, len(self.gallery.get_images()))
+
+        with open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r') as fp:
+            response = self.client.post(
+                    reverse(
+                        'zds.gallery.views.new_image',
+                        args=[self.gallery.pk]
+                    ),
+                    {
+                        'title': 'Test title',
+                        'legend': 'Test legend',
+                        'slug': 'test-slug',
+                        'physical': fp
+                    },
+                    follow=True
+            )
+
+        self.assertEqual(403, response.status_code)
+        self.assertEqual(0, len(self.gallery.get_images()))
+
+    def test_fail_new_image_with_no_permission(self):
+        login_check = self.client.login(username=self.profile3.user.username, password='hostel77')
+        self.assertTrue(login_check)
+        self.assertEqual(0, len(self.gallery.get_images()))
+
+        with open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r') as fp:
+            response = self.client.post(
+                    reverse(
+                        'zds.gallery.views.new_image',
+                        args=[self.gallery.pk]
+                    ),
+                    {
+                        'title': 'Test title',
+                        'legend': 'Test legend',
+                        'slug': 'test-slug',
+                        'physical': fp
+                    },
+                    follow=True
+            )
+
+        self.assertEqual(403, response.status_code)
+        self.assertEqual(0, len(self.gallery.get_images()))
+
+    def test_fail_gallery_not_exist(self):
+        login_check = self.client.login(username=self.profile1.user.username, password='hostel77')
+        self.assertTrue(login_check)
+
+        with open(os.path.join(settings.SITE_ROOT, 'fixtures', 'logo.png'), 'r') as fp:
+            response = self.client.post(
+                    reverse(
+                        'zds.gallery.views.new_image',
+                        args=[156]
+                    ),
+                    {
+                        'title': 'Test title',
+                        'legend': 'Test legend',
+                        'slug': 'test-slug',
+                        'physical': fp
+                    },
+                    follow=True
+            )
+
+        self.assertEqual(404, response.status_code)
diff --git a/zds/member/tests.py b/zds/member/tests.py
--- a/zds/member/tests.py
+++ b/zds/member/tests.py
@@ -7,6 +7,7 @@
 from django.test import TestCase
 
 from zds.member.factories import ProfileFactory, StaffProfileFactory
+from zds.member.forms import RegisterForm
 from zds.member.models import Profile
 
 from .models import TokenRegister, Ban
@@ -73,6 +74,80 @@ def test_register(self):
 
         self.assertEquals(User.objects.get(username='firm1').is_active, True)
 
+        # From this point we have a valid user (firm1 / firm1@zestedesavoir.com)
+
+        # Now let's test all the failing cases !!
+
+        # 1. If passwords don't match
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'password',
+            'password_confirm': 'flavour',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 2. If passwords are too shorts
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'pass',
+            'password_confirm': 'pass',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 3. If passwords are equal to username
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'TheUsername',
+            'password_confirm': 'TheUsername',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 4. If there is no username
+        form_data = {
+            'username': '',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 5. If there is no email
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': ''
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 6. If the username is already taken
+        form_data = {
+            'username': 'firm1',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 7. If the email is already taken
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': 'firm1@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
     def test_sanctions(self):
         """Test various sanctions."""