1 | ''' |
---|
2 | BSD Licence |
---|
3 | Copyright (c) 2012, Science & Technology Facilities Council (STFC) |
---|
4 | All rights reserved. |
---|
5 | |
---|
6 | Redistribution and use in source and binary forms, with or without modification, |
---|
7 | are 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 | |
---|
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
---|
20 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
---|
21 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
---|
22 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
---|
23 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
25 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
---|
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
---|
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
28 | |
---|
29 | Created on 2 Nov 2012 |
---|
30 | |
---|
31 | @author: mnagni |
---|
32 | ''' |
---|
33 | from django.http import HttpResponseRedirect |
---|
34 | from paste.auth import auth_tkt |
---|
35 | from paste.auth.auth_tkt import BadTicket |
---|
36 | from django.conf import settings |
---|
37 | from dj_security_middleware.exception import DJMiddlewareException,\ |
---|
38 | MissingCookieException |
---|
39 | from django.utils.html import escape |
---|
40 | from django.utils.http import urlencode |
---|
41 | |
---|
42 | LOGIN_SERVICE_ERROR = 'No LOGIN_SETTING parameter is defined in the \ |
---|
43 | application settings.py file. Please define a proper URL to the \ |
---|
44 | authenticating service' |
---|
45 | |
---|
46 | DJ_SECURITY_SHAREDSECRET_ERROR = 'No DJ_SECURITY_SHAREDSECRET parameter \ |
---|
47 | is defined in the application settings.py file. \ |
---|
48 | Please define it accordingly to the used LOGIN_SERVICE' |
---|
49 | |
---|
50 | AUTHENTICATION_COOKIE_MISSING = 'The expected cookie is missing. \ |
---|
51 | Redirect to the authentication service' |
---|
52 | |
---|
53 | DJ_MIDDLEWARE_IP_ERROR = 'No DJ_MIDDLEWARE_IP parameter \ |
---|
54 | is defined in the application settings.py file. \ |
---|
55 | Please define it accordingly to the machine/proxy seen by the LOGIN_SERVICE' |
---|
56 | |
---|
57 | class DJ_Security_Middleware(object): |
---|
58 | """ |
---|
59 | Validates if the actual user is authenticated agains a |
---|
60 | given authentication service. |
---|
61 | Actually the middleware intercepts all the requests submitted |
---|
62 | to the underlying Django application and verifies if the presence |
---|
63 | or not of a valid paste cookie in the request. |
---|
64 | """ |
---|
65 | def process_request(self, request): |
---|
66 | if not settings.DJ_SECURITY_LOGIN_SERVICE: |
---|
67 | raise DJMiddlewareException(LOGIN_SERVICE_ERROR) |
---|
68 | if not settings.DJ_SECURITY_SHAREDSECRET: |
---|
69 | raise DJMiddlewareException(DJ_SECURITY_SHAREDSECRET_ERROR) |
---|
70 | if not settings.DJ_MIDDLEWARE_IP: |
---|
71 | raise DJMiddlewareException(DJ_MIDDLEWARE_IP_ERROR) |
---|
72 | |
---|
73 | try: |
---|
74 | timestamp, userid, tokens, user_data = _is_authenticated(request) |
---|
75 | except MissingCookieException: |
---|
76 | url = '%s?%s' % (settings.DJ_SECURITY_LOGIN_SERVICE, _build_ret_url(request)) |
---|
77 | return HttpResponseRedirect(url) |
---|
78 | |
---|
79 | def _build_ret_url(request): |
---|
80 | hostname = _get_hostname() |
---|
81 | if request.META['SERVER_PORT'] != 80: |
---|
82 | hostname = "%s:%s" % (hostname, request.META['SERVER_PORT']) |
---|
83 | qs = {} |
---|
84 | qs['r'] = escape('http://%s%s' % (hostname, request.path)) |
---|
85 | return urlencode(qs) |
---|
86 | |
---|
87 | def _is_authenticated(request): |
---|
88 | """ |
---|
89 | Verifies the presence and validity of a paste cookie. |
---|
90 | If the cookie is not present the request is redirected |
---|
91 | to the url specified in LOGIN_SERVICE |
---|
92 | ** Return ** a tuple containing (timestamp, userid, tokens, user_data) |
---|
93 | ** raise ** a DJ_SecurityException if the ticket is not valid |
---|
94 | """ |
---|
95 | if request.COOKIES.has_key('auth_tkt'): |
---|
96 | try: |
---|
97 | return auth_tkt.parse_ticket( |
---|
98 | settings.DJ_SECURITY_SHAREDSECRET, |
---|
99 | request.COOKIES['auth_tkt'], _get_host_ip()) |
---|
100 | except BadTicket as ex: |
---|
101 | raise DJMiddlewareException(ex) |
---|
102 | raise MissingCookieException(AUTHENTICATION_COOKIE_MISSING) |
---|
103 | |
---|
104 | def _get_hostname(): |
---|
105 | import socket |
---|
106 | ret = 'localhost' |
---|
107 | try: |
---|
108 | hostname = socket.gethostname() |
---|
109 | ret = socket.gethostbyname_ex(hostname)[0] |
---|
110 | except Exception: |
---|
111 | pass |
---|
112 | return ret |
---|
113 | |
---|
114 | def _get_host_ip(): |
---|
115 | if settings.DJ_MIDDLEWARE_IP: |
---|
116 | return settings.DJ_MIDDLEWARE_IP |
---|
117 | |
---|
118 | import socket |
---|
119 | ret = 'localhost' |
---|
120 | try: |
---|
121 | hostname = socket.gethostname() |
---|
122 | ret = socket.gethostbyname_ex(hostname)[2][0] |
---|
123 | except Exception: |
---|
124 | pass |
---|
125 | return ret |
---|