Ticket #1428: multiauth.diff
File multiauth.diff, 20.0 KB (added by , 19 years ago) |
---|
-
django/conf/global_settings.py
265 265 # A tuple of IP addresses that have been banned from participating in various 266 266 # Django-powered features. 267 267 BANNED_IPS = () 268 269 ################## 270 # AUTHENTICATION # 271 ################## 272 273 AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',) 274 CREDENTIAL_PLUGINS = ('django.contrib.auth.credentials.username_password_form',) 275 -
django/contrib/auth/backends.py
1 from django.conf import settings 2 from django.contrib.auth.models import User 3 from django.contrib.auth.utils import check_password 4 5 class SettingsBackend: 6 """ 7 Authenticate against vars in settings.py Use the login name, and a hash 8 of the password. 9 10 ADMIN_LOGIN = 'admin' 11 ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de' 12 """ 13 def authenticate(self, username=None, password=None): 14 login_valid = (settings.ADMIN_LOGIN == username) 15 pwd_valid = check_password(password, settings.ADMIN_PASSWORD) 16 if login_valid and pwd_valid: 17 # TODO: This should be abstracted out someplace else. 18 try: 19 user = User.objects.get(username=username) 20 except User.DoesNotExist: 21 user = User(username=username, password='') 22 user.is_staff = True 23 user.is_superuser = True 24 user.save() 25 return user 26 return None 27 28 def get_user(self, user_id): 29 try: 30 return User.objects.get(pk=user_id) 31 except User.DoesNotExist: 32 return None 33 34 class ModelBackend: 35 """ 36 Authenticate against django.contrib.auth.models.User 37 """ 38 # TODO: Model, login attribute name and password attribute name should be 39 # configurable. 40 def authenticate(self, username=None, password=None): 41 try: 42 user = User.objects.get(username=username) 43 if user.check_password(password): 44 return user 45 except User.DoesNotExist: 46 return None 47 48 def get_user(self, user_id): 49 try: 50 return User.objects.get(pk=user_id) 51 except User.DoesNotExist: 52 return None -
django/contrib/auth/middleware.py
4 4 5 5 def __get__(self, request, obj_type=None): 6 6 if self._user is None: 7 from django.contrib.auth.models import User, AnonymousUser, SESSION_KEY 8 try: 9 user_id = request.session[SESSION_KEY] 10 self._user = User.objects.get(pk=user_id) 11 except (KeyError, User.DoesNotExist): 12 self._user = AnonymousUser() 7 from django.contrib.auth import get_current_user 8 self._user = get_current_user(request) 13 9 return self._user 14 10 15 11 class AuthenticationMiddleware: -
django/contrib/auth/views.py
3 3 from django import forms 4 4 from django.shortcuts import render_to_response 5 5 from django.template import RequestContext 6 from django.contrib.auth.models import SESSION_KEY7 6 from django.contrib.sites.models import Site 8 7 from django.http import HttpResponse, HttpResponseRedirect 9 8 from django.contrib.auth.decorators import login_required … … 19 18 # Light security check -- make sure redirect_to isn't garbage. 20 19 if not redirect_to or '://' in redirect_to or ' ' in redirect_to: 21 20 redirect_to = '/accounts/profile/' 22 request.session[SESSION_KEY] = manipulator.get_user_id() 21 from django.contrib.auth import login 22 login(request, manipulator.get_user()) 23 23 request.session.delete_test_cookie() 24 24 return HttpResponseRedirect(redirect_to) 25 25 else: … … 33 33 34 34 def logout(request, next_page=None): 35 35 "Logs out the user and displays 'You are logged out' message." 36 from django.contrib.auth import logout 36 37 try: 37 del request.session[SESSION_KEY]38 logout(request) 38 39 except KeyError: 39 40 return render_to_response('registration/logged_out.html', {'title': 'Logged out'}, context_instance=RequestContext(request)) 40 41 else: -
django/contrib/auth/credentials.py
1 def username_password_form(request): 2 try: 3 username = request.POST['username'] 4 password = request.POST['password'] 5 return {'username': username, 'password': password} 6 except KeyError: 7 return None 8 9 def token(request): 10 try: 11 return request.POST['token'] 12 except KeyError: 13 return None -
django/contrib/auth/__init__.py
1 from django.core.exceptions import ImproperlyConfigured 2 3 SESSION_KEY = '_auth_user_id' 4 BACKEND_SESSION_KEY = '_auth_user_backend' 1 5 LOGIN_URL = '/accounts/login/' 2 6 REDIRECT_FIELD_NAME = 'next' 7 8 def load_plugin(path): 9 i = path.rfind('.') 10 module, attr = path[:i], path[i+1:] 11 try: 12 mod = __import__(module, '', '', [attr]) 13 except ImportError, e: 14 raise ImproperlyConfigured, 'Error importing credential plugin %s: "%s"' % (module, e) 15 try: 16 func = getattr(mod, attr) 17 except AttributeError: 18 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" credential plugin' % (module, attr) 19 return func 20 21 def load_backend(path): 22 i = path.rfind('.') 23 module, attr = path[:i], path[i+1:] 24 try: 25 mod = __import__(module, '', '', [attr]) 26 except ImportError, e: 27 raise ImproperlyConfigured, 'Error importing authentication backend %s: "%s"' % (module, e) 28 try: 29 cls = getattr(mod, attr) 30 except AttributeError: 31 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" authentication backend' % (module, attr) 32 return cls() 33 34 def get_backends(): 35 from django.conf import settings 36 backends = [] 37 for backend_path in settings.AUTHENTICATION_BACKENDS: 38 backends.append(load_backend(backend_path)) 39 return backends 40 41 def get_credential_plugins(): 42 from django.conf import settings 43 credential_plugins = [] 44 for plugin_path in settings.CREDENTIAL_PLUGINS: 45 credential_plugins.append(load_plugin(plugin_path)) 46 return credential_plugins 47 48 def authenticate_credentials(**credentials): 49 """ 50 If the given credentials, return a user object. 51 """ 52 for backend in get_backends(): 53 try: 54 user = backend.authenticate(**credentials) 55 except TypeError: 56 # this backend doesn't accept these credentials as arguments, try the next one. 57 continue 58 if user is None: 59 continue 60 # annotate the user object with the path of the backend 61 user.backend = str(backend.__class__) 62 return user 63 64 def authenticate_request(request): 65 """ 66 Use CREDENTIAL_PLUGINS to find credentials in the request and try to 67 authenticate them. 68 """ 69 for plugin in get_credential_plugins(): 70 credentials = plugin(request) 71 if credentials is None: 72 continue 73 user = authenticate_credentials(**credentials) 74 if user is None: 75 continue 76 return user 77 78 def login(request, user=None): 79 """ 80 Persist a user id and a backend in the request. This way a user doesn't 81 have to reauthenticate on every request. 82 """ 83 if user is None: 84 user = request.user 85 # TODO: It would be nice to support different login methods, like signed cookies. 86 request.session[SESSION_KEY] = user.id 87 request.session[BACKEND_SESSION_KEY] = user.backend 88 89 def authenticate_request_and_login(request): 90 """ 91 Convenience function to authenticate a request and log a user in. Returns 92 the user object, or None if authentication failed. 93 """ 94 user = authenticate_request(request) 95 if user is not None: 96 login(request, user) 97 return user 98 99 def logout(request): 100 """ 101 Remove the authenticated user's id from request. 102 """ 103 del request.session[SESSION_KEY] 104 del request.session[BACKEND_SESSION_KEY] 105 106 def get_current_user(request): 107 from django.contrib.auth.models import AnonymousUser 108 try: 109 user_id = request.session[SESSION_KEY] 110 backend_path = request.session[BACKEND_SESSION_KEY] 111 backend = load_backend(backend_path) 112 user = backend.get_user(user_id) or AnonymousUser() 113 except KeyError: 114 user = AnonymousUser() 115 return user -
django/contrib/auth/utils.py
1 def encrypt_password(raw_password): 2 import sha, random 3 algo = 'sha1' 4 salt = sha.new(str(random.random())).hexdigest()[:5] 5 hsh = sha.new(salt+raw_password).hexdigest() 6 return '%s$%s$%s' % (algo, salt, hsh) 7 8 def check_password(raw_password, enc_password): 9 """ 10 Returns a boolean of whether the raw_password was correct. Handles 11 encryption formats behind the scenes. 12 """ 13 # Backwards-compatibility check. Older passwords won't include the 14 # algorithm or salt. 15 if '$' not in enc_password: 16 import md5 17 return enc_password == md5.new(raw_password).hexdigest() 18 algo, salt, hsh = enc_password.split('$') 19 if algo == 'md5': 20 import md5 21 return hsh == md5.new(salt+raw_password).hexdigest() 22 elif algo == 'sha1': 23 import sha 24 return hsh == sha.new(salt+raw_password).hexdigest() 25 raise ValueError, "Got unknown password algorithm type in password." 26 -
django/contrib/auth/models.py
4 4 from django.utils.translation import gettext_lazy as _ 5 5 import datetime 6 6 7 SESSION_KEY = '_auth_user_id'8 9 7 class SiteProfileNotAvailable(Exception): 10 8 pass 11 9 -
django/contrib/auth/forms.py
1 1 from django.contrib.auth.models import User 2 from django.contrib.auth import authenticate_request 2 3 from django.contrib.sites.models import Site 3 4 from django.template import Context, loader 4 5 from django.core import validators … … 20 21 self.fields = [ 21 22 forms.TextField(field_name="username", length=15, maxlength=30, is_required=True, 22 23 validator_list=[self.isValidUser, self.hasCookiesEnabled]), 23 forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True, 24 validator_list=[self.isValidPasswordForUser]), 24 forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True), 25 25 ] 26 26 self.user_cache = None 27 27 … … 30 30 raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.") 31 31 32 32 def isValidUser(self, field_data, all_data): 33 try: 34 self.user_cache = User.objects.get(username=field_data) 35 except User.DoesNotExist: 33 self.user_cache = authenticate_request(self.request) 34 if self.user_cache is None: 36 35 raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.") 37 36 38 def isValidPasswordForUser(self, field_data, all_data):39 if self.user_cache is not None and not self.user_cache.check_password(field_data):40 self.user_cache = None41 raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")42 43 37 def get_user_id(self): 44 38 if self.user_cache: 45 39 return self.user_cache.id -
django/contrib/comments/views/comments.py
5 5 from django.core.exceptions import ObjectDoesNotExist 6 6 from django.shortcuts import render_to_response 7 7 from django.template import RequestContext 8 from django.contrib.auth.models import SESSION_KEY9 8 from django.contrib.comments.models import Comment, FreeComment, PHOTOS_REQUIRED, PHOTOS_OPTIONAL, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC 10 9 from django.contrib.contenttypes.models import ContentType 11 10 from django.contrib.auth.forms import AuthenticationForm … … 219 218 # If user gave correct username/password and wasn't already logged in, log them in 220 219 # so they don't have to enter a username/password again. 221 220 if manipulator.get_user() and new_data.has_key('password') and manipulator.get_user().check_password(new_data['password']): 222 request.session[SESSION_KEY] = manipulator.get_user_id() 221 from django.contrib.auth import login 222 login(request, manipulator.get_user()) 223 223 if errors or request.POST.has_key('preview'): 224 224 class CommentFormWrapper(forms.FormWrapper): 225 225 def __init__(self, manipulator, new_data, errors, rating_choices): -
django/contrib/admin/views/decorators.py
1 1 from django import http, template 2 2 from django.conf import settings 3 from django.contrib.auth.models import User, SESSION_KEY 3 from django.contrib.auth.models import User 4 from django.contrib.auth import authenticate_request, login 4 5 from django.shortcuts import render_to_response 5 6 from django.utils.translation import gettext_lazy 6 7 import base64, datetime, md5 … … 69 70 return _display_login_form(request, message) 70 71 71 72 # Check the password. 72 username = request.POST.get('username', '') 73 try: 74 user = User.objects.get(username=username, is_staff=True) 75 except User.DoesNotExist: 73 user = authenticate_request(request) 74 if user is None: 76 75 message = ERROR_MESSAGE 76 username = request.POST.get('username', '') 77 77 if '@' in username: 78 78 # Mistakenly entered e-mail address instead of username? Look it up. 79 79 try: … … 86 86 87 87 # The user data is correct; log in the user in and continue. 88 88 else: 89 if user.check_password(request.POST.get('password', '')): 90 request.session[SESSION_KEY] = user.id 89 if user.is_staff: 90 login(request, user) 91 # TODO: set last_login with an event. 91 92 user.last_login = datetime.datetime.now() 92 93 user.save() 93 94 if request.POST.has_key('post_data'): -
docs/authentication.txt
269 269 270 270 To log a user in, do the following within a view:: 271 271 272 from django.contrib.auth .models import SESSION_KEY273 request.session[SESSION_KEY] = some_user.id272 from django.contrib.auth import login 273 login(request) 274 274 275 Because th is uses sessions, you'll need to make sure you have275 Because the login functions uses sessions, you'll need to make sure you have 276 276 ``SessionMiddleware`` enabled. See the `session documentation`_ for more 277 277 information. 278 278 279 This assumes ``some_user`` is your ``User`` instance. Depending on your task, 280 you'll probably want to make sure to validate the user's username and password.279 Depending on your task, you'll probably want to make sure to validate the 280 user's username and password before you log them in. 281 281 282 282 Limiting access to logged-in users 283 283 ---------------------------------- … … 611 611 database. To send messages to anonymous users, use the `session framework`_. 612 612 613 613 .. _session framework: http://www.djangoproject.com/documentation/sessions/ 614 615 Other Authentication Sources 616 ============================ 617 618 Django supports other authentication sources as well. You can even use 619 multiple sources at the same time. 620 621 Using multiple backends 622 ----------------------- 623 624 The list of backends to use is controlled by the ``AUTHENTICATION_BACKENDS`` 625 setting. This should be a tuple of python path names. It defaults to 626 ``('django.contrib.auth.backends.ModelBackend',)``. To add additional backends 627 just add them to your settings.py file. Ordering matters, so if the same 628 username and password is valid in multiple backends, the first one in the 629 list will return a user object, and the remaining ones won't even get a chance. 630 631 632 Customizing ModelBackend 633 ------------------------ 634 635 TODO: write me 636 637 638 Writing an authentication backend 639 --------------------------------- 640 641 An authentication backend is a class that implements 2 methods: ``get_user(id)`` 642 and ``authenticate(**credentials)``. The ``get_user`` method takes an id, which 643 could be a username, and database id, whatever, and returns a user object. The 644 ``authenticate`` method takes credentials as keyword arguments. Many times it 645 will just look like this:: 646 647 class MyBackend: 648 def authenticate(username=None, password=None): 649 # check the username/password and return a user 650 651 but it could also authenticate a token like so:: 652 653 class MyBackend: 654 def authenticate(token=None): 655 # check the token and return a user 656 657 Regardless, ``authenticate`` should check the credentials it gets, and if they 658 are valid, it should return a user object that matches those credentials. 659 660 The Django admin system is tightly coupled to the Django User object described 661 at the beginning of this document. For now, the best way to deal with this is to 662 create a Django User object for each user that exists for your backend (i.e. 663 in your ldap directory, your external sql database, etc.) You can either 664 write a script to do this in advance, or your ``authenticate`` method can do 665 it the first time a user logs in. `django.contrib.auth.backends.SettingsBackend`_ 666 is an example of the latter approach. Note that you don't have to save a user's 667 password in the Django User object. Your backend can still check the password 668 against an external source, and return a Django User object. 669 670 .. _django.contrib.auth.backends.SettingsBackend: http://code.djangoproject.com/browser/django/branches/magic-removal/django/contrib/auth/backends.py 671 672 Credential Plugins 673 ================== 674 675 Like metaclasses, 99.999% of people should never need to bother with these. 676 I'm not sure they're even worth keeping... at least conceptually. The work 677 they do is probably best done in a few utility functions that get used in 678 decorators or views. Basically, they allow using different types of 679 credentials for the same view or url. The main use I can think of is REST, or 680 changing your whole app from form/cookie auth, to http basic or digest auth. I 681 don't think that will happen very often, if ever. 682 683 TODO: write more... maybe 684 No newline at end of file