add Python3 and django 1.11 support (fixes #30321)
Also remove the sample application.
This commit is contained in:
parent
61a8167e9e
commit
e8a1a7221c
|
@ -0,0 +1,50 @@
|
||||||
|
@Library('eo-jenkins-lib@master') import eo.Utils
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
options { disableConcurrentBuilds() }
|
||||||
|
stages {
|
||||||
|
stage('Unit Tests') {
|
||||||
|
steps {
|
||||||
|
sh """
|
||||||
|
rm -rf htmlcov* .coverage* coverage* junit*.xml
|
||||||
|
rm -rf venv
|
||||||
|
virtualenv -p python3 venv
|
||||||
|
. venv/bin/activate
|
||||||
|
pip install tox"""
|
||||||
|
sh './venv/bin/tox -rv'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
script {
|
||||||
|
utils = new Utils()
|
||||||
|
utils.publish_coverage('coverage.xml')
|
||||||
|
utils.publish_coverage_native('index.html')
|
||||||
|
utils.publish_pylint('pylint.out')
|
||||||
|
}
|
||||||
|
junit 'junit*.xml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Packaging') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
if (env.JOB_NAME == 'django-kerberos' && env.GIT_BRANCH == 'origin/master') {
|
||||||
|
sh 'sudo -H -u eobuilder /usr/local/bin/eobuilder django-kerberos'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
script {
|
||||||
|
utils = new Utils()
|
||||||
|
utils.mail_notify(currentBuild, env, 'admin+jenkins-django-kerberos@entrouvert.com')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success {
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
README
2
README
|
@ -3,6 +3,8 @@ Kerberos authentication for Django
|
||||||
|
|
||||||
Provide Kerberos authentication to Django applications.
|
Provide Kerberos authentication to Django applications.
|
||||||
|
|
||||||
|
Python 2 and 3, Django >1.8 are supported.
|
||||||
|
|
||||||
Basic usage
|
Basic usage
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Source: python-django-kerberos
|
Source: python-django-kerberos
|
||||||
Maintainer: Benjamin Dauvergne <info@entrouvert.com>
|
Maintainer: Benjamin Dauvergne <bdauvergne@entrouvert.com>
|
||||||
Section: python
|
Section: python
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6), debhelper (>= 7.4.3),
|
Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6), debhelper (>= 7.4.3),
|
||||||
|
@ -10,7 +10,16 @@ X-Python-Version: >= 2.6
|
||||||
Package: python-django-kerberos
|
Package: python-django-kerberos
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${misc:Depends}, ${python:Depends},
|
Depends: ${misc:Depends}, ${python:Depends},
|
||||||
python-django (>= 1.5),
|
python-six,
|
||||||
|
python-django (>= 1.8),
|
||||||
python-kerberos
|
python-kerberos
|
||||||
Description: Kerberos authentication frontend for Authentic2
|
Description: Kerberos authentication frontend for Authentic2
|
||||||
|
|
||||||
|
Package: python3-django-kerberos
|
||||||
|
Architecture: all
|
||||||
|
Depends: ${misc:Depends}, ${python3:Depends},
|
||||||
|
python3-six,
|
||||||
|
python3-django (>= 1.8),
|
||||||
|
python3-kerberos
|
||||||
|
Description: Kerberos authentication frontend for Authentic2
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
env
|
||||||
|
if [ -f /var/lib/jenkins/pylint.django.rc ]; then
|
||||||
|
PYLINT_RC=/var/lib/jenkins/pylint.django.rc
|
||||||
|
elif [ -f pylint.django.rc ]; then
|
||||||
|
PYLINT_RC=pylint.django.rc
|
||||||
|
else
|
||||||
|
echo No pylint RC found
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
pylint -f parseable --rcfile ${PYLINT_RC} "$@" | tee pylint.out || /bin/true
|
|
@ -1 +0,0 @@
|
||||||
Rename config.py.example to config.py, and update default settings.
|
|
|
@ -1,3 +0,0 @@
|
||||||
KERBEROS_SERVICE_PRINCIPAL = 'HTTP/fenouil.entrouvert.org@ENTROUVERT.ORG'
|
|
||||||
KERBEROS_DEFAULT_REALM = 'ENTROUVERT.ORG'
|
|
||||||
KERBEROS_BACKEND_ADMIN_REGEXP = '^.*/admin@.*$'
|
|
|
@ -1,10 +0,0 @@
|
||||||
#!/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)
|
|
|
@ -1,98 +0,0 @@
|
||||||
"""
|
|
||||||
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',
|
|
||||||
'django_kerberos.backends.KerberosPasswordBackend',
|
|
||||||
)
|
|
||||||
|
|
||||||
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'
|
|
||||||
LOGIN_REDIRECT_URL = '/'
|
|
||||||
|
|
||||||
KERBEROS_BACKEND_CREATE = True
|
|
||||||
KERBEROS_BACKEND_ADMIN_REGEXP = r'^.*/admin$'
|
|
||||||
|
|
||||||
execfile('config.py', globals())
|
|
|
@ -1,7 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
{% block content %}
|
|
||||||
{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,14 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% include "django_kerberos/autologin.html" %}
|
|
||||||
{% if user.is_authenticated %}
|
|
||||||
<p>Hello {{ user }}</p>
|
|
||||||
{% if user.is_superuser %}
|
|
||||||
<p><a href="/admin">Admin</a></p>
|
|
||||||
{% endif %}
|
|
||||||
<p><a href="{% url "logout" %}?next=/">Logout</a></p>
|
|
||||||
{% else %}
|
|
||||||
<p>Please <a href="{% url "kerberos-login" %}">login with kerberos</a> or with your <a href="{% url "login" %}">password</a></p>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
|
@ -1,9 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.as_p }}
|
|
||||||
<button>Login</button>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,16 +0,0 @@
|
||||||
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_kerberos.urls')),
|
|
||||||
url('^accounts/', include('django.contrib.auth.urls')),
|
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
|
||||||
)
|
|
|
@ -1,4 +0,0 @@
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
def home(request):
|
|
||||||
return render(request, 'index.html')
|
|
|
@ -1,14 +0,0 @@
|
||||||
"""
|
|
||||||
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()
|
|
27
setup.py
27
setup.py
|
@ -9,7 +9,7 @@ from setuptools.command.sdist import sdist
|
||||||
|
|
||||||
class eo_sdist(sdist):
|
class eo_sdist(sdist):
|
||||||
def run(self):
|
def run(self):
|
||||||
print "creating VERSION file"
|
print("creating VERSION file")
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
os.remove('VERSION')
|
os.remove('VERSION')
|
||||||
version = get_version()
|
version = get_version()
|
||||||
|
@ -17,35 +17,41 @@ class eo_sdist(sdist):
|
||||||
version_file.write(version)
|
version_file.write(version)
|
||||||
version_file.close()
|
version_file.close()
|
||||||
sdist.run(self)
|
sdist.run(self)
|
||||||
print "removing VERSION file"
|
print("removing VERSION file")
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
os.remove('VERSION')
|
os.remove('VERSION')
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
'''Use the VERSION, if absent generates a version with git describe, if not
|
'''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.
|
tag exists, take 0.0- and add the length of the commit log.
|
||||||
'''
|
'''
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
with open('VERSION', 'r') as v:
|
with open('VERSION', 'r') as v:
|
||||||
return v.read()
|
return v.read()
|
||||||
if os.path.exists('.git'):
|
if os.path.exists('.git'):
|
||||||
p = subprocess.Popen(['git', 'describe', '--dirty', '--match=v*'], stdout=subprocess.PIPE,
|
p = subprocess.Popen(['git', 'describe', '--dirty=.dirty','--match=v*'], stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
result = p.communicate()[0]
|
result = p.communicate()[0]
|
||||||
if p.returncode == 0:
|
if p.returncode == 0:
|
||||||
result = result.split()[0][1:]
|
result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v
|
||||||
|
if '-' in result: # not a tagged version
|
||||||
|
real_number, commit_count, commit_hash = result.split('-', 2)
|
||||||
|
version = '%s.post%s+%s' % (real_number, commit_count, commit_hash)
|
||||||
|
else:
|
||||||
|
version = result
|
||||||
|
return version
|
||||||
else:
|
else:
|
||||||
result = '0.0.0-%s' % len(subprocess.check_output(
|
return '0.0.post%s' % len(
|
||||||
['git', 'rev-list', 'HEAD']).splitlines())
|
subprocess.check_output(
|
||||||
return result.replace('-', '.').replace('.g', '+g')
|
['git', 'rev-list', 'HEAD']).splitlines())
|
||||||
return '0.0.0'
|
return '0.0'
|
||||||
|
|
||||||
setup(name="django-kerberos",
|
setup(name="django-kerberos",
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
license="AGPLv3 or later",
|
license="AGPLv3 or later",
|
||||||
description="Kerberos authentication for Django",
|
description="Kerberos authentication for Django",
|
||||||
long_description=file('README').read(),
|
long_description=open('README').read(),
|
||||||
url="http://dev.entrouvert.org/projects/authentic/",
|
url="http://dev.entrouvert.org/projects/authentic/",
|
||||||
author="Entr'ouvert",
|
author="Entr'ouvert",
|
||||||
author_email="info@entrouvert.org",
|
author_email="info@entrouvert.org",
|
||||||
|
@ -53,6 +59,7 @@ setup(name="django-kerberos",
|
||||||
maintainer_email="bdauvergne@entrouvert.com",
|
maintainer_email="bdauvergne@entrouvert.com",
|
||||||
packages=find_packages('src'),
|
packages=find_packages('src'),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
'six',
|
||||||
'django>1.5',
|
'django>1.5',
|
||||||
'pykerberos',
|
'pykerberos',
|
||||||
],
|
],
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from . import app_settings
|
import six
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
|
@ -27,6 +27,8 @@ from django.contrib.auth.backends import ModelBackend
|
||||||
|
|
||||||
import kerberos
|
import kerberos
|
||||||
|
|
||||||
|
from . import app_settings
|
||||||
|
|
||||||
|
|
||||||
class KerberosBackend(ModelBackend):
|
class KerberosBackend(ModelBackend):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -74,7 +76,7 @@ class KerberosBackend(ModelBackend):
|
||||||
self.provision_user(principal, user)
|
self.provision_user(principal, user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def authenticate(self, principal=None, **kwargs):
|
def authenticate(self, request=None, principal=None):
|
||||||
if principal and self.authorize_principal(principal):
|
if principal and self.authorize_principal(principal):
|
||||||
return self.lookup_user(principal)
|
return self.lookup_user(principal)
|
||||||
|
|
||||||
|
@ -102,22 +104,26 @@ class KerberosPasswordBackend(KerberosBackend):
|
||||||
' set')
|
' set')
|
||||||
return app_settings.SERVICE_PRINCIPAL
|
return app_settings.SERVICE_PRINCIPAL
|
||||||
|
|
||||||
def authenticate(self, username=None, password=None, **kwargs):
|
def authenticate(self, request=None, username=None, password=None):
|
||||||
'''Verify username and password using Kerberos'''
|
'''Verify username and password using Kerberos'''
|
||||||
if not username:
|
if not username:
|
||||||
return
|
return
|
||||||
|
|
||||||
principal = force_bytes(self.principal_from_username(username))
|
kerb_principal = principal = self.principal_from_username(username)
|
||||||
password = force_bytes(password)
|
kerb_password = password
|
||||||
|
|
||||||
|
if six.PY2:
|
||||||
|
kerb_principal = force_bytes(kerb_principal)
|
||||||
|
kerb_password = force_bytes(kerb_principal)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not kerberos.checkPassword(principal, password,
|
if not kerberos.checkPassword(kerb_principal, kerb_password,
|
||||||
self.service_principal(),
|
self.service_principal(),
|
||||||
self.default_realm()):
|
self.default_realm()):
|
||||||
return
|
return
|
||||||
except kerberos.KrbError as e:
|
except kerberos.KrbError as e:
|
||||||
logging.getLogger(__name__).error(
|
logging.getLogger(__name__).error(
|
||||||
'password validation forprincipal %r failed %s', principal, e)
|
'password validation for principal %r failed %s', principal, e)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if principal and self.authorize_principal(principal):
|
if principal and self.authorize_principal(principal):
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = [
|
||||||
url(r'^login/$', views.login, name='kerberos-login'),
|
url(r'^login/$', views.login, name='kerberos-login'),
|
||||||
)
|
]
|
||||||
|
|
|
@ -52,7 +52,7 @@ class NegotiateView(View):
|
||||||
'''Do something with the principal we received'''
|
'''Do something with the principal we received'''
|
||||||
self.logger.info(u'got ticket for principal %s', self.principal)
|
self.logger.info(u'got ticket for principal %s', self.principal)
|
||||||
user = authenticate(principal=self.principal)
|
user = authenticate(principal=self.principal)
|
||||||
next_url = request.REQUEST.get(self.NEXT_URL_FIELD) or settings.LOGIN_REDIRECT_URL
|
next_url = request.POST.get(self.NEXT_URL_FIELD) or request.GET.get(self.NEXT_URL_FIELD) or settings.LOGIN_REDIRECT_URL
|
||||||
if user:
|
if user:
|
||||||
self.login_user(request, user)
|
self.login_user(request, user)
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
|
@ -88,8 +88,8 @@ class NegotiateView(View):
|
||||||
# ensure context is finalized
|
# ensure context is finalized
|
||||||
try:
|
try:
|
||||||
if result != 1:
|
if result != 1:
|
||||||
self.logger.warning(u'authGSSServerInit result is non-zero: %s', result)
|
self.logger.warning(u'authGSSServerInit result is non-one: %s', result)
|
||||||
details = u'authGSSServerInit result is non-zero: %s' % result
|
details = u'authGSSServerInit result is non-one: %s' % result
|
||||||
return TemplateResponse(request, self.error_template_name,
|
return TemplateResponse(request, self.error_template_name,
|
||||||
context={'details': details}, status=500)
|
context={'details': details}, status=500)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,5 +1,42 @@
|
||||||
|
import django
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if django.VERSION < (1, 10):
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.http.ConditionalGetMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'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',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
|
@ -9,3 +46,8 @@ INSTALLED_APPS = (
|
||||||
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), 'templates'),)
|
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), 'templates'),)
|
||||||
ROOT_URLCONF = 'django_kerberos.urls'
|
ROOT_URLCONF = 'django_kerberos.urls'
|
||||||
SECRET_KEY = 'xxx'
|
SECRET_KEY = 'xxx'
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'django_kerberos.backends.KerberosBackend',
|
||||||
|
'django_kerberos.backends.KerberosPasswordBackend',
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import logging
|
||||||
import pytest
|
import pytest
|
||||||
|
import json
|
||||||
|
|
||||||
import kerberos
|
import kerberos
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -11,7 +14,8 @@ def kerberos_mock(request, mocker):
|
||||||
'kerberos.authGSSServerStep',
|
'kerberos.authGSSServerStep',
|
||||||
'kerberos.authGSSServerResponse',
|
'kerberos.authGSSServerResponse',
|
||||||
'kerberos.authGSSServerUserName',
|
'kerberos.authGSSServerUserName',
|
||||||
'kerberos.authGSSServerClean'
|
'kerberos.authGSSServerClean',
|
||||||
|
'kerberos.checkPassword'
|
||||||
)
|
)
|
||||||
for name in d:
|
for name in d:
|
||||||
if hasattr(request, 'param') and name in request.param:
|
if hasattr(request, 'param') and name in request.param:
|
||||||
|
@ -29,9 +33,143 @@ def test_login_no_header(client, settings, kerberos_mock):
|
||||||
@pytest.mark.parametrize('kerberos_mock', ['kerberos.authGSSServerInit'], indirect=True)
|
@pytest.mark.parametrize('kerberos_mock', ['kerberos.authGSSServerInit'], indirect=True)
|
||||||
def test_login_missing_keytab(client, settings, kerberos_mock, caplog):
|
def test_login_missing_keytab(client, settings, kerberos_mock, caplog):
|
||||||
resp = client.get('/login/', HTTP_AUTHORIZATION='Negotiate coin')
|
resp = client.get('/login/', HTTP_AUTHORIZATION='Negotiate coin')
|
||||||
for key, mock in kerberos_mock.iteritems():
|
for key, mock in kerberos_mock.items():
|
||||||
assert mock.call_count == 0
|
assert mock.call_count == 0
|
||||||
assert 'keytab problem' in resp.content
|
assert b'keytab problem' in resp.content
|
||||||
assert 'keytab problem' in caplog.text()
|
assert 'keytab problem' in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_login(client, db, settings, kerberos_mock, caplog):
|
||||||
|
caplog.set_level(logging.INFO)
|
||||||
|
kerberos_mock['kerberos.authGSSServerInit'].side_effect = kerberos.KrbError('coin')
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 500
|
||||||
|
assert b'exception during authGSSServerInit' in response.content
|
||||||
|
assert 'exception during authGSSServerInit' in caplog.text
|
||||||
|
assert b'coin' in response.content
|
||||||
|
kerberos_mock['kerberos.authGSSServerInit'].side_effect = None
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
kerberos_mock['kerberos.authGSSServerInit'].return_value = 0, None
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 500
|
||||||
|
assert b'authGSSServerInit result is non-one' in response.content
|
||||||
|
assert 'authGSSServerInit result is non-one' in caplog.text
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
kerberos_mock['kerberos.authGSSServerInit'].return_value = 1, None
|
||||||
|
kerberos_mock['kerberos.authGSSServerStep'].side_effect = kerberos.KrbError('coin')
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 500
|
||||||
|
assert b'exception during authGSSServerStep' in response.content
|
||||||
|
assert 'exception during authGSSServerStep' in caplog.text
|
||||||
|
assert b'coin' in response.content
|
||||||
|
kerberos_mock['kerberos.authGSSServerStep'].side_effect = None
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
kerberos_mock['kerberos.authGSSServerStep'].return_value = 0
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
kerberos_mock['kerberos.authGSSServerStep'].return_value = 1
|
||||||
|
kerberos_mock['kerberos.authGSSServerUserName'].side_effect = kerberos.KrbError('coin')
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 500
|
||||||
|
assert b'exception during authGSSServerUserName' in response.content
|
||||||
|
assert 'exception during authGSSServerUserName' in caplog.text
|
||||||
|
assert b'coin' in response.content
|
||||||
|
kerberos_mock['kerberos.authGSSServerUserName'].side_effect = None
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
kerberos_mock['kerberos.authGSSServerUserName'].return_value = 'john.doe@EXAMPLE.COM'
|
||||||
|
kerberos_mock['kerberos.authGSSServerResponse'].return_value = 'yyyy'
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert 'principal john.doe@EXAMPLE.COM has no local user' in caplog.text
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
user = User.objects.create(username='john.doe@example.com')
|
||||||
|
assert '_auth_user_id' not in client.session
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response['WWW-Authenticate'] == 'Negotiate yyyy'
|
||||||
|
assert 'principal john.doe@EXAMPLE.COM has no local user' not in caplog.text
|
||||||
|
assert client.session['_auth_user_id'] == str(user.id)
|
||||||
|
client.logout()
|
||||||
|
user.delete()
|
||||||
|
assert User.objects.count() == 0
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
settings.KERBEROS_BACKEND_CREATE = True
|
||||||
|
assert '_auth_user_id' not in client.session
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert 'principal john.doe@EXAMPLE.COM has no local user' not in caplog.text
|
||||||
|
assert User.objects.count() == 1
|
||||||
|
user = User.objects.get()
|
||||||
|
assert not user.is_staff
|
||||||
|
assert not user.is_superuser
|
||||||
|
assert client.session['_auth_user_id'] == str(user.id)
|
||||||
|
assert 'got ticket for principal john.doe@EXAMPLE.COM' in caplog.text
|
||||||
|
client.logout()
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
settings.KERBEROS_BACKEND_ADMIN_REGEXP = 'john.doe'
|
||||||
|
assert '_auth_user_id' not in client.session
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx')
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert 'principal john.doe@EXAMPLE.COM has no local user' not in caplog.text
|
||||||
|
assert User.objects.count() == 1
|
||||||
|
user = User.objects.get()
|
||||||
|
assert user.is_staff
|
||||||
|
assert user.is_superuser
|
||||||
|
assert client.session['_auth_user_id'] == str(user.id)
|
||||||
|
assert 'got ticket for principal john.doe@EXAMPLE.COM' in caplog.text
|
||||||
|
assert 'giving superuser power to principal \'john.doe@EXAMPLE.COM\'' in caplog.text
|
||||||
|
client.logout()
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
assert '_auth_user_id' not in client.session
|
||||||
|
response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxxx', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert json.loads(response.content.decode('ascii')) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_password_backend(db, settings, kerberos_mock, caplog):
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
|
|
||||||
|
settings.KERBEROS_DEFAULT_REALM = 'EXAMPLE.COM'
|
||||||
|
settings.KERBEROS_SERVICE_PRINCIPAL = 'HTTP/SERVICE.EXAMPLE.COM@EXAMPLE.COM'
|
||||||
|
|
||||||
|
m = kerberos_mock['kerberos.checkPassword']
|
||||||
|
m.return_value = False
|
||||||
|
assert authenticate(username='john.doe', password='password') is None
|
||||||
|
assert User.objects.count() == 0
|
||||||
|
|
||||||
|
m.return_value = True
|
||||||
|
assert authenticate(username='john.doe', password='password') is None
|
||||||
|
assert User.objects.count() == 0
|
||||||
|
|
||||||
|
user = User.objects.create(username='john.doe@example.com')
|
||||||
|
assert authenticate(username='john.doe', password='password') == user
|
||||||
|
user.delete()
|
||||||
|
|
||||||
|
assert User.objects.count() == 0
|
||||||
|
settings.KERBEROS_BACKEND_CREATE = True
|
||||||
|
new_user = authenticate(username='john.doe', password='password')
|
||||||
|
assert new_user
|
||||||
|
assert User.objects.count() == 1
|
||||||
|
assert new_user.username == 'john.doe@example.com'
|
||||||
|
assert not new_user.has_usable_password()
|
||||||
|
|
||||||
|
settings.KERBEROS_KEEP_PASSWORD = True
|
||||||
|
new_user = authenticate(username='john.doe', password='password')
|
||||||
|
assert User.objects.count() == 1
|
||||||
|
assert new_user.username == 'john.doe@example.com'
|
||||||
|
assert new_user.has_usable_password()
|
||||||
|
assert new_user.check_password('password')
|
||||||
|
|
||||||
|
caplog.clear()
|
||||||
|
m.side_effect = kerberos.KrbError('coin')
|
||||||
|
assert authenticate(username='john.doe', password='password') is None
|
||||||
|
assert 'password validation for principal %r failed coin' % u'john.doe@EXAMPLE.COM' in caplog.text
|
||||||
|
|
48
tox.ini
48
tox.ini
|
@ -3,40 +3,38 @@
|
||||||
# test suite on all supported python versions. To use it, "pip install tox"
|
# test suite on all supported python versions. To use it, "pip install tox"
|
||||||
# and then run "tox" from this directory.
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
[testenv:coverage]
|
[tox]
|
||||||
|
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/django-kerberos/{env:BRANCH_NAME:}
|
||||||
|
envlist = py27-coverage-{dj18,dj111}-{pg,sqlite},py3-coverage-{dj18,dj111,dj20,djlast}-{pg,sqlite},pylint
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
whitelist_externals =
|
||||||
|
/bin/mv
|
||||||
|
/bin/rm
|
||||||
setenv =
|
setenv =
|
||||||
DJANGO_SETTINGS_MODULE=settings
|
DJANGO_SETTINGS_MODULE=settings
|
||||||
PYTHONPATH=tests
|
PYTHONPATH=tests
|
||||||
|
coverage: COVERAGE=--cov-branch --cov-append --cov=src/ --cov-report=html --cov-report=xml --cov-config .coveragerc
|
||||||
|
sqlite: DB_ENGINE=django.db.backends.sqlite3
|
||||||
|
pg: DB_ENGINE=django.db.backends.postgresql_psycopg2
|
||||||
usedevelop = true
|
usedevelop = true
|
||||||
deps =
|
deps =
|
||||||
pytest
|
dj18: django>1.8,<1.9
|
||||||
|
dj18: django-tables2<1.1
|
||||||
|
dj111: django<2.0
|
||||||
|
dj20: django<2.1
|
||||||
|
djlast: django
|
||||||
|
pg: psycopg2-binary
|
||||||
|
pytest<4.2
|
||||||
pytest-mock
|
pytest-mock
|
||||||
pytest-django
|
pytest-django
|
||||||
pytest-cov
|
pytest-cov
|
||||||
pytest-capturelog
|
|
||||||
commands =
|
commands =
|
||||||
py.test {posargs:--junit-xml=junit.xml --cov=src --cov-report xml --nomigrations tests}
|
py.test {env:COVERAGE:} --junit-xml=junit-{envname}.xml {posargs:tests}
|
||||||
|
|
||||||
[testenv:nocoverage]
|
[testenv:pylint]
|
||||||
setenv =
|
|
||||||
DJANGO_SETTINGS_MODULE=settings
|
|
||||||
PYTHONPATH=tests
|
|
||||||
deps =
|
deps =
|
||||||
pytest
|
pylint<1.8
|
||||||
pytest-mock
|
pylint-django<0.8.1
|
||||||
pytest-django
|
|
||||||
pytest-cov
|
|
||||||
pytest-capturelog
|
|
||||||
commands =
|
commands =
|
||||||
py.test {posargs:--nomigrations tests}
|
pylint: ./pylint.sh src/django_kerberos/
|
||||||
|
|
||||||
[testenv:package]
|
|
||||||
# eobuilder is not on pypi, too bad
|
|
||||||
deps = setuptools
|
|
||||||
pip<8
|
|
||||||
pyasn1
|
|
||||||
ndg-httpsclient
|
|
||||||
pyopenssl
|
|
||||||
commands =
|
|
||||||
pip install -U --find-links https://jenkins.entrouvert.org/packages/ eobuilder
|
|
||||||
sh -c "sudo -u eobuilder -E env HOME=/var/lib/eobuilder PATH=$PATH $VIRTUAL_ENV/bin/eobuilder-ctl -d wheezy,jessie {posargs:django-kerberos}"
|
|
||||||
|
|
Reference in New Issue