add new login/password backend using Kerberos, rename KERBEROS_HASHER_SERVICE_PRINCIPAL to KERBEROS_SERVICE_PRINCIPAL

This commit is contained in:
Benjamin Dauvergne 2014-08-09 20:05:37 +02:00
parent 7896944e30
commit 2cb06046b8
4 changed files with 84 additions and 9 deletions

26
README
View File

@ -44,13 +44,25 @@ Whether to create user if no existing model can be found, default is `False`.
A regular expression that the principal must match to get superuser privileges,
default is `None`. A classic example could be `r'^.*/admin$'`.
`KERBEROS_HASHER_SERVICE_PRINCIPAL`
`KERBEROS_SERVICE_PRINCIPAL`
-----------------------------------
The service principal to user when checking a password against the
The service principal to use when checking a password against the
KDC, you don't need the secret key for this principal, it should
just exist inside the Kerberos database as the check is done by
trying to get ticket for this service.
trying to get ticket for this service. Default is
None. It's used only by the pseudo password haser
and the login/password authentication backend.
`KERBEROS_KEEP_PASSWORD`
------------------------
Does the KerbersoPasswordBackend store a hash of the
checked password inside the user model each time a
user log in. Default is False. It allows your
website to provide a backup authentication if
Kerberos is failing or if you ever need to detach
from the realm.
Custom backend
==============
@ -101,3 +113,11 @@ The content of the password field must be `kerberos$<principal name>`.
To create an user for a principal you can do::
User.objects.create(username=new_username, password='kerberos$' + principal)
Login/Password backend
======================
If your users does not have their browser configured
for SPNEGO HTTP authentication you can also provide
a classic login/password form which check passwords
using Kerberos.

View File

@ -5,9 +5,11 @@ class AppSettings(object):
__DEFAULTS = {
'BACKEND_CREATE': False,
'BACKEND_ADMIN_REGEXP': None,
'SERVICE_PRINCIPAL': None,
'DEFAULT_REALM': None,
'SERVICE_PRINCIPAL': '',
'HOSTNAME': None,
'KEYTAB': None,
'KEEP_PASSWORD': False,
}
def __getattr__(self, name):

View File

@ -1,12 +1,16 @@
import re
import logging
from . import app_settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.encoding import force_bytes
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
import kerberos
class KerberosBackend(ModelBackend):
def __init__(self):
self.logger = logging.getLogger(__name__)
@ -38,6 +42,9 @@ class KerberosBackend(ModelBackend):
kwargs = {username_field: username}
if app_settings.BACKEND_CREATE:
user, created = User.objects.get_or_create(**kwargs)
if created:
user.set_unusable_password()
user.save()
else:
try:
user = User.objects.get(**kwargs)
@ -47,9 +54,53 @@ class KerberosBackend(ModelBackend):
return user
def authenticate(self, principal=None):
def authenticate(self, principal=None, **kwargs):
if principal and self.authorize_principal(principal):
return self.lookup_user(principal)
class KerberosPasswordBackend(KerberosBackend):
def default_realm(self):
'''Default realm for usernames without a realm'''
return app_settings.DEFAULT_REALM
def principal_from_username(self, username):
realm = self.default_realm()
if '@' not in username and realm:
username = u'%s@%s' % (username, realm)
return username
def keep_password(self):
'''Do we save a password hash ?'''
return app_settings.KEEP_PASSWORD
def service_principal(self):
'''Service principal for checking password'''
if not app_settings.SERVICE_PRINCIPAL:
raise ImproperlyConfigured('Kerberos password backend needs '
'the setting KERBEROS_SERVICE_PRINCIPAL to be '
'set')
return app_settings.SERVICE_PRINCIPAL
def authenticate(self, username=None, password=None, **kwargs):
'''Verify username and password using Kerberos'''
if not username:
return
principal = force_bytes(self.principal_from_username(username))
password = force_bytes(password)
try:
if not kerberos.checkPassword(principal, password,
self.service_principal(), self.default_realm()):
return
except kerberos.KrbError, e:
logging.getLogger(__name__).error('password validation'
'for principal %r failed %s', principal, e)
return
else:
if principal and self.authorize_principal(principal):
user = self.lookup_user(principal)
if self.keep_password():
user.set_password(password)
return user

View File

@ -1,10 +1,12 @@
import logging
import kerberos
from django.core.exceptions import ImproperlyConfigured
from django.contrib.auth.hashers import BasePasswordHasher
from django.utils.encoding import force_bytes
from django.contrib.auth.hashers import BasePasswordHasher
import kerberos
from . import app_settings
class KerberosHasher(BasePasswordHasher):
@ -16,7 +18,7 @@ class KerberosHasher(BasePasswordHasher):
algorithm, principal = encoded.split('$', 2)
assert algorithm == self.algorithm
principal = force_bytes(principal)
password = force_bytes(principal)
password = force_bytes(password)
if not app_settings.SERVICE_PRINCIPAL:
raise ImproperlyConfigured('Kerberos pseudo password hasher needs '
'the setting KERBEROS_SERVICE_PRINCIPAL to be '