Revision 7376ef80

b/snf-astakos-app/astakos/im/api/__init__.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
import logging
35
import urllib
36

  
37
from traceback import format_exc
38

  
34 39
from django.http import HttpResponse
35 40
from django.utils import simplejson as json
41
from django.conf import settings
42
from django.core.urlresolvers import reverse
36 43

  
37
from astakos.im.models import AstakosUser
38
from astakos.im.api.faults import ItemNotFound
44
from astakos.im.models import AstakosUser, Service
45
from astakos.im.api.faults import Fault, ItemNotFound, InternalServerError, Unauthorized, BadRequest
46
from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
39 47

  
48
logger = logging.getLogger(__name__)
40 49
format = ('%a, %d %b %Y %H:%M:%S GMT')
41 50

  
51
class apiMethod(object):
52
    def __init__(self, http_method=None, token_required=False, perms=None):
53
        self.http_method = http_method
54
        self.token_required = token_required
55
        self.perms = perms if perms else ()
56

  
57
    def __call__(self, func):
58
        def wrapped_f(request, *args, **kwargs):
59
            try:
60
                if self.http_method and request.method != self.http_method:
61
                    raise BadRequest('Method not allowed.')
62
                x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
63
                if self.token_required:
64
                    if not x_auth_token:
65
                        raise Unauthorized('Access denied')
66
                    kwargs['user'] = self._get_token_owner(x_auth_token)
67
                response = func(request, *args, **kwargs)
68
                return response
69
            except Fault, fault:
70
                return render_fault(request, fault)
71
            except BaseException, e:
72
                logger.exception('Unexpected error: %s' % e)
73
                fault = InternalServerError('Unexpected error')
74
                return render_fault(request, fault)
75
        return wrapped_f
76
    
77
    def _get_token_owner(self, x_auth_token):
78
        return
79

  
80
def render_fault(request, fault):
81
    if isinstance(fault, InternalServerError) and settings.DEBUG:
82
        fault.details = format_exc(fault)
83

  
84
    request.serialization = 'text'
85
    data = fault.message + '\n'
86
    if fault.details:
87
        data += '\n' + fault.details
88
    response = HttpResponse(data, status=fault.code)
89
    response['Content-Length'] = len(response.content)
90
    return response
91

  
42 92
def _get_user_by_username(user_id):
43 93
    try:
44 94
        user = AstakosUser.objects.get(username = user_id)
......
87 137
        response.content = json.dumps(user_info)
88 138
        response['Content-Type'] = 'application/json; charset=UTF-8'
89 139
        response['Content-Length'] = len(response.content)
90
        return response
140
        return response
141

  
142
@apiMethod(http_method='GET')
143
def get_services(request):
144
    callback = request.GET.get('callback', None)
145
    services = Service.objects.all()
146
    data = tuple({'name':s.name, 'url':s.url, 'icon':s.icon} for s in services)
147
    data = json.dumps(data)
148
    mimetype = 'application/json'
149

  
150
    if callback:
151
        mimetype = 'application/javascript'
152
        data = '%s(%s)' % (callback, data)
153

  
154
    return HttpResponse(content=data, mimetype=mimetype)
155

  
156
@apiMethod()
157
def get_menu(request, with_extra_links=False, with_signout=True):
158
    index_url = reverse('index')
159
    absolute = lambda (url): request.build_absolute_uri(url)
160
    l = [{ 'url': absolute(index_url), 'name': "Sign in"}]
161
    cookie = urllib.unquote(request.COOKIES.get(COOKIE_NAME, ''))
162
    email = cookie.partition('|')[0]
163
    try:
164
        user = AstakosUser.objects.get(email=email, is_active=True)
165
    except AstakosUser.DoesNotExist:
166
        pass
167
    else:
168
        l = []
169
        l.append({ 'url': absolute(reverse('astakos.im.views.index')),
170
                  'name': user.email})
171
        l.append({ 'url': absolute(reverse('astakos.im.views.edit_profile')),
172
                  'name': "My account" })
173
        if with_extra_links:
174
            if user.has_usable_password():
175
                l.append({ 'url': absolute(reverse('password_change')),
176
                          'name': "Change password" })
177
            if EMAILCHANGE_ENABLED:
178
                l.append({'url':absolute(reverse('email_change')),
179
                          'name': "Change email"})
180
            if INVITATIONS_ENABLED:
181
                l.append({ 'url': absolute(reverse('astakos.im.views.invite')),
182
                          'name': "Invitations" })
183
            l.append({ 'url': absolute(reverse('astakos.im.views.feedback')),
184
                      'name': "Feedback" })
185
        if with_signout:
186
            l.append({ 'url': absolute(reverse('astakos.im.views.logout')),
187
                      'name': "Sign out"})
188
    
189
    callback = request.GET.get('callback', None)
190
    data = json.dumps(tuple(l))
191
    mimetype = 'application/json'
192

  
193
    if callback:
194
        mimetype = 'application/javascript'
195
        data = '%s(%s)' % (callback, data)
196

  
197
    return HttpResponse(content=data, mimetype=mimetype)
b/snf-astakos-app/astakos/im/api/admin.py
34 34
import logging
35 35
import urllib
36 36

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

  
44
from django.conf import settings
45 39
from django.http import HttpResponse
46 40
from django.utils import simplejson as json
47
from django.core.urlresolvers import reverse
48 41

  
49 42
from astakos.im.api.faults import *
43
from astakos.im.api import _get_user_by_email, _get_user_by_username, apiMethod
50 44
from astakos.im.models import AstakosUser, Service
51
from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
52 45
from astakos.im.util import epoch
53
from astakos.im.api import _get_user_by_email, _get_user_by_username
54 46

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

  
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
                        ## Check if the token has expired.
88
                        #if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
89
                        #    raise Unauthorized('Authentication expired')
90
                        if not user.has_perms(perms):
91
                            raise Forbidden('Unauthorized request')
92
                    except AstakosUser.DoesNotExist, e:
93
                        raise Unauthorized('Invalid X-Auth-Token')
94
                    kwargs['user'] = user
95
                response = func(request, *args, **kwargs)
96
                return response
97
            except Fault, fault:
98
                return render_fault(request, fault)
99
            except BaseException, e:
100
                logger.exception('Unexpected error: %s' % e)
101
                fault = InternalServerError('Unexpected error')
102
                return render_fault(request, fault)
103
        return wrapper
104
    return decorator
105

  
106
@api_method(http_method='GET', token_required=True)
50
class admin_api_method(apiMethod):
51
    def _get_token_owner(self, x_auth_token):
52
        try:
53
            user = AstakosUser.objects.get(auth_token=x_auth_token)
54
            # Check if the token has expired.
55
            if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
56
                raise Unauthorized('Authentication expired')
57
            if not user.has_perms(self.perms):
58
                raise Forbidden('Unauthorized request')
59
            return user
60
        except AstakosUser.DoesNotExist, e:
61
            raise Unauthorized('Invalid X-Auth-Token')
62

  
63
@admin_api_method(http_method='GET', token_required=True)
107 64
def authenticate_old(request, user=None):
108 65
    # Normal Response Codes: 204
109 66
    # Error Response Codes: internalServerError (500)
......
116 73
    if not user.is_active:
117 74
        raise Unauthorized('User inactive')
118 75

  
119
    # Check if the token has expired.
120
    if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
121
        raise Unauthorized('Authentication expired')
122

  
123 76
    if not user.signed_terms():
124 77
        raise Unauthorized('Pending approval terms')
125 78

  
......
138 91
    response['Content-Length'] = len(response.content)
139 92
    return response
140 93

  
141
@api_method(http_method='GET', token_required=True)
94
@admin_api_method(http_method='GET', token_required=True)
142 95
def authenticate(request, user=None):
143 96
    # Normal Response Codes: 204
144 97
    # Error Response Codes: internalServerError (500)
......
151 104
    if not user.is_active:
152 105
        raise Unauthorized('User inactive')
153 106

  
154
    # Check if the token has expired.
155
    if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
156
        raise Unauthorized('Authentication expired')
157

  
158 107
    if not user.signed_terms():
159 108
        raise Unauthorized('Pending approval terms')
160 109

  
161 110
    response = HttpResponse()
162 111
    response.status=204
163
    user_info = {'userid':user.username,
112
    user_info = {'uniq':user.username,
164 113
                 'email':[user.email],
165 114
                 'name':user.realname,
166 115
                 'auth_token':user.auth_token,
......
174 123
    response['Content-Length'] = len(response.content)
175 124
    return response
176 125

  
177
@api_method(http_method='GET')
178
def get_services(request):
179
    callback = request.GET.get('callback', None)
180
    services = Service.objects.all()
181
    data = tuple({'id':s.pk, 'name':s.name, 'url':s.url, 'icon':s.icon} for s in services)
182
    data = json.dumps(data)
183
    mimetype = 'application/json'
184

  
185
    if callback:
186
        mimetype = 'application/javascript'
187
        data = '%s(%s)' % (callback, data)
188

  
189
    return HttpResponse(content=data, mimetype=mimetype)
190

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

  
224
    callback = request.GET.get('callback', None)
225
    data = json.dumps(tuple(l))
226
    mimetype = 'application/json'
227

  
228
    if callback:
229
        mimetype = 'application/javascript'
230
        data = '%s(%s)' % (callback, data)
231

  
232
    return HttpResponse(content=data, mimetype=mimetype)
233

  
234
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
126
@admin_api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
235 127
def get_user_by_email(request, user=None):
236 128
    # Normal Response Codes: 200
237 129
    # Error Response Codes: internalServerError (500)
......
242 134
    email = request.GET.get('name')
243 135
    return _get_user_by_email(email)
244 136

  
245
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
137
@admin_api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
246 138
def get_user_by_username(request, user_id, user=None):
247 139
    # Normal Response Codes: 200
248 140
    # Error Response Codes: internalServerError (500)
b/snf-astakos-app/astakos/im/api/service.py
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
import logging
35
import urllib
36 35

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

  
44
from django.conf import settings
45 38
from django.http import HttpResponse
46
from django.core.urlresolvers import reverse
39
from django.utils import simplejson as json
47 40
from django.views.decorators.csrf import csrf_exempt
48 41

  
49 42
from astakos.im.api.faults import *
43
from astakos.im.api import _get_user_by_email, _get_user_by_username, apiMethod
50 44
from astakos.im.models import AstakosUser, Service
51
from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
52 45
from astakos.im.util import epoch
53 46
from astakos.im.forms import FeedbackForm
54 47
from astakos.im.functions import send_feedback as send_feedback_func, SendMailError
55 48

  
56 49
logger = logging.getLogger(__name__)
57 50

  
58
def render_fault(request, fault):
59
    if isinstance(fault, InternalServerError) and settings.DEBUG:
60
        fault.details = format_exc(fault)
51
class service_api_method(apiMethod):
52
    def _get_token_owner(self, x_auth_token):
53
        try:
54
            service = Service.objects.get(auth_token=x_auth_token)
55
            
56
            # Check if the token has expired.
57
            if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
58
                raise Unauthorized('Authentication expired')
59
            
60
            return service
61
        except Service.DoesNotExist, e:
62
            raise Unauthorized('Invalid X-Auth-Token')
61 63

  
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):
71
    """Decorator function for views that implement an API method."""
72
    def decorator(func):
73
        @wraps(func)
74
        def wrapper(request, *args, **kwargs):
75
            try:
76
                if http_method and request.method != http_method:
77
                    raise BadRequest('Method not allowed.')
78
                x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
79
                if token_required:
80
                    if not x_auth_token:
81
                        raise Unauthorized('Access denied')
82
                    try:
83
                        service = Service.objects.get(auth_token=x_auth_token)
84
                        
85
                        # Check if the token has expired.
86
                        if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
87
                            raise Unauthorized('Authentication expired')
88
                    except Service.DoesNotExist, e:
89
                        raise Unauthorized('Invalid X-Auth-Token')
90
                response = func(request, *args, **kwargs)
91
                return response
92
            except Fault, fault:
93
                return render_fault(request, fault)
94
            except BaseException, e:
95
                logger.exception('Unexpected error: %s' % e)
96
                fault = InternalServerError('Unexpected error')
97
                return render_fault(request, fault)
98
        return wrapper
99
    return decorator
100

  
101
@api_method(http_method='GET', token_required=True)
64
@service_api_method(http_method='GET', token_required=True)
102 65
def get_user_by_email(request, user=None):
103 66
    # Normal Response Codes: 200
104 67
    # Error Response Codes: internalServerError (500)
......
109 72
    email = request.GET.get('name')
110 73
    return _get_user_by_email(email)
111 74

  
112
@api_method(http_method='GET', token_required=True)
75
@service_api_method(http_method='GET', token_required=True)
113 76
def get_user_by_username(request, user_id, user=None):
114 77
    # Normal Response Codes: 200
115 78
    # Error Response Codes: internalServerError (500)
......
120 83
    return _get_user_by_username(user_id)
121 84

  
122 85
@csrf_exempt
123
@api_method(http_method='POST', token_required=True)
86
@service_api_method(http_method='POST', token_required=True)
124 87
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
125 88
    # Normal Response Codes: 200
126 89
    # Error Response Codes: internalServerError (500)
b/snf-astakos-app/astakos/im/context_processors.py
33 33

  
34 34
from astakos.im.settings import IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL, \
35 35
        COOKIE_NAME, LOGIN_MESSAGES, PROFILE_EXTRA_LINKS
36
from astakos.im.api.admin import get_menu
36
from astakos.im.api import get_menu
37 37
from astakos.im.util import get_query
38 38

  
39 39
from django.conf import settings
b/snf-astakos-app/astakos/im/urls.py
92 92
        url(r'^login/twitter/authenticated/?$', 'twitter.authenticated')
93 93
    )
94 94

  
95
urlpatterns += patterns('astakos.im.api.admin',
96
    url(r'^authenticate/?$', 'authenticate_old'),
97
    #url(r'^authenticate/v2/?$', 'authenticate'),
95
urlpatterns += patterns('astakos.im.api',
98 96
    url(r'^get_services/?$', 'get_services'),
99 97
    url(r'^get_menu/?$', 'get_menu'),
98
)
99

  
100
urlpatterns += patterns('astakos.im.api.admin',
101
    url(r'^authenticate/?$', 'authenticate_old'),
102
    url(r'^authenticate/v2.0/?$', 'authenticate'),
100 103
    url(r'^admin/api/v2.0/users/?$', 'get_user_by_email'),
101 104
    url(r'^admin/api/v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
102 105
)

Also available in: Unified diff