source: mauRepo/dj_security_middleware/trunk/dj_security_middleware/middleware.py @ 8795

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/dj_security_middleware/trunk/dj_security_middleware/middleware.py@8795
Revision 8795, 7.5 KB checked in by mnagni, 6 years ago (diff)

Incomplete - # 22802: [CEDA Site Python Port] Login - no message is displayed when the user enters the wrong password
 http://team.ceda.ac.uk/trac/ceda/ticket/22802

The last update added a bug on the logout. Fixed

  • Property svn:mime-type set to text/plain
Line 
1'''
2BSD Licence
3Copyright (c) 2012, Science & Technology Facilities Council (STFC)
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without modification,
7are permitted provided that the following conditions are met:
8
9    * Redistributions of source code must retain the above copyright notice,
10        this list of conditions and the following disclaimer.
11    * Redistributions in binary form must reproduce the above copyright notice,
12        this list of conditions and the following disclaimer in the documentation
13        and/or other materials provided with the distribution.
14    * Neither the name of the Science & Technology Facilities Council (STFC)
15        nor the names of its contributors may be used to endorse or promote
16        products derived from this software without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29Created on 2 Nov 2012
30
31@author: mnagni
32'''
33from paste.auth import auth_tkt
34from paste.auth.auth_tkt import BadTicket
35from django.conf import settings
36
37from django.utils.http import urlencode
38from django.http import HttpResponseRedirect
39
40from dj_security_middleware.exception import DJMiddlewareException,\
41    MissingCookieException
42from dj_security_middleware import _get_host_ip, security_filter, auth_tkt_name,\
43    shared_secret, token_field_name, redirect_field_name, cookie_domain
44
45import socket
46import logging
47import re
48import urllib
49
50LOGIN_SERVICE_ERROR = 'No LOGIN_SETTING parameter is defined in the \
51application settings.py file. Please define a proper URL to the \
52authenticating service'
53
54DJ_SECURITY_SHAREDSECRET_ERROR = 'No SECURITY_SHAREDSECRET parameter \
55is defined in the application settings.py file. \
56Please define it accordingly to the used LOGIN_SERVICE'
57
58AUTHENTICATION_COOKIE_MISSING = 'The expected cookie is missing. \
59Redirect to the authentication service'
60
61DJ_MIDDLEWARE_IP_ERROR = 'No DJ_MIDDLEWARE_IP parameter \
62is defined in the application settings.py file. \
63Please define it accordingly to the machine/proxy seen by the LOGIN_SERVICE'
64
65LOGOUT = 'logout'
66
67LOGGER = logging.getLogger(__name__)
68
69def preapare_user_for_session(request, timestamp, userid, tokens, user_data):
70    request.authenticated_user = {'timestamp': timestamp, \
71                                     'userid': userid, \
72                                     'tokens': tokens, \
73                                     'user_data': user_data}
74    LOGGER.debug("stored in request - userid:%s, user_data:%s" % (userid, user_data))
75    request.session['accountid'] = userid
76
77class DJ_Security_Middleware(object):
78    """
79        Validates if the actual user is authenticated agains a
80        given authentication service.
81        Actually the middleware intercepts all the requests submitted
82        to the underlying Django application and verifies if the presence
83        or not of a valid paste cookie in the request.
84    """
85
86    def process_request(self, request):
87        #Has to process a reset password request?
88        if len(request.REQUEST.get(LOGOUT, '')) > 0:
89            response = HttpResponseRedirect(_build_url(request))           
90            response.delete_cookie(auth_tkt_name(), domain = cookie_domain())
91            return response
92
93        request.session['accountid'] = None
94        url_fiters = getattr(settings, 'DJ_SECURITY_FILTER', [])
95       
96        #adds a default filter for reset password request
97        reset_regexpr = '%s=[a-f0-9-]*$' % (token_field_name())
98        if reset_regexpr not in security_filter():
99            url_fiters.append(reset_regexpr)
100       
101        if url_fiters \
102            and filter(_build_url(request), url_fiters):
103            return
104       
105        if not getattr(settings, 'DJ_SECURITY_LOGIN_SERVICE', None):
106            raise DJMiddlewareException(LOGIN_SERVICE_ERROR) 
107       
108        custom_auth = getattr(settings, 'DJ_SECURITY_AUTH_CHECK', None)
109        if custom_auth:
110            try:
111                if custom_auth(request):
112                    return
113            #Cannot specify the Exception type as don't know the
114            # exceptions type raised by custom_auth                 
115            except Exception:
116                pass
117       
118        #if not settings.DJ_MIDDLEWARE_IP:
119        #    raise DJMiddlewareException(DJ_MIDDLEWARE_IP_ERROR)       
120         
121        try:
122            qs = {redirect_field_name():
123                  urllib.quote_plus((_build_url(request)))}             
124            url = '%s?%s' % (settings.DJ_SECURITY_LOGIN_SERVICE,
125                             urlencode(qs))
126            timestamp, userid, tokens, user_data = _is_authenticated(request)
127            preapare_user_for_session(request,
128                                      timestamp,
129                                      userid,
130                                      tokens,
131                                      user_data)
132        except MissingCookieException as ex:
133            LOGGER.info("Missing cookie '%s'. Redirecting to %s" % (auth_tkt_name(), url))
134            return HttpResponseRedirect(url)
135        except DJMiddlewareException as ex:                   
136            LOGGER.info("Error in authentication. Redirecting to %s" % (url))
137            return HttpResponseRedirect(url)           
138
139
140           
141    def process_response(self, request, response):
142        return response
143
144def _build_url(request):
145    hostname = socket.getfqdn()
146    new_get = request.GET.copy()
147    if new_get.has_key(LOGOUT):
148        new_get.pop(LOGOUT)
149    if request.META['SERVER_PORT'] != 80:
150        hostname = "%s:%s" % (hostname, request.META['SERVER_PORT'])
151    return 'http://%s%s?%s' % (hostname,
152                               request.path,
153                               urllib.urlencode(new_get))
154
155def _is_authenticated(request):
156    """
157        Verifies the presence and validity of a paste cookie.
158        If the cookie is not present the request is redirected
159        to the url specified in LOGIN_SERVICE
160        ** Return ** a tuple containing (timestamp, userid, tokens, user_data)
161        ** raise ** a DJ_SecurityException if the ticket is not valid
162    """
163    if auth_tkt_name() in request.COOKIES:
164        LOGGER.debug("Found cookie '%s': %s in cookies" \
165                     % (auth_tkt_name(), request.COOKIES.get(auth_tkt_name())))
166        try:
167           
168            return auth_tkt.parse_ticket(
169                    shared_secret(),
170                    request.COOKIES.get(auth_tkt_name(), ''),
171                    _get_host_ip())
172        except BadTicket as ex:
173            raise DJMiddlewareException(ex)
174    raise MissingCookieException(AUTHENTICATION_COOKIE_MISSING)
175
176def filter(string, filters):
177    """
178        Checks a given strings against a list of strings.
179        ** string ** string a url
180        ** filters ** a list of strings
181    """
182    for ifilter in filters:
183        if re.search(ifilter, string):
184            return True
Note: See TracBrowser for help on using the repository browser.