Revision ee7a2b87

b/snf-astakos-app/astakos/im/api/__init__.py
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
......
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from functools import wraps
35
from traceback import format_exc
36
from urllib import quote, unquote
34
from functools import partial
37 35

  
38 36
from django.http import HttpResponse
39 37
from django.utils import simplejson as json
40
from django.conf import settings
41 38
from django.core.urlresolvers import reverse
42 39
from django.utils.translation import ugettext as _
43 40
from django.contrib import messages
44 41

  
45
from astakos.im.models import AstakosUser, Service, Resource
42
from astakos.im.models import AstakosUser, Service
43
from snf_django.lib import api
46 44
from snf_django.lib.api import faults
47 45
from astakos.im.settings import (
48 46
    INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL,
......
58 56
absolute = lambda request, url: request.build_absolute_uri(url)
59 57

  
60 58

  
61
def render_fault(request, fault):
62
    if isinstance(fault, faults.InternalServerError) and settings.DEBUG:
63
        fault.details = format_exc(fault)
64

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

  
73

  
74
def api_method(http_method=None):
75
    """Decorator function for views that implement an API method."""
76
    def decorator(func):
77
        @wraps(func)
78
        def wrapper(request, *args, **kwargs):
79
            try:
80
                if http_method and request.method != http_method:
81
                    raise faults.BadRequest('Method not allowed.')
82
                response = func(request, *args, **kwargs)
83
                return response
84
            except faults.Fault, fault:
85
                return render_fault(request, fault)
86
            except BaseException, e:
87
                logger.exception('Unexpected error: %s' % e)
88
                fault = faults.InternalServerError('Unexpected error')
89
                return render_fault(request, fault)
90
        return wrapper
91
    return decorator
59
# Decorator for API methods, using common utils.api_method decorator.
60
# It is used for 'get_services' and 'get_menu' methods that do not
61
# require any sort of authentication
62
api_method = partial(api.api_method, user_required=False,
63
                     token_required=False, logger=logger)
92 64

  
93 65

  
94 66
def get_services_dict():
95
    services = Service.objects.all()
96
    data = tuple({'id': s.pk, 'name': s.name, 'url': s.url, 'icon':
97
                 s.icon} for s in services)
98
    return data
67
    """Return dictionary with information about available Services."""
68
    return list(Service.objects.values("id", "name", "url", "icon"))
69

  
99 70

  
100 71
@api_method(http_method=None)
101 72
def get_services(request):
......
122 93
@api_method()
123 94
def get_menu(request, with_extra_links=False, with_signout=True):
124 95
    user = request.user
125
    from_location = request.GET.get('location')
126 96
    index_url = reverse('index')
127 97

  
128
    l = [{'url': absolute(request, index_url), 'name': _("Sign in")}]
129 98
    if user.is_authenticated():
130 99
        l = []
131 100
        append = l.append
132 101
        item = MenuItem
133 102
        item.current_path = absolute(request, request.path)
134
        append(item(
135
               url=absolute(request, reverse('index')),
136
               name=user.email))
103
        append(item(url=absolute(request, reverse('index')),
104
                    name=user.email))
137 105
        if with_extra_links:
138
            append(item(
139
                url=absolute(request, reverse('landing')),
140
                name="Overview"))
106
            append(item(url=absolute(request, reverse('landing')),
107
                        name="Overview"))
141 108
        if with_signout:
142
            append(item(
143
                   url=absolute(request, reverse('edit_profile')),
144
                   name="Dashboard"))
109
            append(item(url=absolute(request, reverse('edit_profile')),
110
                        name="Dashboard"))
145 111
        if with_extra_links:
146 112
            append(item(url=absolute(request, reverse('edit_profile')),
147
                    name="Profile"))
113
                        name="Profile"))
148 114

  
149 115
        if with_extra_links:
150 116
            if INVITATIONS_ENABLED:
151
                append(item(
152
                       url=absolute(request, reverse('invite')),
153
                       name="Invitations"))
117
                append(item(url=absolute(request, reverse('invite')),
118
                            name="Invitations"))
154 119

  
120
            append(item(url=absolute(request, reverse('resource_usage')),
121
                        name="Usage"))
155 122

  
156
            append(item(
157
                   url=absolute(request, reverse('resource_usage')),
158
                   name="Usage"))
159 123
            if QUOTAHOLDER_URL and PROJECTS_VISIBLE:
160
                append(item(
161
                       url=absolute(request, reverse('project_list')),
162
                       name="Projects"))
124
                append(item(url=absolute(request, reverse('project_list')),
125
                            name="Projects"))
163 126
            #append(item(
164 127
                #url=absolute(request, reverse('api_access')),
165 128
                #name="API Access"))
166 129

  
167
            append(item(
168
                   url=absolute(request, reverse('feedback')),
169
                   name="Contact"))
130
            append(item(url=absolute(request, reverse('feedback')),
131
                        name="Contact"))
170 132
        if with_signout:
171
            append(item(
172
                   url=absolute(request, reverse('logout')),
173
                   name="Sign out"))
133
            append(item(url=absolute(request, reverse('logout')),
134
                        name="Sign out"))
135
    else:
136
        l = [{'url': absolute(request, index_url),
137
              'name': _("Sign in")}]
174 138

  
175 139
    callback = request.GET.get('callback', None)
176 140
    data = json.dumps(tuple(l))
......
217 181
        if name == 'current_path':
218 182
            self.__set_is_active__()
219 183

  
184

  
220 185
def __get_uuid_displayname_catalogs(request, user_call=True):
221 186
    # Normal Response Codes: 200
222 187
    # Error Response Codes: BadRequest (400)
......
232 197
        displaynames = input_data.get('displaynames', [])
233 198
        if displaynames == None and user_call:
234 199
            displaynames = []
235
        d  = {'uuid_catalog':AstakosUser.objects.uuid_catalog(uuids),
236
              'displayname_catalog':AstakosUser.objects.displayname_catalog(displaynames)}
200
        user_obj = AstakosUser.objects
201
        d = {'uuid_catalog': user_obj.uuid_catalog(uuids),
202
             'displayname_catalog': user_obj.displayname_catalog(displaynames)}
237 203

  
238 204
        response = HttpResponse()
239 205
        response.content = json.dumps(d)
......
241 207
        response['Content-Length'] = len(response.content)
242 208
        return response
243 209

  
244
def __send_feedback(request, email_template_name='im/feedback_mail.txt', user=None):
210

  
211
def __send_feedback(request, email_template_name='im/feedback_mail.txt',
212
                    user=None):
245 213
    if not user:
246 214
        auth_token = request.POST.get('auth', '')
247 215
        if not auth_token:
b/snf-astakos-app/astakos/im/api/service.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
import logging
35

  
36
from functools import wraps
37 34
from time import time, mktime
38

  
35
from functools import wraps
39 36
from django.views.decorators.csrf import csrf_exempt
40 37

  
41
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
38
from . import  __get_uuid_displayname_catalogs, __send_feedback
39
from snf_django.lib import api
42 40
from snf_django.lib.api import faults
43 41
from astakos.im.models import Service
44 42

  
43
import logging
45 44
logger = logging.getLogger(__name__)
46 45

  
47 46

  
48
def api_method(http_method=None, token_required=False):
49
    """Decorator function for views that implement an API method."""
50
    def decorator(func):
51
        @wraps(func)
52
        def wrapper(request, *args, **kwargs):
53
            try:
54
                if http_method and request.method != http_method:
55
                    raise faults.BadRequest('Method not allowed.')
56
                x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
57
                if token_required:
58
                    if not x_auth_token:
59
                        raise faults.Unauthorized('Access denied')
60
                    try:
61
                        service = Service.objects.get(auth_token=x_auth_token)
47
def service_from_token(func):
48
    """Decorator for authenticating service by it's token.
62 49

  
63
                        # Check if the token has expired.
64
                        if service.auth_token_expires:
65
                            if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
66
                                raise faults.Unauthorized('Authentication expired')
67
                    except Service.DoesNotExist, e:
68
                        raise faults.Unauthorized('Invalid X-Auth-Token')
69
                response = func(request, *args, **kwargs)
70
                return response
71
            except faults.Fault, fault:
72
                return render_fault(request, fault)
73
            except BaseException, e:
74
                logger.exception('Unexpected error: %s' % e)
75
                fault = faults.InternalServerError('Unexpected error')
76
                return render_fault(request, fault)
77
        return wrapper
78
    return decorator
50
    Check that a service with the corresponding token exists. Also,
51
    if service's token has an expiration token, check that it has not
52
    expired.
53

  
54
    """
55
    @wraps(func)
56
    def wrapper(request, *args, **kwargs):
57
        try:
58
            token = request.x_auth_token
59
        except AttributeError:
60
            raise faults.Unauthorized("No authentication token")
61

  
62
        if not token:
63
            raise faults.Unauthorized("Invalid X-Auth-Token")
64
        try:
65
            service = Service.objects.get(auth_token=token)
66
        except Service.DoesNotExist:
67
            raise faults.Unauthorized("Invalid X-Auth-Token")
68

  
69
        # Check if the token has expired
70
        expiration_date = service.auth_token_expires
71
        if expiration_date:
72
            expires_at = mktime(expiration_date.timetuple())
73
            if time() > expires_at:
74
                raise faults.Unauthorized("Authentication expired")
75

  
76
        return func(request, *args, **kwargs)
77
    return wrapper
79 78

  
80 79

  
81 80
@csrf_exempt
82
@api_method(http_method='POST', token_required=True)
81
@api.api_method(http_method='POST', token_required=True, user_required=False,
82
            logger=logger)
83
@service_from_token  # Authenticate service !!
83 84
def get_uuid_displayname_catalogs(request):
84 85
    # Normal Response Codes: 200
85 86
    # Error Response Codes: internalServerError (500)
86 87
    #                       badRequest (400)
87 88
    #                       unauthorised (401)
88

  
89 89
    return __get_uuid_displayname_catalogs(request, user_call=False)
90 90

  
91 91

  
92 92
@csrf_exempt
93
@api_method(http_method='POST', token_required=True)
93
@api.api_method(http_method='POST', token_required=True, user_required=False,
94
            logger=logger)
95
@service_from_token  # Authenticate service !!
94 96
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
95 97
    # Normal Response Codes: 200
96 98
    # Error Response Codes: internalServerError (500)
97 99
    #                       badRequest (400)
98 100
    #                       unauthorised (401)
99

  
100 101
    return __send_feedback(request, email_template_name)
b/snf-astakos-app/astakos/im/api/user.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
import logging
35

  
36 34
from functools import wraps
37 35
from time import time, mktime
38 36

  
......
40 38
from django.utils import simplejson as json
41 39
from django.views.decorators.csrf import csrf_exempt
42 40

  
41
from snf_django.lib import api
43 42
from snf_django.lib.api import faults
44
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
43
from . import  __get_uuid_displayname_catalogs, __send_feedback
45 44

  
46 45
from astakos.im.models import AstakosUser
47 46
from astakos.im.util import epoch
......
49 48
from astakos.im.api.callpoint import AstakosCallpoint
50 49
callpoint = AstakosCallpoint()
51 50

  
51
import logging
52 52
logger = logging.getLogger(__name__)
53 53
format = ('%a, %d %b %Y %H:%M:%S GMT')
54 54

  
55 55

  
56
def api_method(http_method=None, token_required=False, perms=None):
57
    """Decorator function for views that implement an API method."""
58
    if not perms:
59
        perms = []
60

  
61
    def decorator(func):
62
        @wraps(func)
63
        def wrapper(request, *args, **kwargs):
64
            try:
65
                if http_method and request.method != http_method:
66
                    raise faults.BadRequest('Method not allowed.')
67
                x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
68
                if token_required:
69
                    if not x_auth_token:
70
                        raise faults.Unauthorized('Access denied')
71
                    try:
72
                        user = AstakosUser.objects.get(auth_token=x_auth_token)
73
                        if not user.has_perms(perms):
74
                            raise faults.Forbidden('Unauthorized request')
75
                    except AstakosUser.DoesNotExist, e:
76
                        raise faults.Unauthorized('Invalid X-Auth-Token')
77
                    kwargs['user'] = user
78
                response = func(request, *args, **kwargs)
79
                return response
80
            except faults.Fault, fault:
81
                return render_fault(request, fault)
82
            except BaseException, e:
83
                logger.exception('Unexpected error: %s' % e)
84
                fault = faults.InternalServerError('Unexpected error')
85
                return render_fault(request, fault)
86
        return wrapper
87
    return decorator
88

  
89

  
90
@api_method(http_method='GET', token_required=True)
56
def user_from_token(func):
57
    @wraps(func)
58
    def wrapper(request, *args, **kwargs):
59
        try:
60
            token = request.x_auth_token
61
        except AttributeError:
62
            raise faults.Unauthorized("No authentication token")
63

  
64
        if not token:
65
            raise faults.Unauthorized("Invalid X-Auth-Token")
66

  
67
        try:
68
            user = AstakosUser.objects.get(auth_token=token)
69
        except AstakosUser.DoesNotExist:
70
            raise faults.Unauthorized('Invalid X-Auth-Token')
71

  
72
        return func(request, user, *args, **kwargs)
73
    return wrapper
74

  
75

  
76
@api.api_method(http_method="GET", token_required=True, user_required=False,
77
                  logger=logger)
78
@user_from_token  # Authenticate user!!
91 79
def authenticate(request, user=None):
92 80
    # Normal Response Codes: 200
93 81
    # Error Response Codes: internalServerError (500)
......
136 124

  
137 125

  
138 126
@csrf_exempt
139
@api_method(http_method='POST', token_required=True)
127
@api.api_method(http_method="POST", token_required=True, user_required=False,
128
                  logger=logger)
129
@user_from_token  # Authenticate user!!
140 130
def get_uuid_displayname_catalogs(request, user=None):
141 131
    # Normal Response Codes: 200
142 132
    # Error Response Codes: internalServerError (500)
......
147 137

  
148 138

  
149 139
@csrf_exempt
150
@api_method(http_method='POST', token_required=True)
140
@api.api_method(http_method="POST", token_required=True, user_required=False,
141
                  logger=logger)
142
@user_from_token  # Authenticate user!!
151 143
def send_feedback(request, email_template_name='im/feedback_mail.txt',
152 144
                  user=None):
153 145
    # Normal Response Codes: 200

Also available in: Unified diff