Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api.py @ 672d445a

History | View | Annotate | Download (9.9 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

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

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

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

    
52
logger = logging.getLogger(__name__)
53

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

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

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

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

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

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

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

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

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

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

    
179
@api_method()
180
def get_menu(request, with_extra_links=False, with_signout=True):
181
    exclude = []
182
    index_url = reverse('index')
183
    absolute = lambda (url): request.build_absolute_uri(url)
184
    l = [{ 'url': absolute(index_url), 'name': "Sign in"}]
185
    if request.user.is_authenticated():
186
        l = []
187
        l.append({ 'url': absolute(reverse('astakos.im.views.index')),
188
                  'name': request.user.email})
189
        l.append({ 'url': absolute(reverse('astakos.im.views.edit_profile')),
190
                  'name': "My account" })
191
        if with_extra_links:
192
            if request.user.has_usable_password():
193
                l.append({ 'url': absolute(reverse('password_change')),
194
                          'name': "Change password" })
195
            if INVITATIONS_ENABLED:
196
                l.append({ 'url': absolute(reverse('astakos.im.views.invite')),
197
                          'name': "Invitations" })
198
            l.append({ 'url': absolute(reverse('astakos.im.views.feedback')),
199
                      'name': "Feedback" })
200
        if with_signout:
201
            l.append({ 'url': absolute(reverse('astakos.im.views.logout')),
202
                      'name': "Sign out"})
203
    
204
    callback = request.GET.get('callback', None)
205
    data = json.dumps(tuple(l))
206
    mimetype = 'application/json'
207

    
208
    if callback:
209
        mimetype = 'application/javascript'
210
        data = '%s(%s)' % (callback, data)
211

    
212
    return HttpResponse(content=data, mimetype=mimetype)
213

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

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