Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / util.py @ 5cdbe7cc

History | View | Annotate | Download (11.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 8fb8d0cf Giorgos Korfiatis
    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 ae497612 Olga Brani
51 aab4d540 Sofia Papagiannaki
from astakos.im.models import AstakosUser, Invitation
52 111f3da6 Sofia Papagiannaki
from astakos.im.functions import login
53 0a7a4104 Kostas Papadimitriou
from astakos.im import settings
54 64cd4730 Antony Chazapis
55 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
56 ae497612 Olga Brani
57 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
58 e015e9e6 Sofia Papagiannaki
59 5ce3ce4f Sofia Papagiannaki
60 64cd4730 Antony Chazapis
class UTC(tzinfo):
61 aab4d540 Sofia Papagiannaki
    def utcoffset(self, dt):
62 aab4d540 Sofia Papagiannaki
        return timedelta(0)
63 5ce3ce4f Sofia Papagiannaki
64 aab4d540 Sofia Papagiannaki
    def tzname(self, dt):
65 aab4d540 Sofia Papagiannaki
        return 'UTC'
66 5ce3ce4f Sofia Papagiannaki
67 aab4d540 Sofia Papagiannaki
    def dst(self, dt):
68 aab4d540 Sofia Papagiannaki
        return timedelta(0)
69 64cd4730 Antony Chazapis
70 5ce3ce4f Sofia Papagiannaki
71 64cd4730 Antony Chazapis
def isoformat(d):
72 aab4d540 Sofia Papagiannaki
    """Return an ISO8601 date string that includes a timezone."""
73 5ce3ce4f Sofia Papagiannaki
74 aab4d540 Sofia Papagiannaki
    return d.replace(tzinfo=UTC()).isoformat()
75 0905ccd2 Sofia Papagiannaki
76 5ce3ce4f Sofia Papagiannaki
77 8fb8d0cf Giorgos Korfiatis
def epoch(dt):
78 8fb8d0cf Giorgos Korfiatis
    return int(time.mktime(dt.timetuple()) * 1000)
79 5ce3ce4f Sofia Papagiannaki
80 18ffbee1 Sofia Papagiannaki
81 aab4d540 Sofia Papagiannaki
def get_context(request, extra_context=None, **kwargs):
82 aab4d540 Sofia Papagiannaki
    extra_context = extra_context or {}
83 0905ccd2 Sofia Papagiannaki
    extra_context.update(kwargs)
84 0905ccd2 Sofia Papagiannaki
    return RequestContext(request, extra_context)
85 e2125441 Sofia Papagiannaki
86 5ce3ce4f Sofia Papagiannaki
87 15efc749 Sofia Papagiannaki
def get_invitation(request):
88 15efc749 Sofia Papagiannaki
    """
89 15efc749 Sofia Papagiannaki
    Returns the invitation identified by the ``code``.
90 5ce3ce4f Sofia Papagiannaki

91 0a569195 Sofia Papagiannaki
    Raises ValueError if the invitation is consumed or there is another account
92 0a569195 Sofia Papagiannaki
    associated with this email.
93 15efc749 Sofia Papagiannaki
    """
94 15efc749 Sofia Papagiannaki
    code = request.GET.get('code')
95 15efc749 Sofia Papagiannaki
    if request.method == 'POST':
96 15efc749 Sofia Papagiannaki
        code = request.POST.get('code')
97 15efc749 Sofia Papagiannaki
    if not code:
98 15efc749 Sofia Papagiannaki
        return
99 5ce3ce4f Sofia Papagiannaki
    invitation = Invitation.objects.get(code=code)
100 15efc749 Sofia Papagiannaki
    if invitation.is_consumed:
101 ae497612 Olga Brani
        raise ValueError(_(astakos_messages.INVITATION_CONSUMED_ERR))
102 0a569195 Sofia Papagiannaki
    if reserved_email(invitation.username):
103 ae497612 Olga Brani
        email = invitation.username
104 c0b26605 Sofia Papagiannaki
        raise ValueError(_(astakos_messages.EMAIL_RESERVED) % locals())
105 63ecdd20 Sofia Papagiannaki
    return invitation
106 63ecdd20 Sofia Papagiannaki
107 7c3549f0 Kostas Papadimitriou
108 217994f8 Sofia Papagiannaki
def restrict_next(url, domain=None, allowed_schemes=()):
109 217994f8 Sofia Papagiannaki
    """
110 7c3549f0 Kostas Papadimitriou
    Utility method to validate that provided url is safe to be used as the
111 7c3549f0 Kostas Papadimitriou
    redirect location of an http redirect response. The method parses the
112 7c3549f0 Kostas Papadimitriou
    provided url and identifies if it conforms CORS against provided domain
113 7c3549f0 Kostas Papadimitriou
    AND url scheme matches any of the schemes in `allowed_schemes` parameter.
114 5b65fb47 Kostas Papadimitriou
    If verirication succeeds sanitized safe url is returned. Consider using
115 5b65fb47 Kostas Papadimitriou
    the method's result in the response location header and not the originally
116 5b65fb47 Kostas Papadimitriou
    provided url. If verification fails the method returns None.
117 e5966bd9 Kostas Papadimitriou

118 217994f8 Sofia Papagiannaki
    >>> print restrict_next('/im/feedback', '.okeanos.grnet.gr')
119 217994f8 Sofia Papagiannaki
    /im/feedback
120 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos.okeanos.grnet.gr/im/feedback',
121 7c3549f0 Kostas Papadimitriou
    ...                     '.okeanos.grnet.gr')
122 55baa300 Sofia Papagiannaki
    //pithos.okeanos.grnet.gr/im/feedback
123 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('https://pithos.okeanos.grnet.gr/im/feedback',
124 7c3549f0 Kostas Papadimitriou
    ...                     '.okeanos.grnet.gr')
125 217994f8 Sofia Papagiannaki
    https://pithos.okeanos.grnet.gr/im/feedback
126 1e960db7 Sofia Papagiannaki
    >>> print restrict_next('pithos://127.0.0.1', '.okeanos.grnet.gr')
127 217994f8 Sofia Papagiannaki
    None
128 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos://127.0.0.1', '.okeanos.grnet.gr',
129 7c3549f0 Kostas Papadimitriou
    ...                     allowed_schemes=('pithos'))
130 7c3549f0 Kostas Papadimitriou
    None
131 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos://127.0.0.1', '127.0.0.1',
132 7c3549f0 Kostas Papadimitriou
    ...                     allowed_schemes=('pithos'))
133 7c3549f0 Kostas Papadimitriou
    pithos://127.0.0.1
134 217994f8 Sofia Papagiannaki
    >>> print restrict_next('node1.example.com', '.okeanos.grnet.gr')
135 217994f8 Sofia Papagiannaki
    None
136 217994f8 Sofia Papagiannaki
    >>> print restrict_next('//node1.example.com', '.okeanos.grnet.gr')
137 217994f8 Sofia Papagiannaki
    None
138 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://node1.example.com', '.okeanos.grnet.gr')
139 217994f8 Sofia Papagiannaki
    None
140 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://node1.example.com')
141 217994f8 Sofia Papagiannaki
    https://node1.example.com
142 217994f8 Sofia Papagiannaki
    >>> print restrict_next('//node1.example.com')
143 217994f8 Sofia Papagiannaki
    //node1.example.com
144 217994f8 Sofia Papagiannaki
    >>> print restrict_next('node1.example.com')
145 55baa300 Sofia Papagiannaki
    //node1.example.com
146 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('node1.example.com', allowed_schemes=('pithos',))
147 7c3549f0 Kostas Papadimitriou
    None
148 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos://localhost', 'localhost',
149 7c3549f0 Kostas Papadimitriou
    ...                     allowed_schemes=('pithos',))
150 7c3549f0 Kostas Papadimitriou
    pithos://localhost
151 217994f8 Sofia Papagiannaki
    """
152 217994f8 Sofia Papagiannaki
    if not url:
153 7c3549f0 Kostas Papadimitriou
        return None
154 7c3549f0 Kostas Papadimitriou
155 217994f8 Sofia Papagiannaki
    parts = urlparse(url, scheme='http')
156 55baa300 Sofia Papagiannaki
    if not parts.netloc and not parts.path.startswith('/'):
157 217994f8 Sofia Papagiannaki
        # fix url if does not conforms RFC 1808
158 217994f8 Sofia Papagiannaki
        url = '//%s' % url
159 217994f8 Sofia Papagiannaki
        parts = urlparse(url, scheme='http')
160 7c3549f0 Kostas Papadimitriou
161 7c3549f0 Kostas Papadimitriou
    if not domain and not allowed_schemes:
162 217994f8 Sofia Papagiannaki
        return url
163 5ce3ce4f Sofia Papagiannaki
164 5b65fb47 Kostas Papadimitriou
    # domain validation
165 7c3549f0 Kostas Papadimitriou
    if domain:
166 7c3549f0 Kostas Papadimitriou
        if not parts.netloc:
167 7c3549f0 Kostas Papadimitriou
            return url
168 7c3549f0 Kostas Papadimitriou
        if parts.netloc.endswith(domain):
169 7c3549f0 Kostas Papadimitriou
            return url
170 7c3549f0 Kostas Papadimitriou
        else:
171 7c3549f0 Kostas Papadimitriou
            return None
172 7c3549f0 Kostas Papadimitriou
173 5b65fb47 Kostas Papadimitriou
    # scheme validation
174 7c3549f0 Kostas Papadimitriou
    if allowed_schemes:
175 7c3549f0 Kostas Papadimitriou
        if parts.scheme in allowed_schemes:
176 7c3549f0 Kostas Papadimitriou
            return url
177 7c3549f0 Kostas Papadimitriou
178 7c3549f0 Kostas Papadimitriou
    return None
179 7c3549f0 Kostas Papadimitriou
180 7c3549f0 Kostas Papadimitriou
181 8fbf5367 root
def prepare_response(request, user, next='', renew=False):
182 63ecdd20 Sofia Papagiannaki
    """Return the unique username and the token
183 63ecdd20 Sofia Papagiannaki
       as 'X-Auth-User' and 'X-Auth-Token' headers,
184 63ecdd20 Sofia Papagiannaki
       or redirect to the URL provided in 'next'
185 63ecdd20 Sofia Papagiannaki
       with the 'user' and 'token' as parameters.
186 5ce3ce4f Sofia Papagiannaki

187 63ecdd20 Sofia Papagiannaki
       Reissue the token even if it has not yet
188 63ecdd20 Sofia Papagiannaki
       expired, if the 'renew' parameter is present
189 63ecdd20 Sofia Papagiannaki
       or user has not a valid token.
190 63ecdd20 Sofia Papagiannaki
    """
191 63ecdd20 Sofia Papagiannaki
    renew = renew or (not user.auth_token)
192 67920ea0 Giorgos Korfiatis
    renew = renew or user.token_expired()
193 63ecdd20 Sofia Papagiannaki
    if renew:
194 bf0c6de5 Sofia Papagiannaki
        user.renew_token(
195 bf0c6de5 Sofia Papagiannaki
            flush_sessions=True,
196 bf0c6de5 Sofia Papagiannaki
            current_key=request.session.session_key
197 bf0c6de5 Sofia Papagiannaki
        )
198 27e26a41 Sofia Papagiannaki
        try:
199 27e26a41 Sofia Papagiannaki
            user.save()
200 27e26a41 Sofia Papagiannaki
        except ValidationError, e:
201 e5966bd9 Kostas Papadimitriou
            return HttpResponseBadRequest(e)
202 e5966bd9 Kostas Papadimitriou
203 0a7a4104 Kostas Papadimitriou
    next = restrict_next(next, domain=settings.COOKIE_DOMAIN)
204 e5966bd9 Kostas Papadimitriou
205 0a7a4104 Kostas Papadimitriou
    if settings.FORCE_PROFILE_UPDATE and \
206 0a7a4104 Kostas Papadimitriou
            not user.is_verified and not user.is_superuser:
207 63ecdd20 Sofia Papagiannaki
        params = ''
208 63ecdd20 Sofia Papagiannaki
        if next:
209 63ecdd20 Sofia Papagiannaki
            params = '?' + urlencode({'next': next})
210 6ff7a7ca Sofia Papagiannaki
        next = reverse('edit_profile') + params
211 5ce3ce4f Sofia Papagiannaki
212 63ecdd20 Sofia Papagiannaki
    response = HttpResponse()
213 5ce3ce4f Sofia Papagiannaki
214 8fbf5367 root
    # authenticate before login
215 8fbf5367 root
    user = authenticate(email=user.email, auth_token=user.auth_token)
216 8fbf5367 root
    login(request, user)
217 b42b0987 Sofia Papagiannaki
    request.session.set_expiry(user.auth_token_expires)
218 5ce3ce4f Sofia Papagiannaki
219 63ecdd20 Sofia Papagiannaki
    if not next:
220 0a7a4104 Kostas Papadimitriou
        next = settings.LOGIN_SUCCESS_URL
221 e5966bd9 Kostas Papadimitriou
222 63ecdd20 Sofia Papagiannaki
    response['Location'] = next
223 63ecdd20 Sofia Papagiannaki
    response.status_code = 302
224 8fbf5367 root
    return response
225 c301698f Sofia Papagiannaki
226 8fb8d0cf Giorgos Korfiatis
227 270dd48d Sofia Papagiannaki
class lazy_string(object):
228 270dd48d Sofia Papagiannaki
    def __init__(self, function, *args, **kwargs):
229 5ce3ce4f Sofia Papagiannaki
        self.function = function
230 5ce3ce4f Sofia Papagiannaki
        self.args = args
231 5ce3ce4f Sofia Papagiannaki
        self.kwargs = kwargs
232 5ce3ce4f Sofia Papagiannaki
233 270dd48d Sofia Papagiannaki
    def __str__(self):
234 270dd48d Sofia Papagiannaki
        if not hasattr(self, 'str'):
235 5ce3ce4f Sofia Papagiannaki
            self.str = self.function(*self.args, **self.kwargs)
236 270dd48d Sofia Papagiannaki
        return self.str
237 270dd48d Sofia Papagiannaki
238 5ce3ce4f Sofia Papagiannaki
239 270dd48d Sofia Papagiannaki
def reverse_lazy(*args, **kwargs):
240 270dd48d Sofia Papagiannaki
    return lazy_string(reverse, *args, **kwargs)
241 270dd48d Sofia Papagiannaki
242 5ce3ce4f Sofia Papagiannaki
243 0a569195 Sofia Papagiannaki
def reserved_email(email):
244 e5966bd9 Kostas Papadimitriou
    return AstakosUser.objects.user_exists(email)
245 5ce3ce4f Sofia Papagiannaki
246 0a569195 Sofia Papagiannaki
247 43332a76 Kostas Papadimitriou
def reserved_verified_email(email):
248 43332a76 Kostas Papadimitriou
    return AstakosUser.objects.verified_user_exists(email)
249 43332a76 Kostas Papadimitriou
250 43332a76 Kostas Papadimitriou
251 0a569195 Sofia Papagiannaki
def get_query(request):
252 f627a979 Sofia Papagiannaki
    try:
253 f627a979 Sofia Papagiannaki
        return request.__getattribute__(request.method)
254 f627a979 Sofia Papagiannaki
    except AttributeError:
255 9a06d96f Olga Brani
        return {}
256 9a06d96f Olga Brani
257 8fb8d0cf Giorgos Korfiatis
258 9efcce2a Sofia Papagiannaki
def get_properties(obj):
259 2f732a9b Sofia Papagiannaki
    def get_class_attr(_class, attr):
260 2f732a9b Sofia Papagiannaki
        try:
261 2f732a9b Sofia Papagiannaki
            return getattr(_class, attr)
262 2f732a9b Sofia Papagiannaki
        except AttributeError:
263 2f732a9b Sofia Papagiannaki
            return
264 64492c49 Kostas Papadimitriou
265 8fb8d0cf Giorgos Korfiatis
    return (i for i in vars(obj.__class__)
266 8fb8d0cf Giorgos Korfiatis
            if isinstance(get_class_attr(obj.__class__, i), property))
267 8fb8d0cf Giorgos Korfiatis
268 9a06d96f Olga Brani
269 68a8935f Giorgos Korfiatis
def model_to_dict(obj, exclude=None, include_empty=True):
270 9a06d96f Olga Brani
    '''
271 9a06d96f Olga Brani
        serialize model object to dict with related objects
272 9a06d96f Olga Brani

273 9a06d96f Olga Brani
        author: Vadym Zakovinko <vp@zakovinko.com>
274 9a06d96f Olga Brani
        date: January 31, 2011
275 9a06d96f Olga Brani
        http://djangosnippets.org/snippets/2342/
276 9a06d96f Olga Brani
    '''
277 68a8935f Giorgos Korfiatis
278 68a8935f Giorgos Korfiatis
    if exclude is None:
279 68a8935f Giorgos Korfiatis
        exclude = ['AutoField', 'ForeignKey', 'OneToOneField']
280 9a06d96f Olga Brani
    tree = {}
281 9a06d96f Olga Brani
    for field_name in obj._meta.get_all_field_names():
282 9a06d96f Olga Brani
        try:
283 9a06d96f Olga Brani
            field = getattr(obj, field_name)
284 9a06d96f Olga Brani
        except (ObjectDoesNotExist, AttributeError):
285 9a06d96f Olga Brani
            continue
286 9a06d96f Olga Brani
287 8fb8d0cf Giorgos Korfiatis
        if field.__class__.__name__ in ['RelatedManager',
288 8fb8d0cf Giorgos Korfiatis
                                        'ManyRelatedManager']:
289 9a06d96f Olga Brani
            if field.model.__name__ in exclude:
290 9a06d96f Olga Brani
                continue
291 9a06d96f Olga Brani
292 9a06d96f Olga Brani
            if field.__class__.__name__ == 'ManyRelatedManager':
293 9a06d96f Olga Brani
                exclude.append(obj.__class__.__name__)
294 9a06d96f Olga Brani
            subtree = []
295 9a06d96f Olga Brani
            for related_obj in getattr(obj, field_name).all():
296 9a06d96f Olga Brani
                value = model_to_dict(related_obj, exclude=exclude)
297 9a06d96f Olga Brani
                if value or include_empty:
298 9a06d96f Olga Brani
                    subtree.append(value)
299 9a06d96f Olga Brani
            if subtree or include_empty:
300 9a06d96f Olga Brani
                tree[field_name] = subtree
301 9a06d96f Olga Brani
            continue
302 9a06d96f Olga Brani
303 9a06d96f Olga Brani
        field = obj._meta.get_field_by_name(field_name)[0]
304 9a06d96f Olga Brani
        if field.__class__.__name__ in exclude:
305 9a06d96f Olga Brani
            continue
306 9a06d96f Olga Brani
307 9a06d96f Olga Brani
        if field.__class__.__name__ == 'RelatedObject':
308 9a06d96f Olga Brani
            exclude.append(field.model.__name__)
309 9a06d96f Olga Brani
            tree[field_name] = model_to_dict(getattr(obj, field_name),
310 9a06d96f Olga Brani
                                             exclude=exclude)
311 9a06d96f Olga Brani
            continue
312 9a06d96f Olga Brani
313 9a06d96f Olga Brani
        value = getattr(obj, field_name)
314 9a06d96f Olga Brani
        if field.__class__.__name__ == 'ForeignKey':
315 9a06d96f Olga Brani
            value = unicode(value) if value is not None else value
316 9a06d96f Olga Brani
        if value or include_empty:
317 9a06d96f Olga Brani
            tree[field_name] = value
318 9efcce2a Sofia Papagiannaki
    properties = list(get_properties(obj))
319 9efcce2a Sofia Papagiannaki
    for p in properties:
320 8fb8d0cf Giorgos Korfiatis
        tree[p] = getattr(obj, p)
321 9efcce2a Sofia Papagiannaki
    tree['str_repr'] = obj.__str__()
322 9a06d96f Olga Brani
323 9a06d96f Olga Brani
    return tree
324 6936103e Kostas Papadimitriou
325 8fb8d0cf Giorgos Korfiatis
326 6936103e Kostas Papadimitriou
def login_url(request):
327 6936103e Kostas Papadimitriou
    attrs = {}
328 6936103e Kostas Papadimitriou
    for attr in ['login', 'key', 'code']:
329 6936103e Kostas Papadimitriou
        val = request.REQUEST.get(attr, None)
330 6936103e Kostas Papadimitriou
        if val:
331 6936103e Kostas Papadimitriou
            attrs[attr] = val
332 6936103e Kostas Papadimitriou
    return "%s?%s" % (reverse('login'), urllib.urlencode(attrs))
333 440f7c0c Kostas Papadimitriou
334 440f7c0c Kostas Papadimitriou
335 440f7c0c Kostas Papadimitriou
def redirect_back(request, default='index'):
336 440f7c0c Kostas Papadimitriou
    """
337 440f7c0c Kostas Papadimitriou
    Redirect back to referer if safe and possible.
338 440f7c0c Kostas Papadimitriou
    """
339 440f7c0c Kostas Papadimitriou
    referer = request.META.get('HTTP_REFERER')
340 440f7c0c Kostas Papadimitriou
341 fb9ba8d5 Kostas Papadimitriou
    safedomain = settings.BASE_URL.replace("https://", "").replace(
342 440f7c0c Kostas Papadimitriou
        "http://", "")
343 440f7c0c Kostas Papadimitriou
    safe = restrict_next(referer, safedomain)
344 440f7c0c Kostas Papadimitriou
    # avoid redirect loop
345 440f7c0c Kostas Papadimitriou
    loops = referer == request.get_full_path()
346 440f7c0c Kostas Papadimitriou
    if referer and safe and not loops:
347 440f7c0c Kostas Papadimitriou
        return redirect(referer)
348 440f7c0c Kostas Papadimitriou
    return redirect(reverse(default))