Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api.py @ 49790d9d

History | View | Annotate | Download (10.2 kB)

1
# Copyright 2011-2012 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
import logging
35
import urllib
36

    
37
from functools import wraps
38
from traceback import format_exc
39
from time import time, mktime
40
from urllib import quote
41
from urlparse import urlparse
42

    
43
from django.conf import settings
44
from django.http import HttpResponse
45
from django.utils import simplejson as json
46
from django.core.urlresolvers import reverse
47

    
48
from astakos.im.faults import BadRequest, Unauthorized, InternalServerError, Fault
49
from astakos.im.models import AstakosUser
50
from astakos.im.settings import CLOUD_SERVICES, INVITATIONS_ENABLED, COOKIE_NAME
51
from astakos.im.util import epoch
52

    
53
logger = logging.getLogger(__name__)
54

    
55
def render_fault(request, fault):
56
    if isinstance(fault, InternalServerError) and settings.DEBUG:
57
        fault.details = format_exc(fault)
58

    
59
    request.serialization = 'text'
60
    data = fault.message + '\n'
61
    if fault.details:
62
        data += '\n' + fault.details
63
    response = HttpResponse(data, status=fault.code)
64
    response['Content-Length'] = len(response.content)
65
    return response
66

    
67
def api_method(http_method=None, token_required=False, perms=[]):
68
    """Decorator function for views that implement an API method."""
69
    
70
    def decorator(func):
71
        @wraps(func)
72
        def wrapper(request, *args, **kwargs):
73
            try:
74
                if http_method and request.method != http_method:
75
                    raise BadRequest('Method not allowed.')
76
                x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
77
                if token_required:
78
                    if not x_auth_token:
79
                        raise Unauthorized('Access denied')
80
                    try:
81
                        user = AstakosUser.objects.get(auth_token=x_auth_token)
82
                        if not user.has_perms(perms):
83
                            raise Unauthorized('Unauthorized request')
84
                    except AstakosUser.DoesNotExist, e:
85
                        raise Unauthorized('Invalid X-Auth-Token')
86
                    kwargs['user'] = user
87
                response = func(request, *args, **kwargs)
88
                return response
89
            except Fault, fault:
90
                return render_fault(request, fault)
91
            except BaseException, e:
92
                logger.exception('Unexpected error: %s' % e)
93
                fault = InternalServerError('Unexpected error')
94
                return render_fault(request, fault)
95
        return wrapper
96
    return decorator
97

    
98
@api_method(http_method='GET', token_required=True)
99
def authenticate_old(request, user=None):
100
    # Normal Response Codes: 204
101
    # Error Response Codes: internalServerError (500)
102
    #                       badRequest (400)
103
    #                       unauthorised (401)
104
    if not user:
105
        raise BadRequest('No user')
106
    
107
    # Check if the is active.
108
    if not user.is_active:
109
        raise Unauthorized('User inactive')
110

    
111
    # Check if the token has expired.
112
    if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
113
        raise Unauthorized('Authentication expired')
114
    
115
    if not user.signed_terms():
116
        raise Unauthorized('Pending approval terms')
117
    
118
    response = HttpResponse()
119
    response.status=204
120
    user_info = {'username':user.username,
121
                 'uniq':user.email,
122
                 'auth_token':user.auth_token,
123
                 'auth_token_created':user.auth_token_created.isoformat(),
124
                 'auth_token_expires':user.auth_token_expires.isoformat(),
125
                 'has_credits':user.has_credits,
126
                 'has_signed_terms':user.signed_terms()}
127
    response.content = json.dumps(user_info)
128
    response['Content-Type'] = 'application/json; charset=UTF-8'
129
    response['Content-Length'] = len(response.content)
130
    return response
131

    
132
@api_method(http_method='GET', token_required=True)
133
def authenticate(request, user=None):
134
    # Normal Response Codes: 204
135
    # Error Response Codes: internalServerError (500)
136
    #                       badRequest (400)
137
    #                       unauthorised (401)
138
    if not user:
139
        raise BadRequest('No user')
140
    
141
    # Check if the is active.
142
    if not user.is_active:
143
        raise Unauthorized('User inactive')
144

    
145
    # Check if the token has expired.
146
    if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
147
        raise Unauthorized('Authentication expired')
148
    
149
    if not user.signed_terms():
150
        raise Unauthorized('Pending approval terms')
151
    
152
    response = HttpResponse()
153
    response.status=204
154
    user_info = {'userid':user.username,
155
                 'email':[user.email],
156
                 'name':user.realname,
157
                 'auth_token':user.auth_token,
158
                 'auth_token_created':epoch(user.auth_token_created),
159
                 'auth_token_expires':epoch(user.auth_token_expires),
160
                 'has_credits':user.has_credits,
161
                 'is_active':user.is_active,
162
                 'groups':[g.name for g in user.groups.all()]}
163
    response.content = json.dumps(user_info)
164
    response['Content-Type'] = 'application/json; charset=UTF-8'
165
    response['Content-Length'] = len(response.content)
166
    return response
167

    
168
@api_method(http_method='GET')
169
def get_services(request):
170
    callback = request.GET.get('callback', None)
171
    data = json.dumps(CLOUD_SERVICES)
172
    mimetype = 'application/json'
173

    
174
    if callback:
175
        mimetype = 'application/javascript'
176
        data = '%s(%s)' % (callback, data)
177

    
178
    return HttpResponse(content=data, mimetype=mimetype)
179

    
180
@api_method()
181
def get_menu(request, with_extra_links=False, with_signout=True):
182
    index_url = reverse('index')
183
    absolute = lambda (url): request.build_absolute_uri(url)
184
    l = [{ 'url': absolute(index_url), 'name': "Sign in"}]
185
    cookie = urllib.unquote(request.COOKIES.get(COOKIE_NAME, ''))
186
    email = cookie.partition('|')[0]
187
    try:
188
        user = AstakosUser.objects.get(email=email, is_active=True)
189
    except AstakosUser.DoesNotExist:
190
        pass
191
    else:
192
        l = []
193
        l.append({ 'url': absolute(reverse('astakos.im.views.index')),
194
                  'name': user.email})
195
        l.append({ 'url': absolute(reverse('astakos.im.views.edit_profile')),
196
                  'name': "My account" })
197
        if with_extra_links:
198
            if user.has_usable_password():
199
                l.append({ 'url': absolute(reverse('password_change')),
200
                          'name': "Change password" })
201
            l.append({'url':absolute(reverse('email_change')),
202
                      'name': "Change email"})
203
            if INVITATIONS_ENABLED:
204
                l.append({ 'url': absolute(reverse('astakos.im.views.invite')),
205
                          'name': "Invitations" })
206
            l.append({ 'url': absolute(reverse('astakos.im.views.feedback')),
207
                      'name': "Feedback" })
208
        if with_signout:
209
            l.append({ 'url': absolute(reverse('astakos.im.views.logout')),
210
                      'name': "Sign out"})
211
    
212
    callback = request.GET.get('callback', None)
213
    data = json.dumps(tuple(l))
214
    mimetype = 'application/json'
215

    
216
    if callback:
217
        mimetype = 'application/javascript'
218
        data = '%s(%s)' % (callback, data)
219

    
220
    return HttpResponse(content=data, mimetype=mimetype)
221

    
222
@api_method(http_method='GET', token_required=True, perms=['astakos.im.can_find_userid'])
223
def find_userid(request):
224
    # Normal Response Codes: 204
225
    # Error Response Codes: internalServerError (500)
226
    #                       badRequest (400)
227
    #                       unauthorised (401)
228
    email = request.GET.get('email')
229
    if not email:
230
        raise BadRequest('Email missing')
231
    try:
232
        user = AstakosUser.objects.get(email = email, is_active=True)
233
    except AstakosUser.DoesNotExist, e:
234
        raise BadRequest('Invalid email')
235
    else:
236
        response = HttpResponse()
237
        response.status=204
238
        user_info = {'userid':user.username}
239
        response.content = json.dumps(user_info)
240
        response['Content-Type'] = 'application/json; charset=UTF-8'
241
        response['Content-Length'] = len(response.content)
242
        return response
243

    
244
@api_method(http_method='GET', token_required=True, perms=['astakos.im.can_find_email'])
245
def find_email(request):
246
    # Normal Response Codes: 204
247
    # Error Response Codes: internalServerError (500)
248
    #                       badRequest (400)
249
    #                       unauthorised (401)
250
    userid = request.GET.get('userid')
251
    if not userid:
252
        raise BadRequest('Userid missing')
253
    try:
254
        user = AstakosUser.objects.get(username = userid)
255
    except AstakosUser.DoesNotExist, e:
256
        raise BadRequest('Invalid userid')
257
    else:
258
        response = HttpResponse()
259
        response.status=204
260
        user_info = {'userid':user.email}
261
        response.content = json.dumps(user_info)
262
        response['Content-Type'] = 'application/json; charset=UTF-8'
263
        response['Content-Length'] = len(response.content)
264
        return response