authentic/src/authentic2/backends/models_backend.py

107 lines
3.7 KiB
Python

# authentic2 - versatile identity manager
# Copyright (C) 2010-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/>.
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db import models
from django.utils import six
from authentic2.backends import get_user_queryset
from authentic2.user_login_failure import user_login_failure, user_login_success
from .. import app_settings
def upn(username, realm):
'''Build an UPN from a username and a realm'''
return u'{0}@{1}'.format(username, realm)
PROXY_USER_MODEL = None
class ModelBackend(ModelBackend):
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def get_query(self, username, realm=None, ou=None):
UserModel = get_user_model()
username_field = 'username'
queries = []
try:
if app_settings.A2_ACCEPT_EMAIL_AUTHENTICATION and UserModel._meta.get_field('email'):
queries.append(models.Q(**{'email__iexact': username}))
except models.FieldDoesNotExist:
pass
if realm is None:
queries.append(models.Q(**{username_field: username}))
if '@' not in username:
if app_settings.REALMS:
for realm, desc in app_settings.REALMS:
queries.append(models.Q(**{username_field: upn(username, realm)}))
else:
queries.append(models.Q(**{username_field: upn(username, realm)}))
queries = six.moves.reduce(models.Q.__or__, queries)
if ou:
queries &= models.Q(ou=ou)
return queries
def must_reset_password(self, user):
from .. import models
return bool(models.PasswordReset.filter(user=user).count())
def authenticate(self, request, username=None, password=None, realm=None, ou=None):
UserModel = get_user_model()
if not username:
return
query = self.get_query(username=username, realm=realm, ou=ou)
users = get_user_queryset().filter(query)
# order by username to make username without realm come before usernames with realms
# i.e. "toto" should come before "toto@example.com"
users = users.order_by('-is_active', UserModel.USERNAME_FIELD, 'id')
for user in users:
if user.check_password(password):
user_login_success(user.get_username())
return user
else:
user_login_failure(user.get_username())
if hasattr(request, 'failed_logins'):
request.failed_logins.add(user)
def get_user(self, user_id):
UserModel = get_user_model()
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user
def get_saml2_authn_context(self):
import lasso
return lasso.SAML2_AUTHN_CONTEXT_PASSWORD
class DummyModelBackend(ModelBackend):
def authenticate(self, request, user=None):
if user is not None:
return user