Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api.py @ 30dc8c1a

History | View | Annotate | Download (11.6 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, \
49
Fault, ItemNotFound, Forbidden
50
from astakos.im.models import AstakosUser
51
from astakos.im.settings import CLOUD_SERVICES, INVITATIONS_ENABLED, COOKIE_NAME, \
52
EMAILCHANGE_ENABLED
53
from astakos.im.util import epoch
54

    
55
logger = logging.getLogger(__name__)
56
format = ('%a, %d %b %Y %H:%M:%S GMT')
57

    
58
def render_fault(request, fault):
59
    if isinstance(fault, InternalServerError) and settings.DEBUG:
60
        fault.details = format_exc(fault)
61

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

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

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

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

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

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

    
173
@api_method(http_method='GET')
174
def get_services(request):
175
    callback = request.GET.get('callback', None)
176
    data = json.dumps(CLOUD_SERVICES)
177
    mimetype = 'application/json'
178

    
179
    if callback:
180
        mimetype = 'application/javascript'
181
        data = '%s(%s)' % (callback, data)
182

    
183
    return HttpResponse(content=data, mimetype=mimetype)
184

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

    
222
    if callback:
223
        mimetype = 'application/javascript'
224
        data = '%s(%s)' % (callback, data)
225

    
226
    return HttpResponse(content=data, mimetype=mimetype)
227

    
228
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
229
def get_user_by_email(request, user=None):
230
    # Normal Response Codes: 200
231
    # Error Response Codes: internalServerError (500)
232
    #                       badRequest (400)
233
    #                       unauthorised (401)
234
    #                       forbidden (403)
235
    #                       itemNotFound (404)
236
    email = request.GET.get('name')
237
    if not email:
238
        raise BadRequest('Email missing')
239
    try:
240
        user = AstakosUser.objects.get(email = email)
241
    except AstakosUser.DoesNotExist, e:
242
        raise ItemNotFound('Invalid email')
243
    
244
    if not user.is_active:
245
        raise ItemNotFound('Inactive user')
246
    else:
247
        response = HttpResponse()
248
        response.status=200
249
        user_info = {'id':user.id,
250
                     'username':user.username,
251
                     'email':[user.email],
252
                     'enabled':user.is_active,
253
                     'name':user.realname,
254
                     'auth_token_created':user.auth_token_created.strftime(format),
255
                     'auth_token_expires':user.auth_token_expires.strftime(format),
256
                     'has_credits':user.has_credits,
257
                     'groups':[g.name for g in user.groups.all()],
258
                     'user_permissions':[p.codename for p in user.user_permissions.all()],
259
                     'group_permissions': list(user.get_group_permissions())}
260
        response.content = json.dumps(user_info)
261
        response['Content-Type'] = 'application/json; charset=UTF-8'
262
        response['Content-Length'] = len(response.content)
263
        return response
264

    
265
@api_method(http_method='GET', token_required=True, perms=['can_access_userinfo'])
266
def get_user_by_username(request, user_id, user=None):
267
    # Normal Response Codes: 200
268
    # Error Response Codes: internalServerError (500)
269
    #                       badRequest (400)
270
    #                       unauthorised (401)
271
    #                       forbidden (403)
272
    #                       itemNotFound (404)
273
    try:
274
        user = AstakosUser.objects.get(username = user_id)
275
    except AstakosUser.DoesNotExist, e:
276
        raise ItemNotFound('Invalid userid')
277
    else:
278
        response = HttpResponse()
279
        response.status=200
280
        user_info = {'id':user.id,
281
                     'username':user.username,
282
                     'email':[user.email],
283
                     'name':user.realname,
284
                     'auth_token_created':user.auth_token_created.strftime(format),
285
                     'auth_token_expires':user.auth_token_expires.strftime(format),
286
                     'has_credits':user.has_credits,
287
                     'enabled':user.is_active,
288
                     'groups':[g.name for g in user.groups.all()]}
289
        response.content = json.dumps(user_info)
290
        response['Content-Type'] = 'application/json; charset=UTF-8'
291
        response['Content-Length'] = len(response.content)
292
        return response