Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / util.py @ cfc4b87c

History | View | Annotate | Download (7.1 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 is_integer(x):
85
    return isinstance(x, (int, long))
86

    
87

    
88
def are_integer(lst):
89
    return all(map(is_integer, lst))
90

    
91

    
92
def validate_user(user):
93
    # Check if the user is active.
94
    if not user.is_active:
95
        raise faults.Unauthorized('User inactive')
96

    
97
    # Check if the token has expired.
98
    if user.token_expired():
99
        raise faults.Unauthorized('Authentication expired')
100

    
101
    # Check if the user has accepted the terms.
102
    if not user.signed_terms:
103
        raise faults.Unauthorized('Pending approval terms')
104

    
105

    
106
def user_from_token(func):
107
    @wraps(func)
108
    def wrapper(request, *args, **kwargs):
109
        try:
110
            token = request.x_auth_token
111
        except AttributeError:
112
            raise faults.Unauthorized("No authentication token")
113

    
114
        if not token:
115
            raise faults.Unauthorized("Invalid X-Auth-Token")
116

    
117
        try:
118
            user = AstakosUser.objects.get(auth_token=token)
119
        except AstakosUser.DoesNotExist:
120
            raise faults.Unauthorized('Invalid X-Auth-Token')
121

    
122
        validate_user(user)
123

    
124
        request.user = user
125
        return func(request, *args, **kwargs)
126
    return wrapper
127

    
128

    
129
def component_from_token(func):
130
    """Decorator for authenticating component by its token.
131

132
    Check that a component with the corresponding token exists. Also,
133
    if component's token has an expiration token, check that it has not
134
    expired.
135

136
    """
137
    @wraps(func)
138
    def wrapper(request, *args, **kwargs):
139
        try:
140
            token = request.x_auth_token
141
        except AttributeError:
142
            raise faults.Unauthorized("No authentication token")
143

    
144
        if not token:
145
            raise faults.Unauthorized("Invalid X-Auth-Token")
146
        try:
147
            component = Component.objects.get(auth_token=token)
148
        except Component.DoesNotExist:
149
            raise faults.Unauthorized("Invalid X-Auth-Token")
150

    
151
        # Check if the token has expired
152
        expiration_date = component.auth_token_expires
153
        if expiration_date:
154
            expires_at = mktime(expiration_date.timetuple())
155
            if time() > expires_at:
156
                raise faults.Unauthorized("Authentication expired")
157

    
158
        request.component_instance = component
159
        return func(request, *args, **kwargs)
160
    return wrapper
161

    
162

    
163
def get_uuid_displayname_catalogs(request, user_call=True):
164
    # Normal Response Codes: 200
165
    # Error Response Codes: BadRequest (400)
166

    
167
    try:
168
        input_data = json.loads(request.body)
169
    except:
170
        raise faults.BadRequest('Request body should be json formatted.')
171
    else:
172
        uuids = input_data.get('uuids', [])
173
        if uuids is None and user_call:
174
            uuids = []
175
        displaynames = input_data.get('displaynames', [])
176
        if displaynames is None and user_call:
177
            displaynames = []
178
        user_obj = AstakosUser.objects
179
        d = {'uuid_catalog': user_obj.uuid_catalog(uuids),
180
             'displayname_catalog': user_obj.displayname_catalog(displaynames)}
181

    
182
        response = HttpResponse()
183
        response.content = json.dumps(d)
184
        response['Content-Type'] = 'application/json; charset=UTF-8'
185
        response['Content-Length'] = len(response.content)
186
        return response
187

    
188

    
189
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
190
    form = FeedbackForm(request.POST)
191
    if not form.is_valid():
192
        logger.error("Invalid feedback request: %r", form.errors)
193
        raise faults.BadRequest('Invalid data')
194

    
195
    msg = form.cleaned_data['feedback_msg']
196
    data = form.cleaned_data['feedback_data']
197
    try:
198
        send_feedback_func(msg, data, request.user, email_template_name)
199
    except:
200
        return HttpResponse(status=502)
201
    return HttpResponse(status=200)
202

    
203

    
204
def rename_meta_key(d, old, new):
205
    if old not in d:
206
        return
207
    d[new] = d[old]
208
    del(d[old])
209

    
210

    
211
def get_int_parameter(p):
212
    if p is not None:
213
        try:
214
            p = int(p)
215
        except ValueError:
216
            return None
217
        if p < 0:
218
            return None
219
    return p
220

    
221

    
222
def get_content_length(request):
223
    content_length = get_int_parameter(request.META.get('CONTENT_LENGTH'))
224
    if content_length is None:
225
        raise faults.LengthRequired('Missing or invalid Content-Length header')
226
    return content_length