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

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/dj_security/trunk/dj_security/views/dj_security_login.py@8741
Revision 8741, 11.5 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

  • 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, BACKEND_SESSION_KEY
8from django.shortcuts import render_to_response
9from django.core.context_processors import csrf
10from django.conf import settings
11from django_authopenid.views import not_authenticated, ask_openid,\
12    signin_failure, _build_context, complete
13from django_authopenid.forms import OpenidSigninForm
14from django.db.models import Q
15from django.contrib.auth.backends import ModelBackend
16from django.shortcuts import render_to_response as render
17
18from userdb_model.models import User
19
20import md5
21import logging
22import base64
23from django_authopenid.utils import get_url_host
24from django.core.urlresolvers import reverse
25import urllib
26from django.http import HttpResponseRedirect
27from django.contrib.auth.forms import AuthenticationForm
28from django import forms
29from django.contrib.auth.signals import user_logged_in
30from django.db.utils import DatabaseError
31from dj_security.exception import DSJOpenIDNotFoundError
32from dj_security.middleware import _encode_authenticated_response
33
34# Get an instance of a logger
35LOGGER = logging.getLogger(__name__)
36
37OPENID = 'openid'
38
39def error_handle(request, context):
40    form = CEDAAuthenticationForm()
41    context['form'] = form
42    context.update(csrf(request))
43    return render_to_response('login.html', context)
44
45class CedaUserAuthenticationBackend(ModelBackend):
46    """
47    Extends Django's ``ModelBackend`` to allow login via username, 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     
75'''
76def login(request):       
77    context = {}
78    if request.method == 'POST': # If the form has been submitted...
79        form = CEDAAuthenticationForm(request.POST) # A form bound to the POST data
80        context['form'] = form
81        if form.is_valid(): # All validation rules pass
82            username = request.POST.get('username')
83            password = request.POST.get('password')
84            LOGGER.debug("logging: %s" % (username))           
85            user = authenticate(username=username, password=password)             
86            if user is not None:
87                LOGGER.debug("authenticated: %s" % (username))
88                if user.is_active:
89                    LOGGER.debug("user %s is Active" % (username))           
90                    context['user'] = SecurityEncoder().encode(user)
91                    request.auth_user = context.get('user')
92                    return _encode_authenticated_response(request, context)         
93                else:
94                    # Return a 'disabled account' error message
95                    context['error'] = u'account disabled'
96                    LOGGER.debug("Account Disabled for user %s" % (username))                   
97                    return error_handle(request, context)
98            else:
99                # Return an 'invalid login' error message.
100                context['error'] = u'invalid login'               
101                LOGGER.debug("Invalid login for user %s" % (username))               
102                return error_handle(request, context)
103        else:
104            context['error'] = u'form is invalid'
105            return error_handle(request, context)
106    else:
107        if getattr(settings, "FAKE_AUTHENTICATION", False):
108            context['user'] = '{"username": "mnagni", '
109            '"first_name": "Maurizio", "last_name": "Nagni", '
110            '"is_active": true, "email": "maurizio.nagni@stfc.ac.uk", '
111            '"is_superuser": false, "is_staff": true, '
112            '"last_login": "2012-10-18 11:05:28.700139+00:00", '
113            '"date_joined": "2012-03-22 14:20:56+00:00", "id": 29, '
114            '"permissions": "auth.add_user,proginfo.add_dataproduct, '
115            'proginfo.change_programme, cedainfoapp.delete_vmrequest"}'
116            return _encode_authenticated_response(request, context) 
117       
118        # An unbound form
119        form = CEDAAuthenticationForm()
120        context['form'] = form
121        context.update(csrf(request))
122        return render_to_response('login.html', context)
123'''
124   
125def get_user_byopenid(user_id):
126    """
127        Returns a tbusers row specified by `user_id`
128    - String **user_id**
129        a user
130    """
131    try:
132        return User.objects.get(openid=user_id)
133    except DatabaseError as ex:
134        logging.error("Openid: %s - Not Found" % user_id)
135        raise DSJOpenIDNotFoundError(ex)   
136                   
137def logged_in(request):
138    '''
139        Retrieves the user after the openid provider authenticated him/her
140    '''
141    if SESSION_KEY not in request.session:
142        if OPENID in request.session:
143            login(request, get_user_byopenid(request.session[OPENID]))
144           
145    return _encode_authenticated_response_(request, context = {})   
146   
147def _encode_authenticated_response_(request, context):
148    redirect_parameter = getattr(settings, 'REDIRECT_URL', 'r')
149    context['redirect_url'] = \
150        base64.b64decode(request.session.get(redirect_parameter, ''))
151    LOGGER.debug("Redirecting to %s" % (context['redirect_url']))   
152    return render_to_response('logged_in.html', context)
153
154class CEDAAuthenticationForm(AuthenticationForm):
155
156    def __init__(self, request=None, *args, **kwargs):
157        super(CEDAAuthenticationForm, self).__init__(request, *args, **kwargs)
158
159    def clean(self):
160        username = self.cleaned_data.get('username')
161        password = self.cleaned_data.get('password')
162
163        if username and password:
164            self.user_cache = authenticate(username=username,
165                                           password=password)
166            if self.user_cache is None:
167                raise forms.ValidationError(
168                    self.error_messages['invalid_login'])
169            #elif not self.user_cache.is_active:
170            #    raise forms.ValidationError(self.error_messages['inactive'])
171        self.check_for_test_cookie()
172        return self.cleaned_data
173
174   
175@not_authenticated
176def signin(request, template_name='authopenid/signin.html',
177        redirect_field_name=REDIRECT_FIELD_NAME, openid_form=OpenidSigninForm,
178        auth_form=CedaUserAuthenticationBackend, on_failure=None, extra_context=None):
179    """Signin page. It manage the legacy authentification (user/password) 
180    and authentification with openid.
181
182    :attr request: request object
183    :attr template_name: string, name of template to use
184    :attr redirect_field_name: string, field name used for redirect. by
185    default 'next'
186    :attr openid_form: form use for openid signin, by default
187    `OpenidSigninForm`
188    :attr auth_form: form object used for legacy authentification.
189    By default AuthentificationForm form auser auth contrib.
190    :attr extra_context: A dictionary of variables to add to the
191    template context. Any callable object in this dictionary will
192    be called to produce the end result which appears in the context.
193    """
194    if on_failure is None:
195        on_failure = signin_failure
196       
197    redirect_to = request.REQUEST.get(redirect_field_name, '')
198    form1 = openid_form()
199    form2 = auth_form()
200    if request.POST:
201        if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
202            redirect_to = settings.LOGIN_REDIRECT_URL     
203        if 'openid_url' in request.POST.keys():
204            form1 = openid_form(data=request.POST)
205            if form1.is_valid():
206                redirect_url = "%s%s?%s" % (
207                        get_url_host(request),
208                        reverse('user_complete_signin'),
209                        urllib.urlencode({ redirect_field_name: redirect_to })
210                )
211                return ask_openid(request,
212                        form1.cleaned_data['openid_url'],
213                        redirect_url,
214                        on_failure=on_failure)
215        else:
216            # perform normal django authentification
217            form2 = auth_form(data=request.POST)
218            if form2.is_valid():
219                #login(request, form2.get_user())
220                if request.session.test_cookie_worked():
221                    request.session.delete_test_cookie()
222                redirect_to = base64.b64decode(redirect_to)                                   
223                response = HttpResponseRedirect(redirect_to)
224                _encode_authenticated_response(request, response, redirect_to, form2.get_user())
225                return response
226    return render(template_name, {
227        'form1': form1,
228        'form2': form2,
229        redirect_field_name: redirect_to,
230        'msg':  request.GET.get('msg','')
231    }, context_instance=_build_context(request, extra_context=extra_context)) 
232
233def signin_success(request, identity_url, openid_response,
234        redirect_field_name=REDIRECT_FIELD_NAME, **kwargs):
235   
236    #redirect_parameter = getattr(settings, 'REDIRECT_URL', 'r')   
237    redirect_url = base64.b64decode(request.REQUEST.get(redirect_field_name, ''))
238    LOGGER.debug("Redirecting to %s" % (redirect_url))
239    '''
240        Retrieves the user after the openid provider authenticated him/her
241    '''
242    response = HttpResponseRedirect(redirect_url)
243   
244    #if SESSION_KEY in request.session:
245    if OPENID in request.session:
246        return _encode_authenticated_response(request, response, redirect_url, get_user_byopenid(request.session[OPENID]))           
247    elif SESSION_KEY in request.session:
248        response = HttpResponseRedirect(redirect_url)
249        return _encode_authenticated_response(request, response, redirect_url)           
250   
251   
252   
253def login(request, user):
254    """
255    Persist a user id and a backend in the request. This way a user doesn't
256    have to reauthenticate on every request. Note that data set during
257    the anonymous session is retained when the user logs in.
258    Overrides the django.contrib.auth.login method
259    """
260    if user is None:
261        user = request.user
262    # TODO: It would be nice to support different login methods, like signed cookies.
263    if SESSION_KEY in request.session:
264        if request.session[SESSION_KEY] != user.accountid:
265            # To avoid reusing another user's session, create a new, empty
266            # session if the existing session corresponds to a different
267            # authenticated user.
268            r_copy = None
269            if request.session.has_key('r'):
270                r_copy = request.session['r']
271            request.session.flush()
272            request.user = user
273            request.session['r'] = r_copy
274    else:
275        request.session.cycle_key()
276    request.session[SESSION_KEY] = user.userkey
277    #request.session[BACKEND_SESSION_KEY] = user.backend
278    if hasattr(request, 'user'):
279        request.user = user
280    user_logged_in.send(sender=user.__class__, request=request, user=user)
Note: See TracBrowser for help on using the repository browser.