From 5b270afc981228a150c065e214e14f087bf62a39 Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Fri, 15 May 2015 18:20:53 +0200 Subject: [PATCH] initial commit --- MANIFEST.in | 3 + README | 0 setup.py | 125 ++++++++++++++++++ src/authentic2_beid/__init__.py | 20 +++ src/authentic2_beid/backends.py | 9 ++ src/authentic2_beid/frontends.py | 31 +++++ .../templates/beid/login_form.html | 19 +++ .../templates/beid/profile.html | 33 +++++ src/authentic2_beid/urls.py | 12 ++ src/authentic2_beid/views.py | 59 +++++++++ 10 files changed, 311 insertions(+) create mode 100644 MANIFEST.in create mode 100644 README create mode 100644 setup.py create mode 100644 src/authentic2_beid/__init__.py create mode 100644 src/authentic2_beid/backends.py create mode 100644 src/authentic2_beid/frontends.py create mode 100644 src/authentic2_beid/templates/beid/login_form.html create mode 100644 src/authentic2_beid/templates/beid/profile.html create mode 100644 src/authentic2_beid/urls.py create mode 100644 src/authentic2_beid/views.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..c08f868 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include COPYING +recursive-include src/authentic2_beid/templates *.html +recursive-include src/authentic2_beid/static *.js *.css *.png diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6df78e0 --- /dev/null +++ b/setup.py @@ -0,0 +1,125 @@ +#!/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.commands.compilemessages import \ + compile_messages + for path, dirs, files in os.walk('src'): + if 'locale' not in dirs: + continue + curdir = os.getcwd() + os.chdir(os.path.realpath(path)) + compile_messages(sys.stderr) + os.chdir(curdir) + except ImportError: + print + sys.stderr.write('!!! Please install Django >= 1.4 to build translations') + print + print + + +class build(_build): + sub_commands = [('compile_translations', None)] + _build.sub_commands + +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(): + '''Use the VERSION, if absent generates a version with git describe, if not + tag exists, take 0.0.0- and add the length of the commit log. + ''' + if os.path.exists('VERSION'): + with open('VERSION', 'r') as v: + return v.read() + if os.path.exists('.git'): + p = subprocess.Popen(['git','describe','--dirty','--match=v*'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = p.communicate()[0] + if p.returncode == 0: + return result.split()[0][1:].replace('-', '.') + else: + return '0.0.0-%s' % len( + subprocess.check_output( + ['git', 'rev-list', 'HEAD']).splitlines()) + return '0.0.0' + + +README = file(os.path.join( + os.path.dirname(__file__), + 'README')).read() + +setup(name='authentic2-beid', + version=get_version(), + license='AGPLv3', + description='Authentic2 Belgian eID authentication', + long_description=README, + author="Entr'ouvert", + author_email="info@entrouvert.com", + packages=find_packages('src'), + package_dir={ + '': 'src', + }, + package_data={ + 'authentic2_beid': [ + 'locale/fr/LC_MESSAGES/*.po', + 'locale/fr/LC_MESSAGES/*.mo', + 'templates/authentic2_beid/*.html', + 'static/authentic2_beid/js/*.js', + 'static/authentic2_beid/css/*.css', + 'static/authentic2_beid/img/*.png', + ], + }, + install_requires=[ + 'authentic2', + ], + entry_points={ + 'authentic2.plugin': [ + 'authentic2-beid = authentic2_beid:Plugin', + ], + }, + cmdclass={ + 'build': build, + 'install_lib': install_lib, + 'compile_translations': compile_translations, + 'sdist': eo_sdist}, +) diff --git a/src/authentic2_beid/__init__.py b/src/authentic2_beid/__init__.py new file mode 100644 index 0000000..a1acb9e --- /dev/null +++ b/src/authentic2_beid/__init__.py @@ -0,0 +1,20 @@ +__version__ = '0.0.1' + +class Plugin(object): + + def get_before_urls(self): + from django.conf.urls import url, patterns, include + return patterns('', url('accounts/beid/', include(__name__ + '.urls'))) + + def get_apps(self): + return [__name__] + + def get_authentication_backends(self): + return ('authentic2_beid.backends.BeIDBackend',) + + + def get_auth_frontends(self): + return ('authentic2_beid.frontends.BeIDFrontend',) + + def get_after_middleware(self): + return ('authentic2.auth2_auth.auth2_ssl.middleware.SSLAuthMiddleware',) diff --git a/src/authentic2_beid/backends.py b/src/authentic2_beid/backends.py new file mode 100644 index 0000000..7941bcc --- /dev/null +++ b/src/authentic2_beid/backends.py @@ -0,0 +1,9 @@ +from authentic2.auth2_auth.auth2_ssl.backends import SSLBackend +from authentic2.auth2_auth.auth2_ssl import util + + +class BeIDBackend(SSLBackend): + + def build_username(ssl_info): + dn = dict(util.explode_dn(ssl_info.subject_dn)) + return dn.get('serialNumber') diff --git a/src/authentic2_beid/frontends.py b/src/authentic2_beid/frontends.py new file mode 100644 index 0000000..8f40d32 --- /dev/null +++ b/src/authentic2_beid/frontends.py @@ -0,0 +1,31 @@ +from django.utils.translation import gettext_noop +from django.contrib.auth import authenticate +from django.shortcuts import render +import django.forms + +from authentic2.utils import redirect_to_login + +from . import views + +class BeIDFrontend(object): + + def enabled(self): + return True + + def id(self): + return 'eid' + + def name(self): + return gettext_noop('Belgian eID card') + + def post(self, request, form, nonce, next_url): + return redirect_to_login(request, login_url='beid_signin',) + + def form(self): + return django.forms.Form + + def template(self): + return 'beid/login_form.html' + + def profile(self, request, *args, **kwargs): + return views.profile(request, *args, **kwargs) diff --git a/src/authentic2_beid/templates/beid/login_form.html b/src/authentic2_beid/templates/beid/login_form.html new file mode 100644 index 0000000..a61fe06 --- /dev/null +++ b/src/authentic2_beid/templates/beid/login_form.html @@ -0,0 +1,19 @@ +{% load i18n %} + +
+

+{% trans "Use your Belgian identity card to log in." %} +

+

+ {% trans "Please connect the card reader and put your identity card in" %} +

+ +
+{% csrf_token %} +{{ form.as_p }} + +{% if cancel %} + +{% endif %} +
+
diff --git a/src/authentic2_beid/templates/beid/profile.html b/src/authentic2_beid/templates/beid/profile.html new file mode 100644 index 0000000..95cbfb9 --- /dev/null +++ b/src/authentic2_beid/templates/beid/profile.html @@ -0,0 +1,33 @@ +{% load i18n %} +

+ {% trans "Belgian eID card" %} +

+ +
+ +

+

+ + + +
+

+
diff --git a/src/authentic2_beid/urls.py b/src/authentic2_beid/urls.py new file mode 100644 index 0000000..7561d81 --- /dev/null +++ b/src/authentic2_beid/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls import patterns, url + +from .views import * + +urlpatterns = patterns('', + url(r'^$', + handle_authentication, + name='beid_signin'), + url(r'^add_beid/', add_beid, name='add_beid'), + url(r'^delete_beid/(?P\d+)/$', + delete_beid, name='delete_beid') +) diff --git a/src/authentic2_beid/views.py b/src/authentic2_beid/views.py new file mode 100644 index 0000000..cc0b1db --- /dev/null +++ b/src/authentic2_beid/views.py @@ -0,0 +1,59 @@ +import logging + +from django.utils.translation import ugettext as _ +from django.template.loader import render_to_string +from django.template import RequestContext +from django.contrib.auth import authenticate, login +from django.contrib import messages + +from authentic2.auth2_auth.auth2_ssl import models, util +from authentic2.utils import continue_to_next_url, redirect + +from .backends import BeIDBackend + +logger = logging.getLogger(__name__) + + +def handle_authentication(request, *args, **kwargs): + ssl_info = util.SSLInfo(request) + logger.debug('received SSL info: %s', ssl_info) + user = authenticate(ssl_info=ssl_info) + logger.debug('got user: %s', user) + if not request.user.is_authenticated(): + login(request, user) + return continue_to_next_url(request) + +def add_beid(request): + if request.user.is_authenticated: + ssl_info = util.SSLInfo(request) + if BeIDBackend().link_user(ssl_info, request.user): + logger.info('Successful linking of the SSL ' + 'Certificate to an account, redirection to %s' % next_url) + messages.info(request, _('BeID card successfully linked to your account')) + else: + logger.error('eid linking failed') + messages.error(request, _('BeID linking failed. Internal server error.')) + else: + return redirect(request, 'account_management', + fragment='a2-beid-certificate-profile') + + +def profile(request, template_name='beid/profile.html', *args, **kwargs): + context_instance = kwargs.pop('context_instance', None) or \ + RequestContext(request) + certificates = models.ClientCertificate.objects.filter(user=request.user) + ctx = {'certificates': certificates} + return render_to_string(template_name, ctx, + context_instance=context_instance) + +def delete_beid(request, certificate_pk): + try: + beid = models.ClientCertificate.objects.get(pk=certificate_pk) + beid.delete() + logger.info('client certificate %s deleted', beid) + messages.info(request, _('Your BeID card informations are successfully deleted')) + except models.ClientCertificate.DoesNotExist: + logger.info('no client certificate %s', certificate_pk) + messages.error(request, _('No BeID card associated to this account')) + return redirect(request, 'account_management', + fragment='a2-beid-certificate-profile')