first commit

This commit is contained in:
Benjamin Dauvergne 2014-08-09 01:19:09 +02:00
commit cb05f9eb2a
19 changed files with 394 additions and 0 deletions

2
COPYING Normal file
View File

@ -0,0 +1,2 @@
cmsplugin-blurp 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 sample *.py *.html
recursive-include src/django_kerberos/templates *.html

55
README Normal file
View File

@ -0,0 +1,55 @@
Kerberos authentication for Django
==================================
Provide Kerberos authentication to Django applications.
Basic usage
===========
Add this to your project `urls.py`::
url('^accounts/kerberos/', include('django_auth_kerb.urls')),
And use the default authentication backend, by adding that to your `settings.py` file::
AUTHENTICATION_BACKENDS = (
'django_auth_kerberos.backends.KerberosBackend',
)
Settings
========
`KERBEROS_HOSTNAME`
-------------------
Hostname for retrieving the service key, the correspondig principal will be
`HTTP/{KERBEROS_HOSTNAME}@DEFAULT_REAML`, default is `None`. If `None` the hostname
from the request will be used.
`KERBEROS_KEYTAB`
-----------------
File path of the keytab containing the key for the service principal, default
is `None`. If `None` the default host keytab will be tried, which should fails
since it's usually only readable by root.
`KERBEROS_BACKEND_CREATE`
-------------------------
Whether to create user if no existing model can be found, default is `False`.
`KERBEROS_BACKEND_ADMIN_REGEXP`
-------------------------------
A regular expression that the principal must match to get superuser privileges,
default is `None`. A classic example could be `r'^.*/admin$'`.
Custom backend
==============
A custom authentication backend can be used, in this case the signature of the
authenticate method must be::
class CustomKerberosBackend(object):
def authenticate(self, principal=None):
pass

10
sample/manage.py Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sample.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View File

94
sample/sample/settings.py Normal file
View File

@ -0,0 +1,94 @@
"""
Django settings for sample project.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.6/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 's0xb*2mi0#pi48tri&x6cwr96k30mmdu%e6pa28_=n9^4eh-3='
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
TEMPLATE_DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_kerberos',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django_kerberos.backends.KerberosBackend',
)
ROOT_URLCONF = 'sample.urls'
WSGI_APPLICATION = 'sample.wsgi.application'
TEMPLATE_DIRS = ( os.path.join(BASE_DIR, 'sample', 'templates'), )
# Database
# https://docs.djangoproject.com/en/1.6/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.6/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.6/howto/static-files/
STATIC_URL = '/static/'
LOGIN_URL = 'kerberos-login'
KERBEROS_BACKEND_CREATE = True
KERBEROS_BACKEND_ADMIN_REGEXP = r'^.*/admin$'

View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
{% block content %}
{% endblock %}
</body>
</html>

16
sample/sample/urls.py Normal file
View File

@ -0,0 +1,16 @@
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
from . import views
urlpatterns = patterns('',
# Examples:
url(r'^$', views.home, name='home'),
# url(r'^blog/', include('blog.urls')),
url('^accounts/kerberos/', include('django_auth_kerberos.urls')),
url('^accounts/', include('django.contrib.auth.urls')),
url(r'^admin/', include(admin.site.urls)),
)

7
sample/sample/views.py Normal file
View File

@ -0,0 +1,7 @@
from django.contrib.auth.decorators import login_required
from django import http
@login_required
def home(request):
return http.HttpResponse(u'It worked ' + request.user.username + u'!', content_type='text/plain')

14
sample/sample/wsgi.py Normal file
View File

@ -0,0 +1,14 @@
"""
WSGI config for sample project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sample.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

62
setup.py Executable file
View File

@ -0,0 +1,62 @@
#! /usr/bin/env python
''' Setup script for django-kerberos
'''
from setuptools import setup, find_packages
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
setup(name="django-kerberos",
version=get_version(),
license="AGPLv3 or later",
description="",
long_description=file('README').read(),
url="http://dev.entrouvert.org/projects/authentic/",
author="Entr'ouvert",
author_email="info@entrouvert.org",
maintainer="Benjamin Dauvergne",
maintainer_email="bdauvergne@entrouvert.com",
packages=find_packages('src'),
install_requires=[
'django>1.5',
],
package_dir={
'': 'src',
},
package_data={
'django_kerberos': [
'templates/django_kerberos/*.html',
],
},
dependency_links=[],
)

View File

@ -0,0 +1 @@
__version__ = '1.0.0'

View File

@ -0,0 +1,20 @@
import sys
class AppSettings(object):
__PREFIX = 'KERBEROS_'
__DEFAULTS = {
'HOSTNAME': None,
'KEYTAB': None,
'BACKEND_CREATE': False,
'BACKEND_ADMIN_REGEXP': None,
}
def __getattr__(self, name):
from django.conf import settings
if name not in self.__DEFAULTS:
raise AttributeError
return getattr(settings, self.__PREFIX + name, self.__DEFAULTS[name])
app_settings = AppSettings()
app_settings.__name__ = __name__
sys.modules[__name__] = app_settings

View File

@ -0,0 +1,34 @@
import re
from . import app_settings
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
class KerberosBackend(ModelBackend):
def authenticate(self, principal=None):
'''Match principal username with Django user model username'''
if not principal:
return
User = get_user_model()
username = principal.split('@')[0]
username_field = getattr(User, 'USERNAME_FIELD', 'username')
kwargs = {username_field: username}
if app_settings.BACKEND_CREATE:
user, created = User.objects.get_or_create(**kwargs)
else:
try:
user = User.objects.get(**kwargs)
except User.DoesNotExist:
return
# basic authorization
if app_settings.BACKEND_ADMIN_REGEXP:
if re.match(app_settings.BACKEND_ADMIN_REGEXP, username):
if not user.is_staff or not user.is_superuser:
user.is_staff = True
user.is_superuser = True
user.save()
return user

View File

View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
An error occurred during initialization of the GSSAPI context, verify that a properly initialized keytab is accessible
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
Access unauthorized please refresh your ticket cache
{% endblock %}

View File

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

View File

@ -0,0 +1,52 @@
import kerberos
import os
from django import http
from django.template.response import TemplateResponse
from django.contrib.auth import authenticate, login as auth_login
from django.conf import settings
from . import app_settings
def www_authenticate(request):
response = TemplateResponse(request, 'django_kerberos/unauthorized.html', status=401)
response['WWW-Authenticate'] = 'Negotiate'
return response
def login(request):
'''Try to authenticate the user using SPNEGO and Kerberos'''
next_url = request.REQUEST.get('next') or settings.LOGIN_REDIRECT_URL
if app_settings.KEYTAB:
old = os.environ.get('KRB5_KTNAME')
os.environ['KRB5_KTNAME'] = app_settings.KEYTAB
try:
host = app_settings.HOSTNAME or request.get_host().split(':')[0]
service = 'HTTP@%s' % host
if 'HTTP_AUTHORIZATION' in request.META:
kind, authstr = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
print authstr
if kind == 'Negotiate':
result, context = kerberos.authGSSServerInit(service)
if result != 1:
return TemplateResponse(request, 'django_kerberos/error.html')
r = kerberos.authGSSServerStep(context, authstr)
if r == 1:
gssstring = kerberos.authGSSServerResponse(context)
else:
return www_authenticate(request)
principal = kerberos.authGSSServerUserName(context)
kerberos.authGSSServerClean(context)
user = authenticate(principal=principal)
if user:
auth_login(request, user)
response = http.HttpResponseRedirect(next_url)
response['WWW-Authenticate'] = 'Negotiate %s' % gssstring
return response
return www_authenticate(request)
finally:
if app_settings.KEYTAB:
if old:
os.environ['KRB5_KTNAME'] = old
else:
del os.environ['KRB5_KTNAME']