Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / util.py @ 26498848

History | View | Annotate | Download (10.1 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 5ce3ce4f Sofia Papagiannaki
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 5ce3ce4f Sofia Papagiannaki
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 5ce3ce4f Sofia Papagiannaki
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 5ce3ce4f Sofia Papagiannaki
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 5ce3ce4f Sofia Papagiannaki
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 64cd4730 Antony Chazapis
34 0905ccd2 Sofia Papagiannaki
import logging
35 63ecdd20 Sofia Papagiannaki
import datetime
36 18ffbee1 Sofia Papagiannaki
import time
37 6936103e Kostas Papadimitriou
import urllib
38 63ecdd20 Sofia Papagiannaki
39 c0b26605 Sofia Papagiannaki
from urlparse import urlparse
40 64cd4730 Antony Chazapis
from datetime import tzinfo, timedelta
41 c0b26605 Sofia Papagiannaki
42 440f7c0c Kostas Papadimitriou
from django.http import HttpResponse, HttpResponseBadRequest, urlencode, \
43 440f7c0c Kostas Papadimitriou
                        HttpResponseRedirect
44 0905ccd2 Sofia Papagiannaki
from django.template import RequestContext
45 111f3da6 Sofia Papagiannaki
from django.contrib.auth import authenticate
46 63ecdd20 Sofia Papagiannaki
from django.core.urlresolvers import reverse
47 440f7c0c Kostas Papadimitriou
from django.shortcuts import redirect
48 9a06d96f Olga Brani
from django.core.exceptions import ValidationError, ObjectDoesNotExist
49 ae497612 Olga Brani
from django.utils.translation import ugettext as _
50 440f7c0c Kostas Papadimitriou
from django.core.urlresolvers import reverse
51 ae497612 Olga Brani
52 aab4d540 Sofia Papagiannaki
from astakos.im.models import AstakosUser, Invitation
53 111f3da6 Sofia Papagiannaki
from astakos.im.functions import login
54 0a7a4104 Kostas Papadimitriou
from astakos.im import settings
55 64cd4730 Antony Chazapis
56 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
57 ae497612 Olga Brani
58 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
59 e015e9e6 Sofia Papagiannaki
60 5ce3ce4f Sofia Papagiannaki
61 64cd4730 Antony Chazapis
class UTC(tzinfo):
62 aab4d540 Sofia Papagiannaki
    def utcoffset(self, dt):
63 aab4d540 Sofia Papagiannaki
        return timedelta(0)
64 5ce3ce4f Sofia Papagiannaki
65 aab4d540 Sofia Papagiannaki
    def tzname(self, dt):
66 aab4d540 Sofia Papagiannaki
        return 'UTC'
67 5ce3ce4f Sofia Papagiannaki
68 aab4d540 Sofia Papagiannaki
    def dst(self, dt):
69 aab4d540 Sofia Papagiannaki
        return timedelta(0)
70 64cd4730 Antony Chazapis
71 5ce3ce4f Sofia Papagiannaki
72 64cd4730 Antony Chazapis
def isoformat(d):
73 aab4d540 Sofia Papagiannaki
    """Return an ISO8601 date string that includes a timezone."""
74 5ce3ce4f Sofia Papagiannaki
75 aab4d540 Sofia Papagiannaki
    return d.replace(tzinfo=UTC()).isoformat()
76 0905ccd2 Sofia Papagiannaki
77 5ce3ce4f Sofia Papagiannaki
78 18ffbee1 Sofia Papagiannaki
def epoch(datetime):
79 5ce3ce4f Sofia Papagiannaki
    return int(time.mktime(datetime.timetuple()) * 1000)
80 5ce3ce4f Sofia Papagiannaki
81 18ffbee1 Sofia Papagiannaki
82 aab4d540 Sofia Papagiannaki
def get_context(request, extra_context=None, **kwargs):
83 aab4d540 Sofia Papagiannaki
    extra_context = extra_context or {}
84 0905ccd2 Sofia Papagiannaki
    extra_context.update(kwargs)
85 0905ccd2 Sofia Papagiannaki
    return RequestContext(request, extra_context)
86 e2125441 Sofia Papagiannaki
87 5ce3ce4f Sofia Papagiannaki
88 15efc749 Sofia Papagiannaki
def get_invitation(request):
89 15efc749 Sofia Papagiannaki
    """
90 15efc749 Sofia Papagiannaki
    Returns the invitation identified by the ``code``.
91 5ce3ce4f Sofia Papagiannaki

92 0a569195 Sofia Papagiannaki
    Raises ValueError if the invitation is consumed or there is another account
93 0a569195 Sofia Papagiannaki
    associated with this email.
94 15efc749 Sofia Papagiannaki
    """
95 15efc749 Sofia Papagiannaki
    code = request.GET.get('code')
96 15efc749 Sofia Papagiannaki
    if request.method == 'POST':
97 15efc749 Sofia Papagiannaki
        code = request.POST.get('code')
98 15efc749 Sofia Papagiannaki
    if not code:
99 15efc749 Sofia Papagiannaki
        return
100 5ce3ce4f Sofia Papagiannaki
    invitation = Invitation.objects.get(code=code)
101 15efc749 Sofia Papagiannaki
    if invitation.is_consumed:
102 ae497612 Olga Brani
        raise ValueError(_(astakos_messages.INVITATION_CONSUMED_ERR))
103 0a569195 Sofia Papagiannaki
    if reserved_email(invitation.username):
104 ae497612 Olga Brani
        email = invitation.username
105 c0b26605 Sofia Papagiannaki
        raise ValueError(_(astakos_messages.EMAIL_RESERVED) % locals())
106 63ecdd20 Sofia Papagiannaki
    return invitation
107 63ecdd20 Sofia Papagiannaki
108 217994f8 Sofia Papagiannaki
def restrict_next(url, domain=None, allowed_schemes=()):
109 217994f8 Sofia Papagiannaki
    """
110 217994f8 Sofia Papagiannaki
    Return url if having the supplied ``domain`` (if present) or one of the ``allowed_schemes``.
111 217994f8 Sofia Papagiannaki
    Otherwise return None.
112 e5966bd9 Kostas Papadimitriou

113 217994f8 Sofia Papagiannaki
    >>> print restrict_next('/im/feedback', '.okeanos.grnet.gr')
114 217994f8 Sofia Papagiannaki
    /im/feedback
115 217994f8 Sofia Papagiannaki
    >>> print restrict_next('pithos.okeanos.grnet.gr/im/feedback', '.okeanos.grnet.gr')
116 55baa300 Sofia Papagiannaki
    //pithos.okeanos.grnet.gr/im/feedback
117 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://pithos.okeanos.grnet.gr/im/feedback', '.okeanos.grnet.gr')
118 217994f8 Sofia Papagiannaki
    https://pithos.okeanos.grnet.gr/im/feedback
119 1e960db7 Sofia Papagiannaki
    >>> print restrict_next('pithos://127.0.0.1', '.okeanos.grnet.gr')
120 217994f8 Sofia Papagiannaki
    None
121 1e960db7 Sofia Papagiannaki
    >>> print restrict_next('pithos://127.0.0.1', '.okeanos.grnet.gr', allowed_schemes=('pithos'))
122 217994f8 Sofia Papagiannaki
    pithos://127.0.0,1
123 217994f8 Sofia Papagiannaki
    >>> print restrict_next('node1.example.com', '.okeanos.grnet.gr')
124 217994f8 Sofia Papagiannaki
    None
125 217994f8 Sofia Papagiannaki
    >>> print restrict_next('//node1.example.com', '.okeanos.grnet.gr')
126 217994f8 Sofia Papagiannaki
    None
127 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://node1.example.com', '.okeanos.grnet.gr')
128 217994f8 Sofia Papagiannaki
    None
129 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://node1.example.com')
130 217994f8 Sofia Papagiannaki
    https://node1.example.com
131 217994f8 Sofia Papagiannaki
    >>> print restrict_next('//node1.example.com')
132 217994f8 Sofia Papagiannaki
    //node1.example.com
133 217994f8 Sofia Papagiannaki
    >>> print restrict_next('node1.example.com')
134 55baa300 Sofia Papagiannaki
    //node1.example.com
135 217994f8 Sofia Papagiannaki
    """
136 217994f8 Sofia Papagiannaki
    if not url:
137 217994f8 Sofia Papagiannaki
        return
138 217994f8 Sofia Papagiannaki
    parts = urlparse(url, scheme='http')
139 55baa300 Sofia Papagiannaki
    if not parts.netloc and not parts.path.startswith('/'):
140 217994f8 Sofia Papagiannaki
        # fix url if does not conforms RFC 1808
141 217994f8 Sofia Papagiannaki
        url = '//%s' % url
142 217994f8 Sofia Papagiannaki
        parts = urlparse(url, scheme='http')
143 217994f8 Sofia Papagiannaki
    # TODO more scientific checks?
144 217994f8 Sofia Papagiannaki
    if not parts.netloc:    # internal url
145 217994f8 Sofia Papagiannaki
        return url
146 217994f8 Sofia Papagiannaki
    elif not domain:
147 217994f8 Sofia Papagiannaki
        return url
148 217994f8 Sofia Papagiannaki
    elif parts.netloc.endswith(domain):
149 217994f8 Sofia Papagiannaki
        return url
150 217994f8 Sofia Papagiannaki
    elif parts.scheme in allowed_schemes:
151 217994f8 Sofia Papagiannaki
        return url
152 5ce3ce4f Sofia Papagiannaki
153 8fbf5367 root
def prepare_response(request, user, next='', renew=False):
154 63ecdd20 Sofia Papagiannaki
    """Return the unique username and the token
155 63ecdd20 Sofia Papagiannaki
       as 'X-Auth-User' and 'X-Auth-Token' headers,
156 63ecdd20 Sofia Papagiannaki
       or redirect to the URL provided in 'next'
157 63ecdd20 Sofia Papagiannaki
       with the 'user' and 'token' as parameters.
158 5ce3ce4f Sofia Papagiannaki

159 63ecdd20 Sofia Papagiannaki
       Reissue the token even if it has not yet
160 63ecdd20 Sofia Papagiannaki
       expired, if the 'renew' parameter is present
161 63ecdd20 Sofia Papagiannaki
       or user has not a valid token.
162 63ecdd20 Sofia Papagiannaki
    """
163 63ecdd20 Sofia Papagiannaki
    renew = renew or (not user.auth_token)
164 67920ea0 Giorgos Korfiatis
    renew = renew or user.token_expired()
165 63ecdd20 Sofia Papagiannaki
    if renew:
166 bf0c6de5 Sofia Papagiannaki
        user.renew_token(
167 bf0c6de5 Sofia Papagiannaki
            flush_sessions=True,
168 bf0c6de5 Sofia Papagiannaki
            current_key=request.session.session_key
169 bf0c6de5 Sofia Papagiannaki
        )
170 27e26a41 Sofia Papagiannaki
        try:
171 27e26a41 Sofia Papagiannaki
            user.save()
172 27e26a41 Sofia Papagiannaki
        except ValidationError, e:
173 e5966bd9 Kostas Papadimitriou
            return HttpResponseBadRequest(e)
174 e5966bd9 Kostas Papadimitriou
175 0a7a4104 Kostas Papadimitriou
    next = restrict_next(next, domain=settings.COOKIE_DOMAIN)
176 e5966bd9 Kostas Papadimitriou
177 0a7a4104 Kostas Papadimitriou
    if settings.FORCE_PROFILE_UPDATE and \
178 0a7a4104 Kostas Papadimitriou
            not user.is_verified and not user.is_superuser:
179 63ecdd20 Sofia Papagiannaki
        params = ''
180 63ecdd20 Sofia Papagiannaki
        if next:
181 63ecdd20 Sofia Papagiannaki
            params = '?' + urlencode({'next': next})
182 6ff7a7ca Sofia Papagiannaki
        next = reverse('edit_profile') + params
183 5ce3ce4f Sofia Papagiannaki
184 63ecdd20 Sofia Papagiannaki
    response = HttpResponse()
185 5ce3ce4f Sofia Papagiannaki
186 8fbf5367 root
    # authenticate before login
187 8fbf5367 root
    user = authenticate(email=user.email, auth_token=user.auth_token)
188 8fbf5367 root
    login(request, user)
189 b42b0987 Sofia Papagiannaki
    request.session.set_expiry(user.auth_token_expires)
190 5ce3ce4f Sofia Papagiannaki
191 63ecdd20 Sofia Papagiannaki
    if not next:
192 0a7a4104 Kostas Papadimitriou
        next = settings.LOGIN_SUCCESS_URL
193 e5966bd9 Kostas Papadimitriou
194 63ecdd20 Sofia Papagiannaki
    response['Location'] = next
195 63ecdd20 Sofia Papagiannaki
    response.status_code = 302
196 8fbf5367 root
    return response
197 c301698f Sofia Papagiannaki
198 270dd48d Sofia Papagiannaki
class lazy_string(object):
199 270dd48d Sofia Papagiannaki
    def __init__(self, function, *args, **kwargs):
200 5ce3ce4f Sofia Papagiannaki
        self.function = function
201 5ce3ce4f Sofia Papagiannaki
        self.args = args
202 5ce3ce4f Sofia Papagiannaki
        self.kwargs = kwargs
203 5ce3ce4f Sofia Papagiannaki
204 270dd48d Sofia Papagiannaki
    def __str__(self):
205 270dd48d Sofia Papagiannaki
        if not hasattr(self, 'str'):
206 5ce3ce4f Sofia Papagiannaki
            self.str = self.function(*self.args, **self.kwargs)
207 270dd48d Sofia Papagiannaki
        return self.str
208 270dd48d Sofia Papagiannaki
209 5ce3ce4f Sofia Papagiannaki
210 270dd48d Sofia Papagiannaki
def reverse_lazy(*args, **kwargs):
211 270dd48d Sofia Papagiannaki
    return lazy_string(reverse, *args, **kwargs)
212 270dd48d Sofia Papagiannaki
213 5ce3ce4f Sofia Papagiannaki
214 0a569195 Sofia Papagiannaki
def reserved_email(email):
215 e5966bd9 Kostas Papadimitriou
    return AstakosUser.objects.user_exists(email)
216 5ce3ce4f Sofia Papagiannaki
217 0a569195 Sofia Papagiannaki
218 43332a76 Kostas Papadimitriou
def reserved_verified_email(email):
219 43332a76 Kostas Papadimitriou
    return AstakosUser.objects.verified_user_exists(email)
220 43332a76 Kostas Papadimitriou
221 43332a76 Kostas Papadimitriou
222 0a569195 Sofia Papagiannaki
def get_query(request):
223 f627a979 Sofia Papagiannaki
    try:
224 f627a979 Sofia Papagiannaki
        return request.__getattribute__(request.method)
225 f627a979 Sofia Papagiannaki
    except AttributeError:
226 9a06d96f Olga Brani
        return {}
227 9a06d96f Olga Brani
228 9efcce2a Sofia Papagiannaki
def get_properties(obj):
229 2f732a9b Sofia Papagiannaki
    def get_class_attr(_class, attr):
230 2f732a9b Sofia Papagiannaki
        try:
231 2f732a9b Sofia Papagiannaki
            return getattr(_class, attr)
232 2f732a9b Sofia Papagiannaki
        except AttributeError:
233 2f732a9b Sofia Papagiannaki
            return
234 64492c49 Kostas Papadimitriou
235 9efcce2a Sofia Papagiannaki
    return (i for i in vars(obj.__class__) \
236 2f732a9b Sofia Papagiannaki
        if isinstance(get_class_attr(obj.__class__, i), property))
237 9a06d96f Olga Brani
238 9a06d96f Olga Brani
def model_to_dict(obj, exclude=['AutoField', 'ForeignKey', 'OneToOneField'],
239 9a06d96f Olga Brani
                  include_empty=True):
240 9a06d96f Olga Brani
    '''
241 9a06d96f Olga Brani
        serialize model object to dict with related objects
242 9a06d96f Olga Brani

243 9a06d96f Olga Brani
        author: Vadym Zakovinko <vp@zakovinko.com>
244 9a06d96f Olga Brani
        date: January 31, 2011
245 9a06d96f Olga Brani
        http://djangosnippets.org/snippets/2342/
246 9a06d96f Olga Brani
    '''
247 9a06d96f Olga Brani
    tree = {}
248 9a06d96f Olga Brani
    for field_name in obj._meta.get_all_field_names():
249 9a06d96f Olga Brani
        try:
250 9a06d96f Olga Brani
            field = getattr(obj, field_name)
251 9a06d96f Olga Brani
        except (ObjectDoesNotExist, AttributeError):
252 9a06d96f Olga Brani
            continue
253 9a06d96f Olga Brani
254 9a06d96f Olga Brani
        if field.__class__.__name__ in ['RelatedManager', 'ManyRelatedManager']:
255 9a06d96f Olga Brani
            if field.model.__name__ in exclude:
256 9a06d96f Olga Brani
                continue
257 9a06d96f Olga Brani
258 9a06d96f Olga Brani
            if field.__class__.__name__ == 'ManyRelatedManager':
259 9a06d96f Olga Brani
                exclude.append(obj.__class__.__name__)
260 9a06d96f Olga Brani
            subtree = []
261 9a06d96f Olga Brani
            for related_obj in getattr(obj, field_name).all():
262 9a06d96f Olga Brani
                value = model_to_dict(related_obj, exclude=exclude)
263 9a06d96f Olga Brani
                if value or include_empty:
264 9a06d96f Olga Brani
                    subtree.append(value)
265 9a06d96f Olga Brani
            if subtree or include_empty:
266 9a06d96f Olga Brani
                tree[field_name] = subtree
267 9a06d96f Olga Brani
            continue
268 9a06d96f Olga Brani
269 9a06d96f Olga Brani
        field = obj._meta.get_field_by_name(field_name)[0]
270 9a06d96f Olga Brani
        if field.__class__.__name__ in exclude:
271 9a06d96f Olga Brani
            continue
272 9a06d96f Olga Brani
273 9a06d96f Olga Brani
        if field.__class__.__name__ == 'RelatedObject':
274 9a06d96f Olga Brani
            exclude.append(field.model.__name__)
275 9a06d96f Olga Brani
            tree[field_name] = model_to_dict(getattr(obj, field_name),
276 9a06d96f Olga Brani
                                             exclude=exclude)
277 9a06d96f Olga Brani
            continue
278 9a06d96f Olga Brani
279 9a06d96f Olga Brani
        value = getattr(obj, field_name)
280 9a06d96f Olga Brani
        if field.__class__.__name__ == 'ForeignKey':
281 9a06d96f Olga Brani
            value = unicode(value) if value is not None else value
282 9a06d96f Olga Brani
        if value or include_empty:
283 9a06d96f Olga Brani
            tree[field_name] = value
284 9efcce2a Sofia Papagiannaki
    properties = list(get_properties(obj))
285 9efcce2a Sofia Papagiannaki
    for p in properties:
286 9efcce2a Sofia Papagiannaki
       tree[p] = getattr(obj, p)
287 9efcce2a Sofia Papagiannaki
    tree['str_repr'] = obj.__str__()
288 9a06d96f Olga Brani
289 9a06d96f Olga Brani
    return tree
290 6936103e Kostas Papadimitriou
291 6936103e Kostas Papadimitriou
def login_url(request):
292 6936103e Kostas Papadimitriou
    attrs = {}
293 6936103e Kostas Papadimitriou
    for attr in ['login', 'key', 'code']:
294 6936103e Kostas Papadimitriou
        val = request.REQUEST.get(attr, None)
295 6936103e Kostas Papadimitriou
        if val:
296 6936103e Kostas Papadimitriou
            attrs[attr] = val
297 6936103e Kostas Papadimitriou
    return "%s?%s" % (reverse('login'), urllib.urlencode(attrs))
298 440f7c0c Kostas Papadimitriou
299 440f7c0c Kostas Papadimitriou
300 440f7c0c Kostas Papadimitriou
def redirect_back(request, default='index'):
301 440f7c0c Kostas Papadimitriou
    """
302 440f7c0c Kostas Papadimitriou
    Redirect back to referer if safe and possible.
303 440f7c0c Kostas Papadimitriou
    """
304 440f7c0c Kostas Papadimitriou
    referer = request.META.get('HTTP_REFERER')
305 440f7c0c Kostas Papadimitriou
306 fb9ba8d5 Kostas Papadimitriou
    safedomain = settings.BASE_URL.replace("https://", "").replace(
307 440f7c0c Kostas Papadimitriou
        "http://", "")
308 440f7c0c Kostas Papadimitriou
    safe = restrict_next(referer, safedomain)
309 440f7c0c Kostas Papadimitriou
    # avoid redirect loop
310 440f7c0c Kostas Papadimitriou
    loops = referer == request.get_full_path()
311 440f7c0c Kostas Papadimitriou
    if referer and safe and not loops:
312 440f7c0c Kostas Papadimitriou
        return redirect(referer)
313 440f7c0c Kostas Papadimitriou
    return redirect(reverse(default))