Revision 35c82779
b/pithos/im/api.py | ||
---|---|---|
1 |
from traceback import format_exc |
|
2 |
from time import time, mktime |
|
3 |
from django.conf import settings |
|
4 |
from django.http import HttpResponse |
|
5 |
from django.utils import simplejson as json |
|
6 |
|
|
7 |
from pithos.im.faults import BadRequest, Unauthorized, ServiceUnavailable |
|
8 |
from pithos.im.models import User |
|
9 |
|
|
10 |
import datetime |
|
11 |
|
|
12 |
def render_fault(request, fault): |
|
13 |
if settings.DEBUG or settings.TEST: |
|
14 |
fault.details = format_exc(fault) |
|
15 |
|
|
16 |
request.serialization = 'text' |
|
17 |
data = '\n'.join((fault.message, fault.details)) + '\n' |
|
18 |
response = HttpResponse(data, status=fault.code) |
|
19 |
return response |
|
20 |
|
|
21 |
def update_response_headers(response): |
|
22 |
response['Content-Type'] = 'application/json; charset=UTF-8' |
|
23 |
response['Content-Length'] = len(response.content) |
|
24 |
|
|
25 |
def authenticate(request): |
|
26 |
# Normal Response Codes: 204 |
|
27 |
# Error Response Codes: serviceUnavailable (503) |
|
28 |
# badRequest (400) |
|
29 |
# unauthorised (401) |
|
30 |
try: |
|
31 |
if request.method != 'GET': |
|
32 |
raise BadRequest('Method not allowed.') |
|
33 |
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN') |
|
34 |
if not x_auth_token: |
|
35 |
return render_fault(request, BadRequest('Missing X-Auth-Token')) |
|
36 |
|
|
37 |
try: |
|
38 |
user = User.objects.get(auth_token=x_auth_token) |
|
39 |
except User.DoesNotExist, e: |
|
40 |
return render_fault(request, Unauthorized('Invalid X-Auth-Token')) |
|
41 |
|
|
42 |
# Check if the is active. |
|
43 |
if user.state != 'ACTIVE': |
|
44 |
return render_fault(request, Unauthorized('User inactive')) |
|
45 |
|
|
46 |
# Check if the token has expired. |
|
47 |
if (time() - mktime(user.auth_token_expires.timetuple())) > 0: |
|
48 |
return render_fault(request, Unauthorized('Authentication expired')) |
|
49 |
|
|
50 |
response = HttpResponse() |
|
51 |
response.status=204 |
|
52 |
user_info = user.__dict__ |
|
53 |
for k,v in user_info.items(): |
|
54 |
if isinstance(v, datetime.datetime): |
|
55 |
user_info[k] = v.strftime('%a, %d-%b-%Y %H:%M:%S %Z') |
|
56 |
user_info.pop('_state') |
|
57 |
response.content = json.dumps(user_info) |
|
58 |
update_response_headers(response) |
|
59 |
return response |
|
60 |
except BaseException, e: |
|
61 |
fault = ServiceUnavailable('Unexpected error') |
|
62 |
return render_fault(request, fault) |
b/pithos/im/faults.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
def camelCase(s): |
|
35 |
return s[0].lower() + s[1:] |
|
36 |
|
|
37 |
class Fault(Exception): |
|
38 |
def __init__(self, message='', details='', name=''): |
|
39 |
Exception.__init__(self, message, details, name) |
|
40 |
self.message = message |
|
41 |
self.details = details |
|
42 |
self.name = name or camelCase(self.__class__.__name__) |
|
43 |
|
|
44 |
class BadRequest(Fault): |
|
45 |
code = 400 |
|
46 |
|
|
47 |
class Unauthorized(Fault): |
|
48 |
code = 401 |
|
49 |
|
|
50 |
class ServiceUnavailable(Fault): |
|
51 |
code = 503 |
b/pithos/im/urls.py | ||
---|---|---|
100 | 100 |
(r'^login/twitter/?$', 'twitter.login'), |
101 | 101 |
(r'^login/twitter/authenticated/?$', 'twitter.authenticated') |
102 | 102 |
) |
103 |
|
|
104 |
urlpatterns += patterns('pithos.im.api', |
|
105 |
(r'^authenticate/?$', 'authenticate') |
|
106 |
) |
|
107 |
|
b/pithos/lib/client.py | ||
---|---|---|
932 | 932 |
headers[quote(k)] = quote(v, safe='/=,:@ *"') if type(v) == types.StringType else v |
933 | 933 |
return headers |
934 | 934 |
|
935 |
def _handle_response(response, verbose, debug):
|
|
935 |
def _handle_response(response, verbose=False, debug=False):
|
|
936 | 936 |
headers = response.getheaders() |
937 | 937 |
headers = dict((unquote(h), unquote(v)) for h,v in headers) |
938 | 938 |
|
... | ... | |
952 | 952 |
raise Fault(data, int(response.status)) |
953 | 953 |
|
954 | 954 |
#print '**', response.status, headers, data, '\n' |
955 |
return response.status, headers, data |
|
955 |
return response.status, headers, data |
|
956 |
|
|
957 |
def authenticate(authentication_host, token): |
|
958 |
con = HTTPConnection(authentication_host) |
|
959 |
kwargs = {} |
|
960 |
kwargs['headers'] = {} |
|
961 |
kwargs['headers']['X-Auth-Token'] = token |
|
962 |
kwargs['headers']['Content-Length'] = 0 |
|
963 |
|
|
964 |
path = '/im/authenticate' |
|
965 |
con.request('GET', path, **kwargs) |
|
966 |
response = con.getresponse() |
|
967 |
return _handle_response(response) |
b/pithos/middleware/auth.py | ||
---|---|---|
33 | 33 |
|
34 | 34 |
from time import time, mktime |
35 | 35 |
from urllib import quote, unquote |
36 |
from django.conf import settings |
|
37 |
from django.utils import simplejson as json |
|
36 | 38 |
|
37 | 39 |
from pithos.im.models import User |
38 |
|
|
40 |
from pithos.lib.client import authenticate, Fault |
|
39 | 41 |
|
40 | 42 |
def get_user_from_token(token): |
43 |
if not token: |
|
44 |
return None |
|
45 |
|
|
46 |
host = settings.AUTHENTICATION_HOST |
|
41 | 47 |
try: |
42 |
return User.objects.get(auth_token=token) |
|
43 |
except User.DoesNotExist: |
|
48 |
status, headers, user = authenticate(host, token) |
|
49 |
return json.loads(user) |
|
50 |
except Fault, f: |
|
44 | 51 |
return None |
45 | 52 |
|
46 |
|
|
47 | 53 |
class AuthMiddleware(object): |
48 | 54 |
def process_request(self, request): |
49 |
request.user = None
|
|
50 |
request.user_uniq = None
|
|
51 |
|
|
52 |
# Try to find token in a parameter, in a request header, or in a cookie.
|
|
53 |
user = get_user_from_token(request.GET.get('X-Auth-Token'))
|
|
54 |
if not user:
|
|
55 |
user = get_user_from_token(request.META.get('HTTP_X_AUTH_TOKEN')) |
|
56 |
if not user:
|
|
57 |
# Back from an im login target.
|
|
58 |
if request.GET.get('user', None): |
|
59 |
token = request.GET.get('token', None)
|
|
60 |
if token:
|
|
61 |
request.set_auth_cookie = True
|
|
62 |
user = get_user_from_token(token)
|
|
55 |
try:
|
|
56 |
host, port = settings.AUTHENTICATION_HOST.split(':')
|
|
57 |
if request.META['SERVER_NAME'] and request.META['SERVER_PORT'] == port: |
|
58 |
#bypass authentication
|
|
59 |
#if it is an authentication request
|
|
60 |
return
|
|
61 |
|
|
62 |
request.user = None
|
|
63 |
request.user_uniq = None
|
|
64 |
|
|
65 |
# Try to find token in a parameter, in a request header, or in a cookie.
|
|
66 |
user = get_user_from_token(request.GET.get('X-Auth-Token'))
|
|
67 |
if not user:
|
|
68 |
user = get_user_from_token(request.META.get('HTTP_X_AUTH_TOKEN'))
|
|
63 | 69 |
if not user: |
64 |
cookie_value = unquote(request.COOKIES.get('_pithos2_a', '')) |
|
65 |
if cookie_value and '|' in cookie_value: |
|
66 |
token = cookie_value.split('|', 1)[1] |
|
70 |
# Back from an im login target. |
|
71 |
if request.GET.get('user', None): |
|
72 |
token = request.GET.get('token', None) |
|
73 |
if token: |
|
74 |
request.set_auth_cookie = True |
|
67 | 75 |
user = get_user_from_token(token) |
68 |
if not user: |
|
69 |
return |
|
70 |
|
|
71 |
# Check if the is active. |
|
72 |
if user.state != 'ACTIVE': |
|
73 |
return |
|
74 |
|
|
75 |
# Check if the token has expired. |
|
76 |
if (time() - mktime(user.auth_token_expires.timetuple())) > 0: |
|
77 |
return |
|
78 |
|
|
79 |
request.user = user |
|
80 |
request.user_uniq = user.uniq |
|
76 |
if not user: |
|
77 |
cookie_value = unquote(request.COOKIES.get('_pithos2_a', '')) |
|
78 |
if cookie_value and '|' in cookie_value: |
|
79 |
token = cookie_value.split('|', 1)[1] |
|
80 |
user = get_user_from_token(token) |
|
81 |
if not user: |
|
82 |
return |
|
83 |
|
|
84 |
request.user = user |
|
85 |
request.user_uniq = user['uniq'] |
|
86 |
except Exception, e: |
|
87 |
print e |
|
81 | 88 |
|
82 | 89 |
def process_response(self, request, response): |
83 | 90 |
if getattr(request, 'user', None) and getattr(request, 'set_auth_cookie', False): |
b/pithos/settings.d/00-apps.conf | ||
---|---|---|
25 | 25 |
'pithos.ui', |
26 | 26 |
'south' |
27 | 27 |
) |
28 |
|
|
29 |
#where astakos is hosted |
|
30 |
AUTHENTICATION_HOST = '127.0.0.1:10000' |
Also available in: Unified diff