Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api.py @ 63fa03fe

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

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

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

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

    
243
@api_method(http_method='GET', token_required=True, perms=['astakos.im.can_find_email'])
244
def find_email(request):
245
    # Normal Response Codes: 204
246
    # Error Response Codes: internalServerError (500)
247
    #                       badRequest (400)
248
    #                       unauthorised (401)
249
    userid = request.GET.get('userid')
250
    if not userid:
251
        raise BadRequest('Userid missing')
252
    try:
253
        user = AstakosUser.objects.get(username = userid)
254
    except AstakosUser.DoesNotExist, e:
255
        raise BadRequest('Invalid userid')
256
    else:
257
        response = HttpResponse()
258
        response.status=204
259
        user_info = {'userid':user.email}
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