Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / util.py @ 2556cf45

History | View | Annotate | Download (7.7 kB)

1
# Copyright 2013 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
from functools import wraps
35
from time import time, mktime
36
import datetime
37

    
38
from django.http import HttpResponse
39
from django.utils import simplejson as json
40
from django.template.loader import render_to_string
41

    
42
from astakos.im.models import AstakosUser, Component
43
from snf_django.lib.api import faults
44
from snf_django.lib.api.utils import isoformat
45

    
46
from astakos.im.forms import FeedbackForm
47
from astakos.im.functions import send_feedback as send_feedback_func
48

    
49
import logging
50
logger = logging.getLogger(__name__)
51

    
52
absolute = lambda request, url: request.build_absolute_uri(url)
53

    
54

    
55
def _dthandler(obj):
56
    if isinstance(obj, datetime.datetime):
57
        return isoformat(obj)
58
    else:
59
        raise TypeError
60

    
61

    
62
def json_response(content, status_code=None):
63
    response = HttpResponse()
64
    if status_code is not None:
65
        response.status_code = status_code
66

    
67
    response.content = json.dumps(content, default=_dthandler)
68
    response['Content-Type'] = 'application/json; charset=UTF-8'
69
    response['Content-Length'] = len(response.content)
70
    return response
71

    
72

    
73
def xml_response(content, template, status_code=None):
74
    response = HttpResponse()
75
    if status_code is not None:
76
        response.status_code = status_code
77

    
78
    response.content = render_to_string(template, content)
79
    response['Content-Type'] = 'application/xml; charset=UTF-8'
80
    response['Content-Length'] = len(response.content)
81
    return response
82

    
83

    
84
def read_json_body(request, default=None):
85
    body = request.raw_post_data
86
    if not body and request.method == "GET":
87
        body = request.GET.get("body")
88
    if not body:
89
        return default
90
    try:
91
        return json.loads(body)
92
    except json.JSONDecodeError:
93
        raise faults.BadRequest("Request body should be in json format.")
94

    
95

    
96
def is_integer(x):
97
    return isinstance(x, (int, long))
98

    
99

    
100
def are_integer(lst):
101
    return all(map(is_integer, lst))
102

    
103

    
104
def validate_user(user):
105
    # Check if the user is active.
106
    if not user.is_active:
107
        raise faults.Unauthorized('User inactive')
108

    
109
    # Check if the token has expired.
110
    if user.token_expired():
111
        raise faults.Unauthorized('Authentication expired')
112

    
113
    # Check if the user has accepted the terms.
114
    if not user.signed_terms:
115
        raise faults.Unauthorized('Pending approval terms')
116

    
117

    
118
def user_from_token(func):
119
    @wraps(func)
120
    def wrapper(request, *args, **kwargs):
121
        try:
122
            token = request.x_auth_token
123
        except AttributeError:
124
            raise faults.Unauthorized("No authentication token")
125

    
126
        if not token:
127
            raise faults.Unauthorized("Invalid X-Auth-Token")
128

    
129
        try:
130
            user = AstakosUser.objects.get(auth_token=token)
131
        except AstakosUser.DoesNotExist:
132
            raise faults.Unauthorized('Invalid X-Auth-Token')
133

    
134
        validate_user(user)
135

    
136
        request.user = user
137
        return func(request, *args, **kwargs)
138
    return wrapper
139

    
140

    
141
def component_from_token(func):
142
    """Decorator for authenticating component by its token.
143

144
    Check that a component with the corresponding token exists. Also,
145
    if component's token has an expiration token, check that it has not
146
    expired.
147

148
    """
149
    @wraps(func)
150
    def wrapper(request, *args, **kwargs):
151
        try:
152
            token = request.x_auth_token
153
        except AttributeError:
154
            raise faults.Unauthorized("No authentication token")
155

    
156
        if not token:
157
            raise faults.Unauthorized("Invalid X-Auth-Token")
158
        try:
159
            component = Component.objects.get(auth_token=token)
160
        except Component.DoesNotExist:
161
            raise faults.Unauthorized("Invalid X-Auth-Token")
162

    
163
        # Check if the token has expired
164
        expiration_date = component.auth_token_expires
165
        if expiration_date:
166
            expires_at = mktime(expiration_date.timetuple())
167
            if time() > expires_at:
168
                raise faults.Unauthorized("Authentication expired")
169

    
170
        request.component_instance = component
171
        return func(request, *args, **kwargs)
172
    return wrapper
173

    
174

    
175
def get_uuid_displayname_catalogs(request, user_call=True):
176
    # Normal Response Codes: 200
177
    # Error Response Codes: BadRequest (400)
178

    
179
    try:
180
        input_data = json.loads(request.raw_post_data)
181
    except:
182
        raise faults.BadRequest('Request body should be json formatted.')
183
    else:
184
        if not isinstance(input_data, dict):
185
            raise faults.BadRequest(
186
                'Request body should be a json formatted dictionary')
187
        uuids = input_data.get('uuids', [])
188
        if uuids is None and user_call:
189
            uuids = []
190
        displaynames = input_data.get('displaynames', [])
191
        if displaynames is None and user_call:
192
            displaynames = []
193
        user_obj = AstakosUser.objects
194
        d = {'uuid_catalog': user_obj.uuid_catalog(uuids),
195
             'displayname_catalog': user_obj.displayname_catalog(displaynames)}
196

    
197
        response = HttpResponse()
198
        response.content = json.dumps(d)
199
        response['Content-Type'] = 'application/json; charset=UTF-8'
200
        response['Content-Length'] = len(response.content)
201
        return response
202

    
203

    
204
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
205
    form = FeedbackForm(request.POST)
206
    if not form.is_valid():
207
        logger.error("Invalid feedback request: %r", form.errors)
208
        raise faults.BadRequest('Invalid data')
209

    
210
    msg = form.cleaned_data['feedback_msg']
211
    data = form.cleaned_data['feedback_data']
212
    try:
213
        send_feedback_func(msg, data, request.user, email_template_name)
214
    except:
215
        return HttpResponse(status=502)
216
    return HttpResponse(status=200)
217

    
218

    
219
def rename_meta_key(d, old, new):
220
    if old not in d:
221
        return
222
    d[new] = d[old]
223
    del(d[old])
224

    
225

    
226
def get_int_parameter(p):
227
    if p is not None:
228
        try:
229
            p = int(p)
230
        except ValueError:
231
            return None
232
        if p < 0:
233
            return None
234
    return p
235

    
236

    
237
def get_content_length(request):
238
    content_length = get_int_parameter(request.META.get('CONTENT_LENGTH'))
239
    if content_length is None:
240
        raise faults.LengthRequired('Missing or invalid Content-Length header')
241
    return content_length
242

    
243

    
244
def invert_dict(d):
245
    return dict((v, k) for k, v in d.iteritems())