This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
django-kerberos/src/django_kerberos/backends.py

134 lines
4.9 KiB
Python

# django-kerberos - SPNEGO/Kerberos authentication for Django applications
# Copyright (C) 2014-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import re
import logging
import six
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
from . import app_settings
class KerberosBackend(ModelBackend):
def __init__(self):
self.logger = logging.getLogger(__name__)
def username_from_principal(self, principal):
'''Make a username from a principal name'''
username, domain = principal.rsplit('@', 1)
return u'{0}@{1}'.format(username, domain.lower())
def authorize_principal(self, principal):
'''Is this principal authorized to login ?'''
return True
def provision_user(self, principal, user):
'''Modify user based on information we can retrieve on this principal'''
if app_settings.BACKEND_ADMIN_REGEXP:
if re.match(app_settings.BACKEND_ADMIN_REGEXP, principal):
if not user.is_staff or not user.is_superuser:
self.logger.info('giving superuser power to principal %r', principal)
user.is_staff = True
user.is_superuser = True
user.save()
def should_create_user(self):
'''Should we create users for new principals ?'''
return app_settings.BACKEND_CREATE
def lookup_user(self, principal):
'''Find the user model linked to this principal'''
User = get_user_model()
username_field = getattr(User, 'USERNAME_FIELD', 'username')
username = self.username_from_principal(principal)
kwargs = {username_field: username}
if self.should_create_user():
user, created = User.objects.get_or_create(**kwargs)
if created:
user.set_unusable_password()
user.save()
else:
try:
user = User.objects.get(**kwargs)
except User.DoesNotExist:
return
self.provision_user(principal, user)
return user
def authenticate(self, request=None, principal=None):
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, request=None, username=None, password=None):
'''Verify username and password using Kerberos'''
if not username:
return
kerb_principal = principal = self.principal_from_username(username)
kerb_password = password
if six.PY2:
kerb_principal = force_bytes(kerb_principal)
kerb_password = force_bytes(kerb_principal)
try:
if not kerberos.checkPassword(kerb_principal, kerb_password,
self.service_principal(),
self.default_realm()):
return
except kerberos.KrbError as 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