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
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()
-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
--- /dev/null
+# -*- 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)
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):
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])
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()
--- /dev/null
+# -*- 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)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+from settings import * # noqa
+
+SAML_AUTH = False
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
--- /dev/null
+# -*- 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)
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', )
--- /dev/null
+# -*- 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'))
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"),
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')),
+ )
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