Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / util.py @ 764d99c4

History | View | Annotate | Download (9.6 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 27e26a41 Sofia Papagiannaki
from django.http import HttpResponse, HttpResponseBadRequest, urlencode
43 0905ccd2 Sofia Papagiannaki
from django.template import RequestContext
44 111f3da6 Sofia Papagiannaki
from django.contrib.auth import authenticate
45 63ecdd20 Sofia Papagiannaki
from django.core.urlresolvers import reverse
46 9a06d96f Olga Brani
from django.core.exceptions import ValidationError, ObjectDoesNotExist
47 ae497612 Olga Brani
from django.utils.translation import ugettext as _
48 ae497612 Olga Brani
49 aab4d540 Sofia Papagiannaki
from astakos.im.models import AstakosUser, Invitation
50 111f3da6 Sofia Papagiannaki
from astakos.im.functions import login
51 0a7a4104 Kostas Papadimitriou
from astakos.im import settings
52 64cd4730 Antony Chazapis
53 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
54 ae497612 Olga Brani
55 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
56 e015e9e6 Sofia Papagiannaki
57 5ce3ce4f Sofia Papagiannaki
58 64cd4730 Antony Chazapis
class UTC(tzinfo):
59 aab4d540 Sofia Papagiannaki
    def utcoffset(self, dt):
60 aab4d540 Sofia Papagiannaki
        return timedelta(0)
61 5ce3ce4f Sofia Papagiannaki
62 aab4d540 Sofia Papagiannaki
    def tzname(self, dt):
63 aab4d540 Sofia Papagiannaki
        return 'UTC'
64 5ce3ce4f Sofia Papagiannaki
65 aab4d540 Sofia Papagiannaki
    def dst(self, dt):
66 aab4d540 Sofia Papagiannaki
        return timedelta(0)
67 64cd4730 Antony Chazapis
68 5ce3ce4f Sofia Papagiannaki
69 64cd4730 Antony Chazapis
def isoformat(d):
70 aab4d540 Sofia Papagiannaki
    """Return an ISO8601 date string that includes a timezone."""
71 5ce3ce4f Sofia Papagiannaki
72 aab4d540 Sofia Papagiannaki
    return d.replace(tzinfo=UTC()).isoformat()
73 0905ccd2 Sofia Papagiannaki
74 5ce3ce4f Sofia Papagiannaki
75 18ffbee1 Sofia Papagiannaki
def epoch(datetime):
76 5ce3ce4f Sofia Papagiannaki
    return int(time.mktime(datetime.timetuple()) * 1000)
77 5ce3ce4f Sofia Papagiannaki
78 18ffbee1 Sofia Papagiannaki
79 aab4d540 Sofia Papagiannaki
def get_context(request, extra_context=None, **kwargs):
80 aab4d540 Sofia Papagiannaki
    extra_context = extra_context or {}
81 0905ccd2 Sofia Papagiannaki
    extra_context.update(kwargs)
82 0905ccd2 Sofia Papagiannaki
    return RequestContext(request, extra_context)
83 e2125441 Sofia Papagiannaki
84 5ce3ce4f Sofia Papagiannaki
85 15efc749 Sofia Papagiannaki
def get_invitation(request):
86 15efc749 Sofia Papagiannaki
    """
87 15efc749 Sofia Papagiannaki
    Returns the invitation identified by the ``code``.
88 5ce3ce4f Sofia Papagiannaki

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

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

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

240 9a06d96f Olga Brani
        author: Vadym Zakovinko <vp@zakovinko.com>
241 9a06d96f Olga Brani
        date: January 31, 2011
242 9a06d96f Olga Brani
        http://djangosnippets.org/snippets/2342/
243 9a06d96f Olga Brani
    '''
244 9a06d96f Olga Brani
    tree = {}
245 9a06d96f Olga Brani
    for field_name in obj._meta.get_all_field_names():
246 9a06d96f Olga Brani
        try:
247 9a06d96f Olga Brani
            field = getattr(obj, field_name)
248 9a06d96f Olga Brani
        except (ObjectDoesNotExist, AttributeError):
249 9a06d96f Olga Brani
            continue
250 9a06d96f Olga Brani
251 9a06d96f Olga Brani
        if field.__class__.__name__ in ['RelatedManager', 'ManyRelatedManager']:
252 9a06d96f Olga Brani
            if field.model.__name__ in exclude:
253 9a06d96f Olga Brani
                continue
254 9a06d96f Olga Brani
255 9a06d96f Olga Brani
            if field.__class__.__name__ == 'ManyRelatedManager':
256 9a06d96f Olga Brani
                exclude.append(obj.__class__.__name__)
257 9a06d96f Olga Brani
            subtree = []
258 9a06d96f Olga Brani
            for related_obj in getattr(obj, field_name).all():
259 9a06d96f Olga Brani
                value = model_to_dict(related_obj, exclude=exclude)
260 9a06d96f Olga Brani
                if value or include_empty:
261 9a06d96f Olga Brani
                    subtree.append(value)
262 9a06d96f Olga Brani
            if subtree or include_empty:
263 9a06d96f Olga Brani
                tree[field_name] = subtree
264 9a06d96f Olga Brani
            continue
265 9a06d96f Olga Brani
266 9a06d96f Olga Brani
        field = obj._meta.get_field_by_name(field_name)[0]
267 9a06d96f Olga Brani
        if field.__class__.__name__ in exclude:
268 9a06d96f Olga Brani
            continue
269 9a06d96f Olga Brani
270 9a06d96f Olga Brani
        if field.__class__.__name__ == 'RelatedObject':
271 9a06d96f Olga Brani
            exclude.append(field.model.__name__)
272 9a06d96f Olga Brani
            tree[field_name] = model_to_dict(getattr(obj, field_name),
273 9a06d96f Olga Brani
                                             exclude=exclude)
274 9a06d96f Olga Brani
            continue
275 9a06d96f Olga Brani
276 9a06d96f Olga Brani
        value = getattr(obj, field_name)
277 9a06d96f Olga Brani
        if field.__class__.__name__ == 'ForeignKey':
278 9a06d96f Olga Brani
            value = unicode(value) if value is not None else value
279 9a06d96f Olga Brani
        if value or include_empty:
280 9a06d96f Olga Brani
            tree[field_name] = value
281 9efcce2a Sofia Papagiannaki
    properties = list(get_properties(obj))
282 9efcce2a Sofia Papagiannaki
    for p in properties:
283 9efcce2a Sofia Papagiannaki
       tree[p] = getattr(obj, p)
284 9efcce2a Sofia Papagiannaki
    tree['str_repr'] = obj.__str__()
285 9a06d96f Olga Brani
286 9a06d96f Olga Brani
    return tree
287 6936103e Kostas Papadimitriou
288 6936103e Kostas Papadimitriou
def login_url(request):
289 6936103e Kostas Papadimitriou
    attrs = {}
290 6936103e Kostas Papadimitriou
    for attr in ['login', 'key', 'code']:
291 6936103e Kostas Papadimitriou
        val = request.REQUEST.get(attr, None)
292 6936103e Kostas Papadimitriou
        if val:
293 6936103e Kostas Papadimitriou
            attrs[attr] = val
294 6936103e Kostas Papadimitriou
    return "%s?%s" % (reverse('login'), urllib.urlencode(attrs))