util.py 7.64 KB
Newer Older
Antony Chazapis's avatar
Antony Chazapis committed
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
4
5
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
6
#
7
8
9
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
10
#
11
12
13
14
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
15
#
16
17
18
19
20
21
22
23
24
25
26
27
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
30
31
32
33
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
34
import logging
35
import datetime
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
36
import time
37
38

from urllib import quote
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
39

40
from datetime import tzinfo, timedelta
41
from django.http import HttpResponse, HttpResponseBadRequest, urlencode
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
42
from django.template import RequestContext
43
from django.utils.translation import ugettext as _
44
from django.contrib.auth import authenticate
45
from django.core.urlresolvers import reverse
Olga Brani's avatar
Olga Brani committed
46
47
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.db.models.fields import Field
48
49
from django.utils.translation import ugettext as _

50
51
from astakos.im.models import AstakosUser, Invitation
from astakos.im.settings import COOKIE_NAME, \
52
53
    COOKIE_DOMAIN, COOKIE_SECURE, FORCE_PROFILE_UPDATE, LOGGING_LEVEL
from astakos.im.functions import login
54

55
56
import astakos.im.messages as astakos_messages

57
58
logger = logging.getLogger(__name__)

59

60
class UTC(tzinfo):
61
62
    def utcoffset(self, dt):
        return timedelta(0)
63

64
65
    def tzname(self, dt):
        return 'UTC'
66

67
68
    def dst(self, dt):
        return timedelta(0)
69

70

71
def isoformat(d):
72
    """Return an ISO8601 date string that includes a timezone."""
73

74
    return d.replace(tzinfo=UTC()).isoformat()
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
75

76

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
77
def epoch(datetime):
78
79
    return int(time.mktime(datetime.timetuple()) * 1000)

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
80

81
82
def get_context(request, extra_context=None, **kwargs):
    extra_context = extra_context or {}
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
83
84
    extra_context.update(kwargs)
    return RequestContext(request, extra_context)
85

86

87
88
89
def get_invitation(request):
    """
    Returns the invitation identified by the ``code``.
90

91
92
    Raises ValueError if the invitation is consumed or there is another account
    associated with this email.
93
94
95
96
97
98
    """
    code = request.GET.get('code')
    if request.method == 'POST':
        code = request.POST.get('code')
    if not code:
        return
99
    invitation = Invitation.objects.get(code=code)
100
    if invitation.is_consumed:
101
        raise ValueError(_(astakos_messages.INVITATION_CONSUMED_ERR))
102
    if reserved_email(invitation.username):
103
104
        email = invitation.username
        raise ValueError(_(astakos_messages.EMAIL_RESRVED) % locals()))
105
106
    return invitation

107

root's avatar
root committed
108
def prepare_response(request, user, next='', renew=False):
109
110
111
112
    """Return the unique username and the token
       as 'X-Auth-User' and 'X-Auth-Token' headers,
       or redirect to the URL provided in 'next'
       with the 'user' and 'token' as parameters.
113

114
115
116
117
118
       Reissue the token even if it has not yet
       expired, if the 'renew' parameter is present
       or user has not a valid token.
    """
    renew = renew or (not user.auth_token)
119
    renew = renew or (user.auth_token_expires < datetime.datetime.now())
120
121
    if renew:
        user.renew_token()
122
123
124
        try:
            user.save()
        except ValidationError, e:
125
126
            return HttpResponseBadRequest(e)

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
127
    if FORCE_PROFILE_UPDATE and not user.is_verified and not user.is_superuser:
128
129
130
        params = ''
        if next:
            params = '?' + urlencode({'next': next})
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
131
        next = reverse('edit_profile') + params
132

133
    response = HttpResponse()
134

root's avatar
root committed
135
136
137
    # authenticate before login
    user = authenticate(email=user.email, auth_token=user.auth_token)
    login(request, user)
138
    set_cookie(response, user)
139
    request.session.set_expiry(user.auth_token_expires)
140

141
    if not next:
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
142
        next = reverse('index')
143

144
145
    response['Location'] = next
    response.status_code = 302
root's avatar
root committed
146
    return response
147

148

149
150
151
152
def set_cookie(response, user):
    expire_fmt = user.auth_token_expires.strftime('%a, %d-%b-%Y %H:%M:%S %Z')
    cookie_value = quote(user.email + '|' + user.auth_token)
    response.set_cookie(COOKIE_NAME, value=cookie_value,
153
                        expires=expire_fmt, path='/',
Olga Brani's avatar
Olga Brani committed
154
                        domain=COOKIE_DOMAIN, secure=COOKIE_SECURE)
155
156
157
158
    msg = 'Cookie [expiring %s] set for %s' % (
        user.auth_token_expires,
        user.email
    )
159
    logger.log(LOGGING_LEVEL, msg)
160

161

162
163
class lazy_string(object):
    def __init__(self, function, *args, **kwargs):
164
165
166
167
        self.function = function
        self.args = args
        self.kwargs = kwargs

168
169
    def __str__(self):
        if not hasattr(self, 'str'):
170
            self.str = self.function(*self.args, **self.kwargs)
171
172
        return self.str

173

174
175
176
def reverse_lazy(*args, **kwargs):
    return lazy_string(reverse, *args, **kwargs)

177

178
def reserved_email(email):
Olga Brani's avatar
Olga Brani committed
179
    return AstakosUser.objects.filter(email__iexact=email).count() != 0
180

181
182

def get_query(request):
183
184
185
    try:
        return request.__getattribute__(request.method)
    except AttributeError:
Olga Brani's avatar
Olga Brani committed
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
        return {}


def model_to_dict(obj, exclude=['AutoField', 'ForeignKey', 'OneToOneField'],
                  include_empty=True):
    '''
        serialize model object to dict with related objects

        author: Vadym Zakovinko <vp@zakovinko.com>
        date: January 31, 2011
        http://djangosnippets.org/snippets/2342/
    '''
    tree = {}
    for field_name in obj._meta.get_all_field_names():
        try:
            field = getattr(obj, field_name)
        except (ObjectDoesNotExist, AttributeError):
            continue

        if field.__class__.__name__ in ['RelatedManager', 'ManyRelatedManager']:
            if field.model.__name__ in exclude:
                continue

            if field.__class__.__name__ == 'ManyRelatedManager':
                exclude.append(obj.__class__.__name__)
            subtree = []
            for related_obj in getattr(obj, field_name).all():
                value = model_to_dict(related_obj, exclude=exclude)
                if value or include_empty:
                    subtree.append(value)
            if subtree or include_empty:
                tree[field_name] = subtree
            continue

        field = obj._meta.get_field_by_name(field_name)[0]
        if field.__class__.__name__ in exclude:
            continue

        if field.__class__.__name__ == 'RelatedObject':
            exclude.append(field.model.__name__)
            tree[field_name] = model_to_dict(getattr(obj, field_name),
                                             exclude=exclude)
            continue

        value = getattr(obj, field_name)
        if field.__class__.__name__ == 'ForeignKey':
            value = unicode(value) if value is not None else value
        if value or include_empty:
            tree[field_name] = value

    return tree