From aa2ccaae52702b7c13ef794e9c958e06d526d8d1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Olivier=20Larchev=C3=AAque?= Date: Tue, 1 May 2012 12:53:56 -0400 Subject: [PATCH 1/1] 1.0 --- .gitignore | 19 ++ CHANGES | 16 ++ auf/__init__.py | 1 + auf/django/__init__.py | 1 + auf/django/saml/admin.py | 14 ++ auf/django/saml/backends.py | 36 ++++ auf/django/saml/decorators.py | 14 ++ auf/django/saml/forms.py | 24 +++ auf/django/saml/mellon_urls.py | 9 + auf/django/saml/middleware.py | 11 ++ auf/django/saml/permissions.py | 17 ++ auf/django/saml/settings.py | 27 +++ auf/django/saml/templates/saml/login_form.html | 32 ++++ auf/django/saml/templatetags/saml.py | 15 ++ auf/django/saml/urls.py | 11 ++ auf/django/saml/views.py | 84 ++++++++ doc/Makefile | 153 +++++++++++++++ doc/changements.rst | 4 + doc/conf.py | 242 ++++++++++++++++++++++++ doc/configuration.rst | 104 ++++++++++ doc/deploiement.rst | 85 +++++++++ doc/fonctionnement.rst | 3 + doc/index.rst | 15 ++ doc/installation.rst | 58 ++++++ doc/integration.rst | 21 ++ setup.cfg | 2 + setup.py | 27 +++ 27 files changed, 1045 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGES create mode 100755 auf/__init__.py create mode 100755 auf/django/__init__.py create mode 100755 auf/django/saml/__init__.py create mode 100644 auf/django/saml/admin.py create mode 100644 auf/django/saml/backends.py create mode 100644 auf/django/saml/decorators.py create mode 100644 auf/django/saml/forms.py create mode 100644 auf/django/saml/mellon_urls.py create mode 100644 auf/django/saml/middleware.py create mode 100644 auf/django/saml/permissions.py create mode 100644 auf/django/saml/settings.py create mode 100644 auf/django/saml/templates/saml/login_form.html create mode 100644 auf/django/saml/templatetags/__init__.py create mode 100644 auf/django/saml/templatetags/saml.py create mode 100644 auf/django/saml/urls.py create mode 100644 auf/django/saml/views.py create mode 100644 doc/Makefile create mode 100644 doc/changements.rst create mode 100644 doc/conf.py create mode 100644 doc/configuration.rst create mode 100644 doc/deploiement.rst create mode 100644 doc/fonctionnement.rst create mode 100644 doc/index.rst create mode 100644 doc/installation.rst create mode 100644 doc/integration.rst create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..366cf68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# binaires +*.pyc +*.pyo + +# Fichier temporaires: +.*.swp +*~ +\#*# +src/* + +# DB de dev +*.db + +# restants de merge +*.orig +*.rej + +*-info +doc/_* diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..c34c6c7 --- /dev/null +++ b/CHANGES @@ -0,0 +1,16 @@ +1.0 +--- + +* Backend d'authentification REMOTE_USER + +* Middleware adaptatif + +* templatetags les urls + +* décorateur is_employe + +* Mode SANBOX + + - ursurpation compte locale + + - simulation du serveur d'identités diff --git a/auf/__init__.py b/auf/__init__.py new file mode 100755 index 0000000..de40ea7 --- /dev/null +++ b/auf/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/auf/django/__init__.py b/auf/django/__init__.py new file mode 100755 index 0000000..de40ea7 --- /dev/null +++ b/auf/django/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/auf/django/saml/__init__.py b/auf/django/saml/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/auf/django/saml/admin.py b/auf/django/saml/admin.py new file mode 100644 index 0000000..61ec8f2 --- /dev/null +++ b/auf/django/saml/admin.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from views import redirect_to_login +from django.contrib.admin import site + +def saml_login(request, extra_context=None): + return redirect_to_login(request) + + +# on recable runtime la fonction qui prend en charge +# le login côté de l'admin pour avoir le même comportement +# que dans le frontend +site.login = saml_login + diff --git a/auf/django/saml/backends.py b/auf/django/saml/backends.py new file mode 100644 index 0000000..b843e3d --- /dev/null +++ b/auf/django/saml/backends.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +from django.contrib.auth.models import User +from django.contrib.auth.backends import RemoteUserBackend, ModelBackend +from auf.django.saml import settings + + +class FakeSPBackend(ModelBackend): + """ + On autentifie uniquement sur le username + """ + def authenticate(self, username=None, password=None): + try: + return User.objects.get(username=username) + except User.DoesNotExist: + return None + + +class RealSPBackend(RemoteUserBackend): + """ + Backend reposant sur le id.auf.org + """ + create_unknown_user = True + + def clean_username(self, username): + """ + Le IdP retourne le courriel + """ + return username.replace('@auf.org', '') + +if settings.SAML_AUTH: + SPBackend = RealSPBackend +else: + SPBackend = FakeSPBackend + + diff --git a/auf/django/saml/decorators.py b/auf/django/saml/decorators.py new file mode 100644 index 0000000..70e84b8 --- /dev/null +++ b/auf/django/saml/decorators.py @@ -0,0 +1,14 @@ + +from views import redirect_to_login +from permissions import is_employe +from settings import SAML_REDIRECT_FIELD_NAME + +def employe_required(function=None, redirect_field_name=SAML_REDIRECT_FIELD_NAME, login_url=None): + """ + """ + def _wrapped_view(request, *args, **kwargs): + if is_employe(request.user): + return function(request, *args, **kwargs) + else: + return redirect_to_login(request, redirect_to=login_url) + return _wrapped_view diff --git a/auf/django/saml/forms.py b/auf/django/saml/forms.py new file mode 100644 index 0000000..d516ca4 --- /dev/null +++ b/auf/django/saml/forms.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth import authenticate +from django import forms + + +class RemoteUserForm(forms.Form): + username = forms.CharField(label=_("Username"), max_length=30) + + def __init__(self, request=None, *args, **kwargs): + self.request = request + super(RemoteUserForm, self).__init__(*args, **kwargs) + + def clean(self): + username = self.cleaned_data.get('username') + + if username: + self.user = authenticate(username=username, password=None) + if self.user is None: + raise forms.ValidationError("Aucun utilisateur\ + local.") + return self.cleaned_data + diff --git a/auf/django/saml/mellon_urls.py b/auf/django/saml/mellon_urls.py new file mode 100644 index 0000000..86e5b20 --- /dev/null +++ b/auf/django/saml/mellon_urls.py @@ -0,0 +1,9 @@ +# -*- encoding: utf-8 -* + +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns( + '', + url(r'^mellon/login$', 'auf.django.saml.views.login_form', ), + url(r'^mellon/logout$', 'auf.django.saml.views.mellon_logout', ), +) diff --git a/auf/django/saml/middleware.py b/auf/django/saml/middleware.py new file mode 100644 index 0000000..a14a186 --- /dev/null +++ b/auf/django/saml/middleware.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from django.contrib.auth.middleware import RemoteUserMiddleware +from auf.django.saml import settings + +class SPMiddleware(RemoteUserMiddleware): + + def process_request(self, request): + if not settings.SAML_AUTH: + return + return super(SPMiddleware, self).process_request(request) diff --git a/auf/django/saml/permissions.py b/auf/django/saml/permissions.py new file mode 100644 index 0000000..9133594 --- /dev/null +++ b/auf/django/saml/permissions.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + + +def is_employe(user): + """ + La dépendance au paquet auf.django.references + est condiotionnelle, 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 + return Employe.objects.filter(courriel=user.email).exists() diff --git a/auf/django/saml/settings.py b/auf/django/saml/settings.py new file mode 100644 index 0000000..0ef0e64 --- /dev/null +++ b/auf/django/saml/settings.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +""" +Ce module définit toutes les constantes utilisées par le paquet +auf.django.saml. Chacune peut être surchargée dans les settings +chargés par Django, typiquement le fichier project/conf.py. +""" +from django.conf import settings + + +# Active par défaut l'utilisation du serveur d'identités +SAML_AUTH = getattr(settings, 'SAML_AUTH', True) + +# Variable utilisée pour fournir au serveur d'identités les +# adresses de retour du site. +SAML_REDIRECT_FIELD_NAME = getattr(settings, '', 'ReturnTo') + +# URL de la page où l'utilisateur sera redirigé après déconnexion +SAML_LOGOUT_REDIRECT_URL = getattr(settings,'', '/') + +# URL où est mappée la fonction login du module Apache Mellon +SAML_MELLON_LOGIN_URL = getattr(settings, '', '/mellon/login') + +# URL où est mappée la fonction logout du module Apache Mellon +SAML_MELLON_LOGOUT_URL = getattr(settings, '', '/mellon/logout') + +# URL où l'utilisateur peut modifier les propriétés globales de son profil +SAML_CHANGE_PASSWORD_URL = getattr(settings, '', 'http://id.auf.org/profile') diff --git a/auf/django/saml/templates/saml/login_form.html b/auf/django/saml/templates/saml/login_form.html new file mode 100644 index 0000000..74aeb7d --- /dev/null +++ b/auf/django/saml/templates/saml/login_form.html @@ -0,0 +1,32 @@ + + + + + + + + +
+

SANDBOX : id.auf.org

+

Simulation de la connexion avec le serveur d'identités

+

Taper le l'identifiant pour vous connectez avec un compte + (django user) qui existe localement.

+ +
{% csrf_token %} + {{ form }} + +
+
+ + + + diff --git a/auf/django/saml/templatetags/__init__.py b/auf/django/saml/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auf/django/saml/templatetags/saml.py b/auf/django/saml/templatetags/saml.py new file mode 100644 index 0000000..c27116b --- /dev/null +++ b/auf/django/saml/templatetags/saml.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from django import template +from auf.django.saml.views import redirect_to_login, redirect_to_logout + +register = template.Library() + +@register.simple_tag(takes_context=True) +def mellon_login_url(context, url=None): + return redirect_to_login(context['request'], url, do_redirect=False) + + +@register.simple_tag(takes_context=True) +def mellon_logout_url(context, url=None): + return redirect_to_logout(context['request'], url, do_redirect=False) diff --git a/auf/django/saml/urls.py b/auf/django/saml/urls.py new file mode 100644 index 0000000..43f5263 --- /dev/null +++ b/auf/django/saml/urls.py @@ -0,0 +1,11 @@ +# -*- encoding: utf-8 -* + +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns( + '', + url(r'^logout/$', 'auf.django.saml.views.local_logout', + name='local_logout'), + url(r'^admin/password_change/$', 'auf.django.saml.views.password_change', + name='password_change'), +) diff --git a/auf/django/saml/views.py b/auf/django/saml/views.py new file mode 100644 index 0000000..d899022 --- /dev/null +++ b/auf/django/saml/views.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- + +from django.http import HttpResponse +from django.core.urlresolvers import reverse +from django.contrib.auth import login as auth_login +from django.contrib.auth import logout as auth_logout +from django.template import RequestContext +from django.shortcuts import render_to_response, redirect +from forms import RemoteUserForm +from settings import SAML_REDIRECT_FIELD_NAME,\ + SAML_MELLON_LOGIN_URL,\ + SAML_MELLON_LOGOUT_URL,\ + SAML_CHANGE_PASSWORD_URL,\ + SAML_LOGOUT_REDIRECT_URL + + +def redirect_to_login(request, redirect_to=None, do_redirect=True): + if redirect_to is None: + redirect_to = request.get_full_path() + url = "%s?%s=%s" % (SAML_MELLON_LOGIN_URL, + SAML_REDIRECT_FIELD_NAME, + redirect_to, + ) + if do_redirect: + return redirect(url) + else: + return url + + +def redirect_to_logout(request, redirect_to=None, do_redirect=True): + if redirect_to is None: + redirect_to = SAML_LOGOUT_REDIRECT_URL + url = "%s?%s=%s" % (reverse('local_logout'), + SAML_REDIRECT_FIELD_NAME, + redirect_to, + ) + if do_redirect: + return redirect(url) + else: + return url + + +def login_form(request, ): + """ + Page de login en mode développement + permet de se connecter avec un user selon son username défini localemement + """ + redirect_to = request.REQUEST.get(SAML_REDIRECT_FIELD_NAME, '/') + if request.method == "POST": + form = RemoteUserForm(request, request.POST) + if form.is_valid(): + auth_login(request, form.user) + return redirect(redirect_to) + else: + form = RemoteUserForm(request) + + c = {'form': form} + return render_to_response("saml/login_form.html", + c, + context_instance=RequestContext(request)) + + +def local_logout(request, ): + """ + Logout pour SAML pour détruire la session Django + """ + query_string = request.META['QUERY_STRING'] + auth_logout(request) + logout_url = "%s?%s" % (SAML_MELLON_LOGOUT_URL, query_string) + response = HttpResponse(content="", status=303) + response["Location"] = logout_url + return response + + +def mellon_logout(request, ): + """ + Simule la vue qui de mellon qui initie le logout sur le l'IdP + """ + redirect_to = request.REQUEST.get(SAML_REDIRECT_FIELD_NAME, '/') + return redirect(redirect_to) + + +def password_change(request, ): + return redirect(SAML_CHANGE_PASSWORD_URL) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..9f64a02 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aufdjangosaml.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aufdjangosaml.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/aufdjangosaml" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aufdjangosaml" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/changements.rst b/doc/changements.rst new file mode 100644 index 0000000..698b559 --- /dev/null +++ b/doc/changements.rst @@ -0,0 +1,4 @@ +Historique des changements +========================== + +.. include:: ../CHANGES diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..c11ebe4 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +# +# auf.django.saml documentation build configuration file, created by +# sphinx-quickstart on Thu Apr 26 15:56:20 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'auf.django.saml' +copyright = u'2012, Olivier Larchevêque' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'fr' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'aufdjangosamldoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'aufdjangosaml.tex', u'auf.django.saml Documentation', + u'Olivier Larchevêque', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'aufdjangosaml', u'auf.django.saml Documentation', + [u'Olivier Larchevêque'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'aufdjangosaml', u'auf.django.saml Documentation', + u'Olivier Larchevêque', 'aufdjangosaml', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff --git a/doc/configuration.rst b/doc/configuration.rst new file mode 100644 index 0000000..1856660 --- /dev/null +++ b/doc/configuration.rst @@ -0,0 +1,104 @@ +Configuration +************* + +Cablâge de SAML dans le site +============================ + +Le paquet apporte : + + * de nouvelles urls + + * de nouveaux backends d'autentification + + * de nouveaux middlewares + +qui doivent être déclarés dans le projet afin d'être effectivement utilisés. + +Fichier *project/urls.py* +------------------------- + +.. warning:: + + Les urls de *auf.django.saml.urls* doit être déclarées avant celles des admins (*admin.sites.urls* ou *admin_tools.urls*) + afin de cour-circuiter la page de connexion et de changement de mot de passe. + + +.. code-block:: python + + from auf.django.saml import settings as saml_settings + + urlpatterns = patterns( + '', + ... + (r'^', include('auf.django.saml.urls')), + ... + ) + + if not saml_settings.SAML_AUTH: + urlpatterns += patterns( + '', + (r'^', include('auf.django.saml.mellon_urls')), + ) + +Fichier *project/settings.py* +----------------------------- + +.. code-block:: python + + INSTALLED_APP = ( + ... + 'auf.djangl.saml', + ... + ) + +Fichier *project/conf.py* +------------------------- + +.. Warning:: + Ce fichier est local, non commité, contenant des informations sensibles + relative à l'environnement de déploiement. + +.. Warning:: + En production, aucunes options SAML ne devraient être redéfinies + +Ce paquet dispose d'une option utile en mode développement: + +**SAML_AUTH** peut être positionné à **False**, ce que a pour effet de simuler la présence +du serveur d'identités en terme de passages entre les 2 sites. + +.. code-block:: python + + SAML_AUTH = False + + +Fichier *project/settings.py* +----------------------------- + +* Middleware **SMiddleware** + +.. Warning:: + *auf.django.saml.middleware.SPMiddleware* doit impérativement être déclaré après + *django.contrib.auth.middleware.AuthenticationMiddleware*. + +.. code-block:: python + + MIDDLEWARE_CLASSES = ( + ... + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'auf.django.saml.middleware.SPMiddleware', + ... + ) + +* Backend d'autentification **SPBackend** + +.. code-block:: python + + AUTHENTICATION_BACKENDS = ( + 'auf.django.saml.backends.SPBackend', + ) + + +Options +======= + +.. literalinclude:: ../auf/django/saml/settings.py diff --git a/doc/deploiement.rst b/doc/deploiement.rst new file mode 100644 index 0000000..a55fe49 --- /dev/null +++ b/doc/deploiement.rst @@ -0,0 +1,85 @@ +Déploiement +*********** + +Lorsqu'on parle de déploiement, c'est dans le sens où on désire brancher +son site Web avec Apache, Mellon et le serveur d'identités. + +Cette opération peut se faire localement, sous réserve que notre application +soit déclarée sur le serveur d'autentification. + +Prérequis +========= + +Installer Apache >=2 +++++++++++++++++++++ + + +Installer Mellon +++++++++++++++++ + +.. warning:: + + La version installée doit respecter ces versions: + + * mod_mellon >= 0.4 + + * liblasso3 > 2.2.2 + +.. Note:: + + Configurer Apache pour qu'il charge ce nouveau module dans le fichier + */etc/apache/httpd.conf* + + LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so + + +Créer un nouveau host ++++++++++++++++++++++ + +Ajout d'un host dans le fichier *etc/hosts*, exemple ici: **olarcheveque** + + +Créer fichiers pour communiquer avec le serveur d'identités +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +**metadata.xml**:: + + wget --no-check-certificate https://id.auf.org/idp/saml2/metadata -O /srv/id.auf.org-metadata.xml + +**clefs**:: + + openssl req -new -x509 -keyout /srv/olarcheveque-mellon-key.pem -out /srv/olarcheveque-mellon-cert.pem -nodes -days 3650 -newkey rsa:2048 -subj "/CN=olarcheveque" + +Créer un vhost *olarcheveque* ++++++++++++++++++++++++++++++ + +dans /etc/site-available/olarcheveque: + +:: + + + ServerName olarcheveque + ErrorLog /var/log/apache2/olarcheveque-error.log + LogLevel warn + CustomLog /var/log/apache2/olarcheveque-access.log combined + Alias /static /net/nfs-authnss.b.ca.auf/home/olivier.larcheveque/Projets/olarcheveque/sitestatic + WSGIScriptAliasMatch ^/(?!mellon) /net/nfs-authnss.b.ca.auf/home/olivier.larcheveque/Projets/olarcheveque/bin/django.wsgi + + + AuthType "Mellon" + MellonEnable "info" + MellonUser "mail" + MellonOrganizationName "olarcheveque" + MellonOrganizationDisplayName "fr" "olarcheveque" + MellonOrganizationURL "http://www.auf.org" + MellonSPPrivateKeyFile /srv/olarcheveque-mellon-key.pem + MellonSPCertFile /srv/olarcheveque-mellon-cert.pem + MellonIdPMetadataFile /srv/id.auf.org-metadata.xml + + + + +.. note:: + + Ne pas oublier d'activer le vhost avec **a2ensite**. + diff --git a/doc/fonctionnement.rst b/doc/fonctionnement.rst new file mode 100644 index 0000000..66010a5 --- /dev/null +++ b/doc/fonctionnement.rst @@ -0,0 +1,3 @@ +Fonctionnement +============== + diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..95883b1 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,15 @@ +Documentation du paquet auf.django.saml +======================================= + + +Ce paquet propose les outils pour coupler un site Django avec le `serveur d'identités de l'AUF `_. + +.. toctree:: + :maxdepth: 2 + + installation + configuration + integration + deploiement + fonctionnement + changements diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 0000000..2427c73 --- /dev/null +++ b/doc/installation.rst @@ -0,0 +1,58 @@ +Installation +************ + +Buildout +======== + +Configuration du fichier *buildout.cfg* +--------------------------------------- + +* Section **[buildout]**, définir l'emplacement du paquet:: + + find-links = + ... + http://pypi.auf.org/simple/auf.django.saml + +* Section **[buildout]**, identifier le paquet à installer:: + + eggs = + ... + auf.django.saml + +* Section **[versions]**, choix de la version du paquet:: + + auf.django.saml = x.x + +Installation automatique via buildout +------------------------------------- + +La configuration précédente va permettre à buildout de télécharger le paquet, +l'installer et le rendre disponible pour le projet Django:: + + bin/builout -c devel.cfg + +Exemple minimaliste du fichier *buildout.cfg* +--------------------------------------------- + +:: + + [buildout] + versions = versions + + find-links = http://pypi.auf.org/simple/auf.recipe.django + http://pypi.auf.org/simple/auf.django.saml + + eggs = + auf.recipe.django + auf.django.saml + + [versions] + auf.django.saml = 1.0 + + [django] + recipe = auf.recipe.django + wsgi=true + settings=production + extra-paths = project + eggs = ${buildout:eggs} + diff --git a/doc/integration.rst b/doc/integration.rst new file mode 100644 index 0000000..ed450c6 --- /dev/null +++ b/doc/integration.rst @@ -0,0 +1,21 @@ +Intégration +*********** + +Cette partie explique comment bien construire les templates. + +Ce paquet fourni des templatetags, pour ne pas avoir à réfléchir +aux urls utilisées pour les redirections au serveur d'identités +ou encore les urls en mode développement. + +Voici un exemple de ce qu'on pourrait trouver dans une template +Django pour générer un **lien de connexion** ou un **lien de +déconnexion** si le user est connecté ou non: + +.. code-block:: guess + + {% load saml %} + + {% if request.user.is_authenticated %} + {% mellon_logout_url %} + {% else %} + {% mellon_login_url request.get_full_path %} diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..49a8107 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[egg_info] +tag_svn_revision = true diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d4e3aed --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +from setuptools import setup, find_packages + +name = 'auf.django.saml' +version = '1.0' + +setup(name=name, + version=version, + description="Package to deal with our Identity Provider", + long_description="""\ +""", + classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + keywords='Django SAML Auth', + author='Olivier Larchev\xc3\xaaque', + author_email='olivier.larcheveque@auf.org', + url='http://pypi.auf.org/%s' % name, + license='GPL', + namespace_packages = ['auf', 'auf.django', ], + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=False, + install_requires=[ + # -*- Extra requirements: -*- + ], + entry_points=""" + # -*- Entry points: -*- + """, + ) -- 1.7.10.4