diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..a3ddbee --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include COPYING +recursive-include src/authentic2_auth_fedict/templates *.html +recursive-include src/authentic2_auth_fedict/static *.png +recursive-include src/authentic2_auth_fedict/locale *.po *.mo +include VERSION +include MANIFEST.in diff --git a/README b/README new file mode 100644 index 0000000..4dbf13e --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +authentic2-auth-fedict +====================== + +Authentic2 plugin to authenticate against the Belgian Federal Authentication +Service provided by Fedict. + +Installation +------------ + +Install with `pip install authentic2-auth-fedict` + +Settings +======== + +Add `A2_AUTH_FEDICT_ENABLE = True` to your `local_settings.py` file and define +the django-mellon adapter: + + MELLON_ADAPTER = ('authentic2_auth_fedict.adapters.AuthenticAdapter',) + +And appropriate django-mellon parameters (MELLON_PUBLIC_KEYS, +MELLON_PRIVATE_KEY, MELLON_IDENTITY_PROVIDERS). + + +License +------- + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..49633bb --- /dev/null +++ b/setup.py @@ -0,0 +1,111 @@ +#!/usr/bin/python +import sys +import os +import subprocess + +from setuptools import setup, find_packages +from setuptools.command.install_lib import install_lib as _install_lib +from distutils.command.build import build as _build +from distutils.command.sdist import sdist +from distutils.cmd import Command + + +class compile_translations(Command): + description = 'compile message catalogs to MO files via django compilemessages' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + try: + from django.core.management import call_command + for path, dirs, files in os.walk('src'): + if 'locale' not in dirs: + continue + curdir = os.getcwd() + os.chdir(os.path.realpath(path)) + call_command('compilemessages') + os.chdir(curdir) + except ImportError: + sys.stderr.write('!!! Please install Django >= 1.4 to build translations\n') + + +class build(_build): + sub_commands = [('compile_translations', None)] + _build.sub_commands + + +class eo_sdist(sdist): + + def run(self): + print "creating VERSION file" + if os.path.exists('VERSION'): + os.remove('VERSION') + version = get_version() + version_file = open('VERSION', 'w') + version_file.write(version) + version_file.close() + sdist.run(self) + print "removing VERSION file" + if os.path.exists('VERSION'): + os.remove('VERSION') + +class install_lib(_install_lib): + def run(self): + self.run_command('compile_translations') + _install_lib.run(self) + + +def get_version(): + if os.path.exists('VERSION'): + version_file = open('VERSION', 'r') + version = version_file.read() + version_file.close() + return version + if os.path.exists('.git'): + p = subprocess.Popen(['git', 'describe', '--dirty', '--match=v*'], stdout=subprocess.PIPE) + result = p.communicate()[0] + if p.returncode == 0: + version = result.split()[0][1:] + version = version.replace('-', '.') + return version + return '0' + + +setup(name='authentic2-auth-fedict', + version=get_version(), + license='AGPLv3', + description='Authentic2 Fedict plugin', + author="Entr'ouvert", + url='https://repos.entrouvert.org/authentic2-auth-fedict.git', + author_email="info@entrouvert.com", + packages=find_packages('src'), + package_dir={ + '': 'src', + }, + package_data={ + 'authentic2_auth_fedict': [ + 'templates/authentic2_auth_fedict/*.html', + 'static/authentic2_auth_fedict/img/*.png', + 'locale/fr/LC_MESSAGES/django.po', + 'locale/fr/LC_MESSAGES/django.mo', + ], + }, + install_requires=[ + 'authentic2', + ], + entry_points={ + 'authentic2.plugin': [ + 'authentic2-auth-fedict = authentic2_auth_fedict:Plugin', + ], + }, + cmdclass={ + 'build': build, + 'install_lib': install_lib, + 'compile_translations': compile_translations, + 'sdist': eo_sdist}, + zip_safe=False, +) diff --git a/src/authentic2_auth_fedict/__init__.py b/src/authentic2_auth_fedict/__init__.py new file mode 100644 index 0000000..53de95b --- /dev/null +++ b/src/authentic2_auth_fedict/__init__.py @@ -0,0 +1,36 @@ +# authentic2_auth_fedict - Fedict authentication for Authentic +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +class Plugin(object): + def get_before_urls(self): + from . import urls + return urls.urlpatterns + + def get_apps(self): + return ['mellon', __name__] + + def get_authentication_backends(self): + return ['authentic2_auth_fedict.backends.FedictBackend'] + + def get_auth_frontends(self): + return ['authentic2_auth_fedict.auth_frontends.FedictFrontend'] + + def redirect_logout_list(self, request, next_url=None): + from mellon.views import logout + if 'mellon_session' in request.session: + response = logout(request) + return [response['Location']] + return [] diff --git a/src/authentic2_auth_fedict/adapters.py b/src/authentic2_auth_fedict/adapters.py new file mode 100644 index 0000000..58f4915 --- /dev/null +++ b/src/authentic2_auth_fedict/adapters.py @@ -0,0 +1,25 @@ +# authentic2_auth_fedict - Fedict authentication for Authentic +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from mellon.adapters import DefaultAdapter +from django.contrib.auth import get_user_model + +class AuthenticAdapter(DefaultAdapter): + def create_user(self, user_class): + return user_class.objects.create() + + def finish_create_user(self, idp, saml_attributes, user): + pass diff --git a/src/authentic2_auth_fedict/app_settings.py b/src/authentic2_auth_fedict/app_settings.py new file mode 100644 index 0000000..cb0df33 --- /dev/null +++ b/src/authentic2_auth_fedict/app_settings.py @@ -0,0 +1,42 @@ +# authentic2_auth_fedict - Fedict authentication for Authentic +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +class AppSettings(object): + '''Thanks django-allauth''' + __SENTINEL = object() + + def __init__(self, prefix): + self.prefix = prefix + + def _setting(self, name, dflt=__SENTINEL): + from django.conf import settings + from django.core.exceptions import ImproperlyConfigured + + v = getattr(settings, self.prefix + name, dflt) + if v is self.__SENTINEL: + raise ImproperlyConfigured('Missing setting %r' % (self.prefix + name)) + return v + + @property + def enable(self): + return self._setting('ENABLE', False) + + +import sys + +app_settings = AppSettings('A2_AUTH_FEDICT_') +app_settings.__name__ = __name__ +sys.modules[__name__] = app_settings diff --git a/src/authentic2_auth_fedict/auth_frontends.py b/src/authentic2_auth_fedict/auth_frontends.py new file mode 100644 index 0000000..cebb6bb --- /dev/null +++ b/src/authentic2_auth_fedict/auth_frontends.py @@ -0,0 +1,56 @@ +# authentic2_auth_fedict - Fedict authentication for Authentic +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.template.loader import render_to_string +from django.template import RequestContext +from django.shortcuts import render +from django.utils.translation import ugettext_lazy as _ + +from mellon.utils import get_idp, get_idps + +from authentic2.utils import redirect_to_login + +from . import app_settings + + +class FedictFrontend(object): + def enabled(self): + return app_settings.enable and list(get_idps()) + + def name(self): + return _('Belgian eID') + + def id(self): + return 'fedict' + + def login(self, request, *args, **kwargs): + context_instance = kwargs.pop('context_instance', None) or RequestContext(request) + submit_name = 'login-%s' % self.id() + if request.method == 'POST' and submit_name in request.POST: + return redirect_to_login(request, login_url='mellon_login') + return render(request, 'authentic2_auth_fedict/login.html', {'submit_name': submit_name}, + context_instance=context_instance) + + def profile(self, request, *args, **kwargs): + context_instance = kwargs.pop('context_instance', None) or RequestContext(request) + user_saml_identifiers = request.user.saml_identifiers.all() + if not user_saml_identifiers: + return '' + for user_saml_identifier in user_saml_identifiers: + user_saml_identifier.idp = get_idp(user_saml_identifier.issuer) + return render_to_string('authentic2_auth_fedict/profile.html', + {'user_saml_identifiers': user_saml_identifiers}, + context_instance=context_instance) diff --git a/src/authentic2_auth_fedict/backends.py b/src/authentic2_auth_fedict/backends.py new file mode 100644 index 0000000..f82b0f8 --- /dev/null +++ b/src/authentic2_auth_fedict/backends.py @@ -0,0 +1,33 @@ +# authentic2_auth_fedict - Fedict authentication for Authentic +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from mellon.backends import SAMLBackend + +from authentic2.middleware import StoreRequestMiddleware + + +class FedictBackend(SAMLBackend): + def get_saml2_authn_context(self): + # Pass AuthnContextClassRef from the previous IdP + request = StoreRequestMiddleware.get_request() + if request: + authn_context_class_ref = request.session.get( + 'mellon_session', {}).get('authn_context_class_ref') + if authn_context_class_ref: + return authn_context_class_ref + + import lasso + return lasso.SAML2_AUTHN_CONTEXT_PREVIOUS_SESSION diff --git a/src/authentic2_auth_fedict/locale/fr/LC_MESSAGES/django.po b/src/authentic2_auth_fedict/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..2ce2528 --- /dev/null +++ b/src/authentic2_auth_fedict/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,34 @@ +# French translation of Authentic2 Fedict authentication plugin +# Copyright (C) 2016 Entr'ouvert +# This file is distributed under the same license as the authentic2-auth-fedict package. +# Frederic Peters , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: authentic2-auth-fedict 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-04-10 14:09+0200\n" +"PO-Revision-Date: 2016-04-10 14:09+0200\n" +"Last-Translator: Frederic Peters \n" +"Language-Team: french \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: auth_frontends.py:18 +msgid "Belgian eID" +msgstr "Carte eID" + +#: templates/authentic2_auth_fedict/login.html:4 +msgid "You can use your Belgian identity card to log in." +msgstr "Vous pouvez utiliser votre carte d'identité pour vous connecter." + +#: templates/authentic2_auth_fedict/login.html:7 +msgid "Login" +msgstr "Connexion" + +#: templates/authentic2_auth_fedict/login.html:9 +msgid "Cancel" +msgstr "Annuler" diff --git a/src/authentic2_auth_fedict/models.py b/src/authentic2_auth_fedict/models.py new file mode 100644 index 0000000..e69de29 diff --git a/src/authentic2_auth_fedict/static/authentic2_auth_fedict/img/beid_image_mini.png b/src/authentic2_auth_fedict/static/authentic2_auth_fedict/img/beid_image_mini.png new file mode 100644 index 0000000..8d79ab1 Binary files /dev/null and b/src/authentic2_auth_fedict/static/authentic2_auth_fedict/img/beid_image_mini.png differ diff --git a/src/authentic2_auth_fedict/templates/authentic2_auth_fedict/login.html b/src/authentic2_auth_fedict/templates/authentic2_auth_fedict/login.html new file mode 100644 index 0000000..421b4db --- /dev/null +++ b/src/authentic2_auth_fedict/templates/authentic2_auth_fedict/login.html @@ -0,0 +1,12 @@ +{% load i18n static %}
+ +

+ {% trans "You can use your Belgian identity card to log in." %} +

+

+ + {% if cancel %} + + {% endif %} +

+ diff --git a/src/authentic2_auth_fedict/templates/authentic2_auth_fedict/profile.html b/src/authentic2_auth_fedict/templates/authentic2_auth_fedict/profile.html new file mode 100644 index 0000000..2951608 --- /dev/null +++ b/src/authentic2_auth_fedict/templates/authentic2_auth_fedict/profile.html @@ -0,0 +1,9 @@ +

SAML

+ +
    +{% for user_saml_identifier in user_saml_identifiers %} +
  • {% firstof user_saml_identifier.idp.DISPLAY_NAME user_saml_identifier.issuer %} : + {{ user_saml_identifier.name_id }} +
  • +{% endfor %} +
diff --git a/src/authentic2_auth_fedict/urls.py b/src/authentic2_auth_fedict/urls.py new file mode 100644 index 0000000..d508fa5 --- /dev/null +++ b/src/authentic2_auth_fedict/urls.py @@ -0,0 +1,19 @@ +# authentic2_auth_fedict - Fedict authentication for Authentic +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.conf.urls import patterns, url, include + +urlpatterns = patterns('', url(r'^accounts/saml/', include('mellon.urls')))