source: mauRepo/dj_security/trunk/dj_security/views/dj_security_login.py @ 8760

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/dj_security/trunk/dj_security/views/dj_security_login.py@8760
Revision 8760, 9.2 KB checked in by mnagni, 7 years ago (diff)

Incomplete - # 22698: [CEDA Site Python Port] MyCEDA Login needs to be more user-friendly
 http://team.ceda.ac.uk/trac/ceda/ticket/22698
Incomplete - # 22689: [CEDA Site Python Port] Logout link
 http://team.ceda.ac.uk/trac/ceda/ticket/22689

  • Property svn:mime-type set to text/plain
Line 
1'''
2Created on 29 Oct 2012
3
4@author: mnagni
5'''
6from django.contrib.auth import authenticate, REDIRECT_FIELD_NAME, \
7    SESSION_KEY
8from django.core.context_processors import csrf
9from django.conf import settings
10from django_authopenid.views import not_authenticated, ask_openid,\
11    signin_failure, _build_context
12from django_authopenid.forms import OpenidSigninForm
13from django.db.models import Q
14from django.contrib.auth.backends import ModelBackend
15from django.shortcuts import render_to_response as render
16
17from userdb_model.models import User
18
19import md5
20import logging
21import base64
22from django_authopenid.utils import get_url_host
23from django.core.urlresolvers import reverse
24import urllib
25from django.http import HttpResponseRedirect
26from django.contrib.auth.forms import AuthenticationForm
27from django import forms
28from django.contrib.auth.signals import user_logged_in
29from django.db.utils import DatabaseError
30from dj_security.exception import DSJOpenIDNotFoundError
31from dj_security.middleware import _encode_authenticated_response
32
33# Get an instance of a logger
34LOGGER = logging.getLogger(__name__)
35
36OPENID = 'openid.identity'
37
38def error_handle(request, context):
39    form = CEDAAuthenticationForm()
40    context['form'] = form
41    context.update(csrf(request))
42    return render('login.html', context)
43
44class CedaUserAuthenticationBackend(ModelBackend):
45    """
46    Extends Django's ``ModelBackend`` to allow login via username,
47    or verification token.
48
49    Args are either ``username`` and ``password``
50    and ``token``. In either case, ``is_active`` can also be given.
51
52    For login, is_active is not given, so that the login form can
53    raise a specific error for inactive users.
54    For password reset, True is given for is_active.
55    For signup verficiation, False is given for is_active.
56    """
57
58    def __init__(self, *args, **kwargs):
59        super(CedaUserAuthenticationBackend, self).__init__(*args, **kwargs)
60
61    def authenticate(self, **kwargs):
62        if kwargs:
63            username = kwargs.pop("username", None)
64            if username:
65                username = Q(accountid=username)
66                password = kwargs.pop("password", None)
67                try:
68                    user = User.objects.get(username, **kwargs)
69                except User.DoesNotExist:
70                    pass
71                else:
72                    if user.md5passwd == md5.new(password).hexdigest():
73                        return user
74   
75def get_user_byopenid(user_id):
76    """
77        Returns a tbusers row specified by `user_id`
78    - String **user_id**
79        a user
80    """
81    try:
82        return User.objects.get(openid=user_id)
83    except DatabaseError as ex:
84        logging.error("Openid: %s - Not Found" % user_id)
85        raise DSJOpenIDNotFoundError(ex)   
86                   
87def logged_in(request):
88    '''
89        Retrieves the user after the openid provider authenticated him/her
90    '''
91    if SESSION_KEY not in request.session:
92        if OPENID in request.session:
93            login(request, get_user_byopenid(request.session[OPENID]))
94           
95    return _encode_authenticated_response_(request, context = {})   
96   
97def _encode_authenticated_response_(request, context):
98    redirect_parameter = getattr(settings, 'REDIRECT_URL', 'r')
99    context['redirect_url'] = \
100        base64.b64decode(request.session.get(redirect_parameter, ''))
101    LOGGER.debug("Redirecting to %s" % (context['redirect_url']))   
102    return render('logged_in.html', context)
103
104class CEDAAuthenticationForm(AuthenticationForm):
105
106    def __init__(self, request=None, *args, **kwargs):
107        super(CEDAAuthenticationForm, self).__init__(request, *args, **kwargs)
108
109    def clean(self):
110        username = self.cleaned_data.get('username')
111        password = self.cleaned_data.get('password')
112
113        if username and password:
114            self.user_cache = authenticate(username=username,
115                                           password=password)
116            if self.user_cache is None:
117                raise forms.ValidationError(
118                    self.error_messages['invalid_login'])
119            #elif not self.user_cache.is_active:
120            #    raise forms.ValidationError(self.error_messages['inactive'])
121        self.check_for_test_cookie()
122        return self.cleaned_data
123
124   
125@not_authenticated
126def signin(request, template_name='authopenid/signin.html',
127        redirect_field_name=REDIRECT_FIELD_NAME, openid_form=OpenidSigninForm,
128        auth_form=CedaUserAuthenticationBackend,
129        on_failure=None, extra_context={'is_login_page': True}):
130    """Signin page. It manage the legacy authentification (user/password) 
131    and authentification with openid.
132
133    :attr request: request object
134    :attr template_name: string, name of template to use
135    :attr redirect_field_name: string, field name used for redirect. by
136    default 'next'
137    :attr openid_form: form use for openid signin, by default
138    `OpenidSigninForm`
139    :attr auth_form: form object used for legacy authentification.
140    By default AuthentificationForm form auser auth contrib.
141    :attr extra_context: A dictionary of variables to add to the
142    template context. Any callable object in this dictionary will
143    be called to produce the end result which appears in the context.
144    """
145    if on_failure is None:
146        on_failure = signin_failure
147       
148    redirect_to = request.REQUEST.get(redirect_field_name, '')
149    form1 = openid_form()
150    form2 = auth_form()
151    if request.POST:
152        if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
153            redirect_to = settings.LOGIN_REDIRECT_URL     
154        if 'openid_url' in request.POST.keys():
155            form1 = openid_form(data=request.POST)
156            if form1.is_valid():
157                redirect_url = "%s%s?%s" % (
158                        get_url_host(request),
159                        reverse('user_complete_signin'),
160                        urllib.urlencode({ redirect_field_name: redirect_to })
161                )
162                return ask_openid(request,
163                        form1.cleaned_data['openid_url'],
164                        redirect_url,
165                        on_failure=on_failure)
166        else:
167            # perform normal django authentification
168            form2 = auth_form(data=request.POST)
169            if form2.is_valid():
170                #login(request, form2.get_user())
171                if request.session.test_cookie_worked():
172                    request.session.delete_test_cookie()
173                redirect_to = base64.b64decode(redirect_to)                                   
174                response = HttpResponseRedirect(redirect_to)
175                _encode_authenticated_response(request,
176                                               response,
177                                               redirect_to,
178                                               form2.get_user())
179                return response
180    return render(template_name, {
181        'form1': form1,
182        'form2': form2,
183        redirect_field_name: redirect_to,
184        'msg':  request.GET.get('msg','')
185    }, context_instance=_build_context(request, extra_context=extra_context)) 
186
187def signin_success(request, identity_url, openid_response,
188        redirect_field_name=REDIRECT_FIELD_NAME, **kwargs):
189   
190    #redirect_parameter = getattr(settings, 'REDIRECT_URL', 'r')   
191    redirect_url = base64.b64decode(request.REQUEST.get(redirect_field_name, ''))
192    LOGGER.debug("Redirecting to %s" % (redirect_url))
193    '''
194        Retrieves the user after the openid provider authenticated him/her
195    '''
196    response = HttpResponseRedirect(redirect_url)
197   
198    if OPENID in request.REQUEST:
199        return _encode_authenticated_response(request,
200                                response,
201                                redirect_url,
202                                get_user_byopenid(request.REQUEST[OPENID]))           
203    elif SESSION_KEY in request.session:
204        response = HttpResponseRedirect(redirect_url)
205        return _encode_authenticated_response(request, response, redirect_url)           
206   
207   
208   
209def login(request, user):
210    """
211    Persist a user id and a backend in the request. This way a user doesn't
212    have to reauthenticate on every request. Note that data set during
213    the anonymous session is retained when the user logs in.
214    Overrides the django.contrib.auth.login method
215    """
216    if user is None:
217        user = request.user
218    # TODO: It would be nice to support different login methods, like signed cookies.
219    if SESSION_KEY in request.session:
220        if request.session[SESSION_KEY] != user.accountid:
221            # To avoid reusing another user's session, create a new, empty
222            # session if the existing session corresponds to a different
223            # authenticated user.
224            r_copy = None
225            if request.session.has_key('r'):
226                r_copy = request.session['r']
227            request.session.flush()
228            request.user = user
229            request.session['r'] = r_copy
230    else:
231        request.session.cycle_key()
232    request.session[SESSION_KEY] = user.userkey
233    #request.session[BACKEND_SESSION_KEY] = user.backend
234    if hasattr(request, 'user'):
235        request.user = user
236    user_logged_in.send(sender=user.__class__, request=request, user=user)
Note: See TracBrowser for help on using the repository browser.