coverage
authorOlivier Larchevêque <olivier.larcheveque@auf.org>
Tue, 29 Oct 2013 18:55:51 +0000 (14:55 -0400)
committerOlivier Larchevêque <olivier.larcheveque@auf.org>
Tue, 29 Oct 2013 18:55:51 +0000 (14:55 -0400)
13 files changed:
auf/django/saml/forms.py
auf/django/saml/permissions.py
auf/django/saml/tests/__init__.py
auf/django/saml/tests/admin.py [new file with mode: 0644]
auf/django/saml/tests/common.py
auf/django/saml/tests/dev.py [new file with mode: 0644]
auf/django/saml/tests/dev_settings.py [new file with mode: 0644]
auf/django/saml/tests/middleware.py
auf/django/saml/tests/permissions.py [new file with mode: 0644]
auf/django/saml/tests/settings.py
auf/django/saml/tests/template_tags.py [new file with mode: 0644]
auf/django/saml/tests/urls.py
tox.ini

index a194982..fd87081 100644 (file)
@@ -18,6 +18,5 @@ class RemoteUserForm(forms.Form):
         if username:
             self.user = authenticate(username=username, password=None)
             if self.user is None:
-                raise forms.ValidationError("Aucun utilisateur\
-                local.")
+                raise forms.ValidationError("Aucun utilisateur local.")
         return self.cleaned_data
index 9133594..e073fb2 100644 (file)
@@ -4,14 +4,8 @@
 def is_employe(user):
     """
     La dépendance au paquet auf.django.references
-    est condiotionnelle, on peut imaginer que l'application
+    est conditionnelle, on peut imaginer que l'application
     soit uniquement pour d'autres types de personnes.
     """
-    try:
-        from auf.django.references.models import Employe
-    except:
-        return False
-
-    if not hasattr(user, 'email'):
-        return False
+    from auf.django.references.models import Employe
     return Employe.objects.filter(courriel=user.email).exists()
index c357504..0047ecd 100644 (file)
@@ -1,3 +1,9 @@
-from .saml import TemplateTagTest  # noqa
-from .saml import AdminTest   # noqa
-from .saml import PermissionTest   # noqa
+
+from auf.django.saml import settings
+
+if settings.SAML_AUTH:
+    from .admin import AdminTest  # noqa
+    from .permissions import PermissionTest  # noqa
+    from .template_tags import TemplateTagTest  # noqa
+else:
+    from .dev import DevTest  # noqa
diff --git a/auf/django/saml/tests/admin.py b/auf/django/saml/tests/admin.py
new file mode 100644 (file)
index 0000000..c38d67b
--- /dev/null
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+
+from django.core.urlresolvers import reverse
+
+from django.contrib.auth.models import User
+
+from auf.django.saml import settings
+
+from .middleware import LOGGED_USER_EMAIL, LOGGED_USER_GN, LOGGED_USER_SN
+from .common import CommonTest
+
+
+class AdminTest(CommonTest):
+    """
+    Teste le comportement de l'admin avec l'IDP.
+    """
+
+    def test_anonymize(self):
+        """
+        Test la fonction qui ajoute un params GET.
+        """
+        url = self.anonymize(reverse('admin:index'))
+        self.assertEqual(url.count('?'), 1)
+
+    def test_admin_index_anonymous(self):
+        """
+        La page de login de l'admin doît être désactivée.
+        Il n'y a pas de redirection vers le login.
+        """
+        url = self.anonymize(reverse('admin:index'))
+        response = self.client.get(url)
+        self.assertEqual(
+            response['location'].count('?'),
+            1,
+            response['location'])
+        self.assertEqual(response.status_code, 302)
+        location, dummy = self.redirectize(response['location']).split('?')
+        self.assertEqual(location, settings.SAML_MELLON_LOGIN_URL)
+
+    def test_admin_index_authenticated(self):
+        """
+        L'admin est inacessible par défaut, mais le user est crée à la volée.
+        """
+        url = reverse('admin:index')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 403)
+
+        self.assertEqual(User.objects.count(), 1)
+        user = User.objects.all()[0]
+        email = LOGGED_USER_EMAIL.replace('@auf.org', '')
+        self.assertEqual(user.username, email,)
+        self.assertEqual(user.email, LOGGED_USER_EMAIL,)
+        self.assertEqual(user.first_name, LOGGED_USER_GN)
+        self.assertEqual(user.last_name, LOGGED_USER_SN)
+
+    def test_admin_login(self):
+        """
+        Test l'accès à l'admin selon le flag is_staff du compte local.
+        """
+        self.creer_user()
+        url = reverse('admin:index')
+        self.client.get(url)
+
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_admin_logout(self):
+        """
+        Test la redirection du logout local puis au IdP
+        """
+        self.creer_user()
+        url = reverse('admin:logout')
+        response = self.client.get(url)
+        location, qs = self.redirectize(response['location']).split('?')
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(location, reverse('local_logout'))
+
+        response = self.client.get(location)
+        self.assertEqual(response.status_code, 302)
+
+    def test_admin_change_password(self):
+        """
+        Test que le changement de mot de passe redirige vers l'IdP.
+        """
+        self.creer_user()
+        url = reverse('admin:password_change')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 302)
index 07441ac..2f173bf 100644 (file)
@@ -6,7 +6,11 @@ import urlparse
 from django.test import TestCase
 from django.test.client import Client
 
-from .middleware import ANONYMOUS_KEY
+from django.contrib.auth.models import User
+
+from auf.django.references import models as ref
+
+from .middleware import LOGGED_USER_EMAIL, ANONYMOUS_KEY, LOGGED_USER_USERNAME
 
 
 class CommonTest(TestCase):
@@ -15,6 +19,10 @@ class CommonTest(TestCase):
         self.client = Client()
 
     def anonymize(self, url):
+        """
+        Ajoute un flag dans l'URL pour fonctionner comme utilisateur non
+        authentifié.
+        """
         params = {ANONYMOUS_KEY: 1, }
         url_parts = urlparse.urlsplit(url)
         qs = urlparse.parse_qs(url_parts[4])
@@ -22,5 +30,32 @@ class CommonTest(TestCase):
         return "%s?%s" % (url, urllib.urlencode(qs), )
 
     def redirectize(self, url):
+        """
+        Extrait de l'URL le protocole et fqdn
+        """
         url_parts = urlparse.urlsplit(url)
-        return "%s?%s" % (url_parts[2], url_parts[3], )
+        if url_parts[3]:
+            return "%s?%s" % (url_parts[2], url_parts[3], )
+        else:
+            return url_parts[2]
+
+    def creer_employe(self):
+        """
+        Créer un employé correspondant à la personne connecté dans le
+        MockMiddleware.
+        """
+        ref.Employe(
+            implantation_id=1,
+            implantation_physique_id=1,
+            service_id=1,
+            courriel=LOGGED_USER_EMAIL).save()
+
+    def creer_user(self):
+        """
+        Créer un user Django staff correspondant à la personne connecté dans le
+        MockMiddleware.
+        """
+        User(
+            is_staff=True,
+            username=LOGGED_USER_USERNAME,
+            email=LOGGED_USER_EMAIL).save()
diff --git a/auf/django/saml/tests/dev.py b/auf/django/saml/tests/dev.py
new file mode 100644 (file)
index 0000000..e80042d
--- /dev/null
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+from django.core.urlresolvers import reverse
+
+from auf.django.saml.settings import SAML_LOGOUT_REDIRECT_URL
+
+from .middleware import LOGGED_USER_EMAIL
+from .common import CommonTest
+
+
+class DevTest(CommonTest):
+    """
+    Teste le comportement en mode sandbox.
+    """
+
+    def test_admin_index(self):
+        """
+        La page d'index affiche le form de login.
+        """
+        url = reverse('admin:index')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 302)
+        location, dummy = self.redirectize(response['location']).split('?')
+        self.assertEqual(location, reverse('sandbox_login'))
+
+    def test_admin_login_no_user(self):
+        """
+        Le compte local n'est pas présent.
+        """
+        url = reverse('admin:index')
+        response = self.client.get(url, follow=True)
+        login_url, dummy = self.redirectize(
+            response.redirect_chain[0][0]).split('?')
+        response = self.client.post(login_url, {
+            'username': LOGGED_USER_EMAIL,
+            },
+            )
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, "Aucun utilisateur local.")
+
+    def test_admin_login_user_in(self):
+        """
+        Le compte local est présent.
+        """
+        self.creer_user()
+        url = reverse('admin:index')
+        response = self.client.get(url, follow=True)
+        login_url = self.redirectize(
+            response.redirect_chain[0][0])
+        response = self.client.post(login_url, {
+            'username': LOGGED_USER_EMAIL,
+            },
+            )
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(url, self.redirectize(response['location']))
+
+    def test_admin_logout(self):
+        """
+        Déconnexion locale.
+        """
+        self.creer_user()
+        url = reverse('admin:index')
+        response = self.client.get(url, follow=True)
+        login_url = self.redirectize(
+            response.redirect_chain[0][0])
+        self.client.post(login_url, {
+            'username': LOGGED_USER_EMAIL,
+            },
+            )
+        response = self.client.get(reverse('admin:logout'), follow=True)
+        urls = [self.redirectize(u).split('?')[0] for (u, status_code) in
+                response.redirect_chain]
+        self.assertEqual(urls[0], reverse('local_logout'))
+        self.assertEqual(urls[1], reverse('sandbox_logout'))
+        self.assertEqual(urls[2], SAML_LOGOUT_REDIRECT_URL)
diff --git a/auf/django/saml/tests/dev_settings.py b/auf/django/saml/tests/dev_settings.py
new file mode 100644 (file)
index 0000000..1c2bbaa
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+from settings import *  # noqa
+
+SAML_AUTH = False
index 0a247e3..c0e21a8 100644 (file)
@@ -3,12 +3,15 @@
 ANONYMOUS_KEY = 'anonymous'
 
 LOGGED_USER_EMAIL = 'admin@auf.org'
+LOGGED_USER_USERNAME = LOGGED_USER_EMAIL.replace('@auf.org', '')
 LOGGED_USER_GN = 'admin_gn'
 LOGGED_USER_SN = 'admin_sn'
 
 
 class MockMiddleware(object):
-
+    """
+    Fake auth sur notre IdP id.auf.org
+    """
     def process_request(self, request):
         if not ANONYMOUS_KEY in request.META['QUERY_STRING']:
             request.META['REMOTE_USER'] = LOGGED_USER_EMAIL
diff --git a/auf/django/saml/tests/permissions.py b/auf/django/saml/tests/permissions.py
new file mode 100644 (file)
index 0000000..261d857
--- /dev/null
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+
+from django.core.urlresolvers import reverse
+
+from .common import CommonTest
+
+
+class PermissionTest(CommonTest):
+    """
+    Teste les outils de sécurisation.
+    """
+
+    def test_employe_required_anonymous(self):
+        """
+        Test le decorateur sans utilisateur connecté.
+        """
+        url = self.anonymize(reverse('test_employe_required'))
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 302)
+
+    def test_employe_required_authenticated(self):
+        """
+        Test le decorateur avec un utilisateur connecté.
+        """
+        url = reverse('test_employe_required')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 403)
+
+    def test_employe_required_ok(self):
+        """
+        Test le decorateur avec un employé connecté.
+        """
+        self.creer_employe()
+        url = reverse('test_employe_required')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_login_required_anonymous(self):
+        """
+        Test le décorateur de connexion requise avec un anonyme.
+        """
+        url = self.anonymize(reverse('test_login_required'))
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 302)
+
+    def test_login_required_authenticated(self):
+        """
+        Test le décorateur de connexion requise.
+        """
+        url = reverse('test_login_required')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
index b297327..edb66d8 100644 (file)
@@ -12,10 +12,13 @@ ROOT_URLCONF = 'auf.django.saml.tests.urls'
 DATABASES = {'default':
             {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', }}
 
+AUF_REFERENCES_MANAGED = True
+
 INSTALLED_APPS = ('django.contrib.auth',
                   'django.contrib.contenttypes',
                   'django.contrib.sessions',
                   'django.contrib.admin',
+                  'auf.django.references',
                   'auf.django.saml', )
 
 
diff --git a/auf/django/saml/tests/template_tags.py b/auf/django/saml/tests/template_tags.py
new file mode 100644 (file)
index 0000000..e61d0e3
--- /dev/null
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+import re
+import urllib
+
+from django.core.urlresolvers import reverse
+
+from auf.django.saml import settings
+
+from .common import CommonTest
+
+
+class TemplateTagTest(CommonTest):
+    """
+    Teste les fonctionnalités des templatetags SAML.
+    """
+
+    def setUp(self):
+        super(TemplateTagTest, self).setUp()
+        self.url = reverse('test_tags')
+        self.response = self.client.get(self.url)
+
+    def test_templatetag_login_var(self):
+        """
+        Test le rendu du templatetag *mellon_login_url* avec une variable en
+        paramètre.
+        """
+        regex = "test_templatetag_login_var:(.*)\?%s=(.*)\n" % (
+            settings.SAML_REDIRECT_FIELD_NAME, )
+        m = re.search(regex, self.response.content)
+        login_url, redirect_url = m.groups()
+        self.assertEqual(login_url, settings.SAML_MELLON_LOGIN_URL)
+        self.assertEqual(redirect_url, urllib.quote_plus(self.url))
+
+    def test_templatetag_login_default(self):
+        """
+        Test le rendu du templatetag *mellon_login_url* sans paramètre.
+        """
+        regex = "test_templatetag_login_default:(.*)\?%s=(.*)\n" % (
+            settings.SAML_REDIRECT_FIELD_NAME, )
+        m = re.search(regex, self.response.content)
+        login_url, redirect_url = m.groups()
+        self.assertEqual(login_url, settings.SAML_MELLON_LOGIN_URL)
+        self.assertEqual(redirect_url, urllib.quote_plus(self.url))
+
+    def test_templatetag_login_string(self):
+        """
+        Test le rendu du templatetag *mellon_login_url* avec paramètre string.
+        """
+        regex = "test_templatetag_login_string:(.*)\?%s=(.*)\n" % (
+            settings.SAML_REDIRECT_FIELD_NAME, )
+        m = re.search(regex, self.response.content)
+        login_url, redirect_url = m.groups()
+        self.assertEqual(login_url, settings.SAML_MELLON_LOGIN_URL)
+        self.assertEqual(redirect_url, urllib.quote_plus('/admin'))
+
+    def test_templatetag_logout_var(self):
+        """
+        Test le rendu du templatetag *mellon_logout_url* avec une variable en
+        paramètre.
+        """
+        regex = "test_templatetag_logout_var:(.*)\?%s=(.*)\n" % (
+            settings.SAML_REDIRECT_FIELD_NAME, )
+        m = re.search(regex, self.response.content)
+        logout_url, redirect_url = m.groups()
+        self.assertEqual(logout_url, reverse('local_logout'))
+        self.assertEqual(redirect_url, urllib.quote_plus(self.url))
+
+    def test_templatetag_logout_default(self):
+        """
+        Test le rendu du templatetag *mellon_logout_url* sans paramètre.
+        """
+        regex = "test_templatetag_logout_default:(.*)\?%s=(.*)\n" % (
+            settings.SAML_REDIRECT_FIELD_NAME, )
+        m = re.search(regex, self.response.content)
+        logout_url, redirect_url = m.groups()
+        self.assertEqual(logout_url, reverse('local_logout'))
+        self.assertEqual(
+            redirect_url,
+            urllib.quote_plus(settings.SAML_LOGOUT_REDIRECT_URL))
+
+    def test_templatetag_logout_string(self):
+        """
+        Test le rendu du templatetag *mellon_logout_url* avec paramètre string.
+        """
+        regex = "test_templatetag_logout_string:(.*)\?%s=(.*)\n" % (
+            settings.SAML_REDIRECT_FIELD_NAME, )
+        m = re.search(regex, self.response.content)
+        logout_url, redirect_url = m.groups()
+        self.assertEqual(logout_url, reverse('local_logout'))
+        self.assertEqual(redirect_url, urllib.quote_plus('/admin'))
index 300b367..36db30f 100644 (file)
@@ -6,11 +6,14 @@ from django.views.generic import TemplateView
 from django.contrib import admin
 
 from auf.django.saml.decorators import employe_required, login_required
+from auf.django.saml.settings import SAML_AUTH
 
 admin.autodiscover()
 
 urlpatterns = patterns(
     '',
+    url(r'^$', TemplateView.as_view(template_name="test_tags.html"),
+        name='home'),
     (r'^', include('auf.django.saml.urls')),
     (r'^admin/', include(admin.site.urls)),
     url(r'^test_tags/', TemplateView.as_view(template_name="test_tags.html"),
@@ -24,3 +27,9 @@ urlpatterns = patterns(
             TemplateView.as_view(template_name="test_tags.html")),
         name='test_login_required'),
     )
+
+if not SAML_AUTH:
+    urlpatterns += patterns(
+        '',
+        url(r'^', include('auf.django.saml.mellon_urls')),
+        )
diff --git a/tox.ini b/tox.ini
index e515fd5..a398f68 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -11,8 +11,10 @@ deps =
 
 commands =
     coverage erase
-    coverage run --source="{envsitepackagesdir}/auf/django/saml/" {envdir}/bin/django-admin.py test saml --settings=auf.django.saml.tests.settings
+    coverage run -p --source="{envsitepackagesdir}/auf/django/saml/" {envdir}/bin/django-admin.py test saml --settings=auf.django.saml.tests.settings
+    coverage run -p --source="{envsitepackagesdir}/auf/django/saml/" {envdir}/bin/django-admin.py test saml --settings=auf.django.saml.tests.dev_settings
     pep8 -r  --statistics  --count {envsitepackagesdir}/auf/django/saml/
+    coverage combine
     coverage report
     coverage html