Project

General

Profile

0001-Re-implement-the-authenticate-to-handle-multiple-use.patch

Benjamin Dauvergne, 08 April 2015 12:49 PM

Download (6.4 KB)

View differences:

Subject: [PATCH] Re-implement the authenticate() to handle multiple user login

An authentication backend can now return a sequence of users, the
end-user is invited to choose one among those found.
 src/authentic2/backends/models_backend.py | 10 +++++
 src/authentic2/utils.py                   | 75 +++++++++++++++++++++++++++----
 2 files changed, 77 insertions(+), 8 deletions(-)
src/authentic2/backends/models_backend.py
20 20
            def roles(self):
21 21
                return self.groups.values_list('name', flat=True)
22 22

  
23 23
            class Meta:
24 24
                proxy = True
25 25
        PROXY_USER_MODEL = ProxyUser
26 26
    return PROXY_USER_MODEL
27 27

  
28
class EmailModelBackend(ModelBackend):
29
    def authenticate(self, email=None):
30
        UserModel = get_proxy_user_model()
31
        users = UserModel.objects.filter(email=email)
32
        if len(users) == 1:
33
            return users[0]
34
        if users:
35
            return users
36
        return None
37

  
28 38
class ModelBackend(ModelBackend):
29 39
    """
30 40
    Authenticates against settings.AUTH_USER_MODEL.
31 41
    """
32 42

  
33 43
    def get_query(self, username, realm):
34 44
        UserModel = get_proxy_user_model()
35 45
        username_field = UserModel.USERNAME_FIELD
src/authentic2/utils.py
1 1
import random
2 2
import time
3 3
import logging
4 4
import urllib
5 5
import six
6 6
import urlparse
7 7
from functools import wraps
8
import inspect
9
import pickle
8 10

  
9 11
from importlib import import_module
10 12

  
11 13
from django.conf import settings
12 14
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
13
from django.core.exceptions import ImproperlyConfigured
15
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
16
from django.contrib.auth import _clean_credentials
17
from django.contrib.auth.signals import user_login_failed
14 18
from django.core import urlresolvers
15 19
from django.http.request import QueryDict
16 20
from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login
17 21
from django import forms
18 22
from django.forms.util import ErrorList
19 23
from django.utils import html, http
20 24
from django.utils.translation import ugettext as _
21 25

  
......
300 304
    '''Find an authentication event occurring during this session and matching
301 305
       this nonce.'''
302 306
    authentication_events = request.session.pop(constants.AUTHENTICATION_EVENTS_SESSION_KEY, [])
303 307
    for event in authentication_events:
304 308
        if event.get('nonce') == nonce:
305 309
            return event
306 310
    return None
307 311

  
308
def login(request, user, how, **kwargs):
309
    '''Login a user model, record the authentication event and redirect to next
310
       URL or settings.LOGIN_REDIRECT_URL.'''
311
    auth_login(request, user)
312
    record_authentication_event(request, how)
313
    return continue_to_next_url(request, **kwargs)
314

  
315 312
def login_require(request, next_url=None, login_url='auth_login', **kwargs):
316 313
    '''Require a login and come back to current URL'''
317 314
    next_url = next_url or request.get_full_path()
318 315
    params = kwargs.setdefault('params', {})
319 316
    params[REDIRECT_FIELD_NAME] = next_url
320 317
    return redirect(request, login_url, **kwargs)
321 318

  
322 319
def redirect_to_logout(request, next_url=None, logout_url='auth_logout', **kwargs):
......
434 431

  
435 432
def csrf_token_check(request, form):
436 433
    '''Check a request for CSRF cookie validation, and add an error to the form
437 434
       if check fails.
438 435
    '''
439 436
    if form.is_valid() and not getattr(request, 'csrf_processing_done', False):
440 437
        msg = _('The form was out of date, please try again.')
441 438
        form._errors[forms.forms.NON_FIELD_ERRORS] = ErrorList([msg])
439

  
440
def _get_backends(return_tuples=False):
441
    backends = []
442
    for backend_path in settings.AUTHENTICATION_BACKENDS:
443
        backend = load_backend(backend_path)
444
        backends.append((backend, backend_path) if return_tuples else backend)
445
    if not backends:
446
        raise ImproperlyConfigured(
447
            'No authentication backends have been defined. Does '
448
            'AUTHENTICATION_BACKENDS contain anything?'
449
        )
450
    return backends
451

  
452
def authenticate(**credentials):
453
    """
454
    If the given credentials are valid, return a User object.
455
    """
456
    for backend, backend_path in _get_backends(return_tuples=True):
457
        try:
458
            inspect.getcallargs(backend.authenticate, **credentials)
459
        except TypeError:
460
            # This backend doesn't accept these credentials as arguments. Try the next one.
461
            continue
462

  
463
        try:
464
            user = backend.authenticate(**credentials)
465
        except PermissionDenied:
466
            # This backend says to stop in our tracks - this user should not be allowed in at all.
467
            return None
468
        if user is None:
469
            continue
470
        # Allow backends to return multiple matches
471
        if isinstance(user, (list, tuple)):
472
            users = user
473
            for user in users:
474
                # Annotate the user object with the path of the backend.
475
                user.backend = backend_path
476
            return users
477
        else:
478
            # Annotate the user object with the path of the backend.
479
            user.backend = backend_path
480
            return user
481

  
482
    # The credentials supplied are invalid to all backends, fire signal
483
    user_login_failed.send(sender=__name__,
484
            credentials=_clean_credentials(credentials))
485

  
486
MULTIPLE_USER_SESSION_KEY = 'multiple_users'
487

  
488
def login(request, user, how, multiple_login_url='a2-multiple-login', **kwargs):
489
    '''Login a user or if user is a list of users, redirect so that user can
490
       pick one.
491
    '''
492
    if isinstance(user, (list, tuple)):
493
        request.session[MULTIPLE_USER_SESSION_KEY] = pickle.dumps(user)
494
        return redirect_to_login(request, login_url=multiple_login_url)
495
    else:
496
        auth_login(request, user)
497
        record_authentication_event(request, how)
498
        return continue_to_next_url(request, **kwargs)
499

  
500

  
442
-