Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api.py @ 8569eb53

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
                 'groups':[g.name for g in user.groups.all()]}
128
    response.content = json.dumps(user_info)
129
    response['Content-Type'] = 'application/json; charset=UTF-8'
130
    response['Content-Length'] = len(response.content)
131
    return response
132

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

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

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

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

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

    
181
@api_method()
182
def get_menu(request, with_extra_links=False, with_signout=True):
183
    exclude = []
184
    index_url = reverse('index')
185
    absolute = lambda (url): request.build_absolute_uri(url)
186
    l = [{ 'url': absolute(index_url), 'name': "Sign in"}]
187
    cookie = urllib.unquote(request.COOKIES.get(COOKIE_NAME, ''))
188
    email = cookie.partition('|')[0]
189
    try:
190
        user = AstakosUser.objects.get(email=email, is_active=True)
191
    except AstakosUser.DoesNotExist:
192
        pass
193
    else:
194
        l = []
195
        l.append({ 'url': absolute(reverse('astakos.im.views.index')),
196
                  'name': user.email})
197
        l.append({ 'url': absolute(reverse('astakos.im.views.edit_profile')),
198
                  'name': "My account" })
199
        if with_extra_links:
200
            if user.has_usable_password():
201
                l.append({ 'url': absolute(reverse('password_change')),
202
                          'name': "Change password" })
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