first commit

This commit is contained in:
Benjamin Dauvergne 2014-08-13 18:05:14 +02:00
commit 3778e18202
20 changed files with 307 additions and 0 deletions

2
COPYING Normal file
View File

@ -0,0 +1,2 @@
authentic2-auth-kerberos is entirely under the copyright of Entr'ouvert and
distributed under the license AGPLv3 or later.

3
MANIFEST.in Normal file
View File

@ -0,0 +1,3 @@
include COPYING
recursive-include src/authentic2_auth_kerberos/templates *.html
recursive-include src/authentic2_auth_kerberos/static *.js *.css *.png

35
README Normal file
View File

@ -0,0 +1,35 @@
Authentic2 Auth Kerberos
==========================
It provides basic implementation of the HTTP Negotiate authentication mechanism
and autologin support using Javascript and a cookie.
The django-kerberos_ project is used as a basis for this plugin.
Install
-------
You just have to install the package in your virtualenv and relaunch, it will
be automatically loaded by authentic2.
You must define the KRB5_KTNAME environment to the path of a keytab file
containing the key for your service principal. See django-kerberos_
documentation for details.
Settings
--------
- A2_AUTH_KERBEROS_ENABLED: enable the authentication module, default is True.
- A2_AUTH_KERBEROS_CREATE_USER: whether to create users for Kerberos
principals, default is True.
- A2_AUTH_KERBEROS_REALM: default reaml to attribute to user, default is None.
If not None, the Kerberos realm is replaced by this one. It's incompatible
with support for multiple realms.
.. _django-kerberos: https://pypi.python.org/pypi/django-kerberos
Roadmap
-------
- LDAP support (think AD support)
- Linking Kerberos principals to existing users

67
setup.py Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/python
from setuptools import setup, find_packages
import os
def get_version():
import glob
import re
import os
version = None
for d in glob.glob('src/*'):
if not os.path.isdir(d):
continue
module_file = os.path.join(d, '__init__.py')
if not os.path.exists(module_file):
continue
for v in re.findall("""__version__ *= *['"](.*)['"]""",
open(module_file).read()):
assert version is None
version = v
if version:
break
assert version is not None
if os.path.exists('.git'):
import subprocess
p = subprocess.Popen(['git','describe','--dirty','--match=v*'],
stdout=subprocess.PIPE)
result = p.communicate()[0]
assert p.returncode == 0, 'git returned non-zero'
new_version = result.split()[0][1:]
assert new_version.split('-')[0] == version, '__version__ must match the last git annotated tag'
version = new_version.replace('-', '.')
return version
README = file(os.path.join(
os.path.dirname(__file__),
'README')).read()
setup(name='authentic2-auth-kerberos',
version=get_version(),
license='AGPLv3',
description='Authentic2 Auth Kerberos',
long_description=README,
author="Entr'ouvert",
author_email="info@entrouvert.com",
packages=find_packages('src'),
package_dir={
'': 'src',
},
package_data={
'authentic2_auth_kerberos': [
'templates/authentic2_auth_kerberos/*.html',
'static/authentic2_auth_kerberos/js/*.js',
'static/authentic2_auth_kerberos/css/*.css',
'static/authentic2_auth_kerberos/img/*.png',
],
},
install_requires=[
'authentic2',
'django-kerberos',
],
entry_points={
'authentic2.plugin': [
'authentic2-auth-kerberos = authentic2_auth_kerberos:Plugin',
],
},
)

View File

@ -0,0 +1,15 @@
__version__ = '1.0.0'
class Plugin(object):
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def get_apps(self):
return [__name__, 'django_kerberos']
def get_authentication_backends(self):
return ['authentic2_auth_kerberos.backends.A2KerberosBackend']
def get_auth_frontends(self):
return ['authentic2_auth_kerberos.auth_frontends.KerberosFrontend']

View File

View File

@ -0,0 +1,25 @@
class AppSettings(object):
__DEFAULTS = {
'ENABLED': True,
'CREATE_USER': True,
'REALM': None,
}
def __init__(self, prefix):
self.prefix = prefix
def _setting(self, name, dflt):
from django.conf import settings
return getattr(settings, self.prefix+name, dflt)
def __getattr__(self, name):
if name not in self.__DEFAULTS:
raise AttributeError(name)
return self._setting(name, self.__DEFAULTS[name])
# Ugly? Guido recommends this himself ...
# http://mail.python.org/pipermail/python-ideas/2012-May/014969.html
import sys
app_settings = AppSettings('A2_AUTH_KERBEROS_')
app_settings.__name__ = __name__
sys.modules[__name__] = app_settings

View File

@ -0,0 +1,23 @@
from django.utils.translation import gettext_noop
from django import forms
from . import app_settings, utils
class KerberosFrontend(object):
def enabled(self):
return app_settings.ENABLED
def name(self):
return gettext_noop('Kerberos')
def id(self):
return 'kerberos'
def form(self):
return forms.Form
def post(self, request, form, nonce, next):
return utils.redirect_next(request, 'kerberos-login', nonce=nonce)
def template(self):
return 'authentic2_auth_kerberos/login.html'

View File

@ -0,0 +1,28 @@
from django_kerberos.backends import KerberosBackend
import logging
from . import app_settings
class A2KerberosBackend(KerberosBackend):
def __init__(self):
super(A2KerberosBackend, self).__init__()
self.logger = logging.getLogger(__name__)
def username_from_principal(self, principal):
if app_settings.REALM:
username, domain = principal.rsplit('@', 1)
return '{0}@{1}'.format(username, app_settings.REALM)
return super(A2KerberosBackend, self).username_from_principal(principal)
def should_create_user(self):
return app_settings.CREATE_USER
def provision_user(self, principal, user):
pass
def authenticate(self, principal=None, **kwargs):
if not app_settings.ENABLED:
return
return super(A2KerberosBackend, self).authenticate(principal=principal,
**kwargs)

View File

@ -0,0 +1,11 @@
from django.utils.translation import ugettext_lazy as _
from admin_tools.dashboard import modules
def get_admin_modules():
'''Show Client model in authentic2 admin'''
model_list = modules.ModelList(_('Authentic2 Auth Kerberos'),
models=('authentic2_auth_kerberos.models.*',))
return (model_list,)

View File

@ -0,0 +1,14 @@
import functools
from django.http import Http404
from . import app_settings
def plugin_enabled(view):
'''If plugin is not enabled, return 404'''
@functools.wraps(view)
def wrapper(*args, **kwargs):
if not app_settings.ENABLED:
raise Http404
return view(*args, **kwargs)
return wrapper

View File

@ -0,0 +1,3 @@
from django import forms

View File

View File

@ -0,0 +1 @@
{% comment %}placeholder{% endcomment %}

View File

@ -0,0 +1,11 @@
{% load i18n %}
<div>
{% include "django_kerberos/autologin.html" %}
<form method="post">
{% csrf_token %}
<input type="submit" name="{{ submit_name }}" value="{% trans "Login" %}"/>
{% if cancel %}
<input type="submit" name="cancel" value="{% trans 'Cancel' %}"/>
{% endif %}
</form>
</div>

View File

@ -0,0 +1,7 @@
from django.conf.urls import patterns, url, include
from . import views
urlpatterns = patterns('',
url(r'^accounts/kerberos/login/$', views.login, name='kerberos-login'),
)

View File

@ -0,0 +1,28 @@
import urlparse
import urllib
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
def redirect_next(request, url, **kwargs):
'''Redirect to URL, add or replace parameters with kwargs
You can use relative or fully qualifier, or view names. You can provide
view args and kwargs using named argument args and kwargs.
'''
# specialize on the next url parameter as it is so frequent
if 'next' in request.REQUEST:
next_url = request.REQUEST.get('next')
kwargs['next'] = next_url
if not url.startswith('/') and not url.startswith('http:') and not url.startswith('https:'):
view_args = kwargs.pop('args', None)
view_kwargs = kwargs.pop('kwargs', None)
url = reverse(url, args=view_args, kwargs=view_kwargs)
parsed = urlparse.urlparse(url)
params = urlparse.parse_qs(parsed.query)
for key in kwargs.keys():
if kwargs[key] is None:
del kwargs[key]
params.update(kwargs)
url = urlparse.urlunparse(parsed[:4] + (urllib.urlencode(params),) + parsed[5:])
return HttpResponseRedirect(url)

View File

@ -0,0 +1,34 @@
import logging
from django_kerberos.views import NegotiateView
from authentic2.models import AuthenticationEvent
__ALL_ = [ 'login' ]
class A2NegotiateView(NegotiateView):
def __init__(self, *args, **kwargs):
self.logger = logging.getLogger(__name__)
super(A2NegotiateView, self).__init__(*args, **kwargs)
def user_found(self, request, user, *args, **kwargs):
response = super(A2NegotiateView, self).user_found(request, user,
*args, **kwargs)
nonce = request.REQUEST.get('nonce', '')
if nonce:
self.logger.info('logged in %r as %r (nonce %r)', self.principal,
user, nonce)
else:
self.logger.info('logged in %r as %r', self.principal, user)
AuthenticationEvent.objects.create(
who=unicode(user),
how='kerberos',
nonce=nonce)
return response
def user_not_found(self, request, user, *args, **kwargs):
self.logger.debug('unable to log in %r', self.principal)
return super(A2NegotiateView, self).user_not_found(request, user,
*args, **kwargs)
login = A2NegotiateView.as_view()