Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views / decorators.py @ f6ff3033

History | View | Annotate | Download (6.8 kB)

1 70e11eaa Sofia Papagiannaki
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 70e11eaa Sofia Papagiannaki
#
3 70e11eaa Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 70e11eaa Sofia Papagiannaki
# without modification, are permitted provided that the following
5 70e11eaa Sofia Papagiannaki
# conditions are met:
6 70e11eaa Sofia Papagiannaki
#
7 70e11eaa Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 70e11eaa Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 70e11eaa Sofia Papagiannaki
#      disclaimer.
10 70e11eaa Sofia Papagiannaki
#
11 70e11eaa Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 70e11eaa Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 70e11eaa Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 70e11eaa Sofia Papagiannaki
#      provided with the distribution.
15 70e11eaa Sofia Papagiannaki
#
16 70e11eaa Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 70e11eaa Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 70e11eaa Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 70e11eaa Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 70e11eaa Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 70e11eaa Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 70e11eaa Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 70e11eaa Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 70e11eaa Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 70e11eaa Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 70e11eaa Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 70e11eaa Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 70e11eaa Sofia Papagiannaki
#
29 70e11eaa Sofia Papagiannaki
# The views and conclusions contained in the software and
30 70e11eaa Sofia Papagiannaki
# documentation are those of the authors and should not be
31 70e11eaa Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 70e11eaa Sofia Papagiannaki
# or implied, of GRNET S.A.
33 70e11eaa Sofia Papagiannaki
34 70e11eaa Sofia Papagiannaki
from functools import wraps
35 70e11eaa Sofia Papagiannaki
36 b6496f0c Kostas Papadimitriou
from django.utils.http import urlquote
37 70e11eaa Sofia Papagiannaki
from django.contrib import messages
38 70e11eaa Sofia Papagiannaki
from django.core.exceptions import PermissionDenied
39 70e11eaa Sofia Papagiannaki
from django.core.urlresolvers import reverse
40 70e11eaa Sofia Papagiannaki
from django.http import HttpResponse, HttpResponseRedirect
41 b6496f0c Kostas Papadimitriou
from django.utils.decorators import available_attrs
42 70e11eaa Sofia Papagiannaki
from django.utils.http import urlencode
43 70e11eaa Sofia Papagiannaki
44 70e11eaa Sofia Papagiannaki
from astakos.im import auth_providers as auth
45 70e11eaa Sofia Papagiannaki
from astakos.im.cookie import CookieHandler
46 70e11eaa Sofia Papagiannaki
47 b6496f0c Kostas Papadimitriou
REDIRECT_FIELD_NAME = 'next'
48 b6496f0c Kostas Papadimitriou
49 b6496f0c Kostas Papadimitriou
50 b6496f0c Kostas Papadimitriou
def user_passes_test(test_func, login_url=None,
51 b6496f0c Kostas Papadimitriou
                     redirect_field_name=REDIRECT_FIELD_NAME):
52 b6496f0c Kostas Papadimitriou
    """
53 b6496f0c Kostas Papadimitriou
    Decorator for views that checks that the user passes the given test,
54 b6496f0c Kostas Papadimitriou
    redirecting to the log-in page if necessary. The test should be a callable
55 b6496f0c Kostas Papadimitriou
    that takes the user object and returns True if the user passes.
56 b6496f0c Kostas Papadimitriou
    """
57 b6496f0c Kostas Papadimitriou
    if not login_url:
58 b6496f0c Kostas Papadimitriou
        from django.conf import settings
59 b6496f0c Kostas Papadimitriou
        login_url = settings.LOGIN_URL
60 b6496f0c Kostas Papadimitriou
61 b6496f0c Kostas Papadimitriou
    def decorator(view_func):
62 b6496f0c Kostas Papadimitriou
        def _wrapped_view(request, *args, **kwargs):
63 b6496f0c Kostas Papadimitriou
            if test_func(request.user):
64 b6496f0c Kostas Papadimitriou
                return view_func(request, *args, **kwargs)
65 b6496f0c Kostas Papadimitriou
            path = urlquote(request.get_full_path())
66 b6496f0c Kostas Papadimitriou
            tup = reverse('login'), redirect_field_name, path
67 b6496f0c Kostas Papadimitriou
            return HttpResponseRedirect('%s?%s=%s' % tup)
68 b6496f0c Kostas Papadimitriou
        return wraps(view_func,
69 b6496f0c Kostas Papadimitriou
                     assigned=available_attrs(view_func))(_wrapped_view)
70 b6496f0c Kostas Papadimitriou
    return decorator
71 b6496f0c Kostas Papadimitriou
72 b6496f0c Kostas Papadimitriou
73 b6496f0c Kostas Papadimitriou
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME,
74 b6496f0c Kostas Papadimitriou
                   login_url=None):
75 b6496f0c Kostas Papadimitriou
    """
76 b6496f0c Kostas Papadimitriou
    Decorator for views that checks that the user is logged in, redirecting
77 b6496f0c Kostas Papadimitriou
    to the log-in page if necessary.
78 b6496f0c Kostas Papadimitriou
    """
79 b6496f0c Kostas Papadimitriou
    actual_decorator = user_passes_test(
80 b6496f0c Kostas Papadimitriou
        lambda u: u.is_authenticated(),
81 b6496f0c Kostas Papadimitriou
        redirect_field_name=redirect_field_name,
82 b6496f0c Kostas Papadimitriou
83 b6496f0c Kostas Papadimitriou
    )
84 b6496f0c Kostas Papadimitriou
    if function:
85 b6496f0c Kostas Papadimitriou
        return actual_decorator(function)
86 b6496f0c Kostas Papadimitriou
    return actual_decorator
87 b6496f0c Kostas Papadimitriou
88 70e11eaa Sofia Papagiannaki
89 70e11eaa Sofia Papagiannaki
def cookie_fix(func):
90 70e11eaa Sofia Papagiannaki
    """
91 70e11eaa Sofia Papagiannaki
    Decorator checks whether the request.user conforms
92 70e11eaa Sofia Papagiannaki
    with the astakos cookie and if not it fixes it.
93 70e11eaa Sofia Papagiannaki
    """
94 70e11eaa Sofia Papagiannaki
    @wraps(func)
95 70e11eaa Sofia Papagiannaki
    def wrapper(request, *args, **kwargs):
96 70e11eaa Sofia Papagiannaki
        cookie = CookieHandler(request)
97 70e11eaa Sofia Papagiannaki
        if not cookie.is_valid:
98 70e11eaa Sofia Papagiannaki
            # redirect to request path to set/delete the cookie
99 70e11eaa Sofia Papagiannaki
            response = HttpResponse(status=302)
100 70e11eaa Sofia Papagiannaki
            response['Location'] = request.get_full_path()
101 70e11eaa Sofia Papagiannaki
            cookie.fix(response)
102 70e11eaa Sofia Papagiannaki
            return response
103 70e11eaa Sofia Papagiannaki
104 70e11eaa Sofia Papagiannaki
        response = func(request, *args, **kwargs)
105 70e11eaa Sofia Papagiannaki
106 70e11eaa Sofia Papagiannaki
        # if the user authentication status has changed during the processing
107 70e11eaa Sofia Papagiannaki
        # set/delete the cookie appropriately
108 70e11eaa Sofia Papagiannaki
        if not cookie.is_valid:
109 70e11eaa Sofia Papagiannaki
            cookie.fix(response)
110 70e11eaa Sofia Papagiannaki
        return response
111 70e11eaa Sofia Papagiannaki
    return wrapper
112 70e11eaa Sofia Papagiannaki
113 70e11eaa Sofia Papagiannaki
114 70e11eaa Sofia Papagiannaki
def requires_auth_provider(provider_id, **perms):
115 70e11eaa Sofia Papagiannaki
    """
116 b6496f0c Kostas Papadimitriou
    View requires specified authentication module to be enabled in
117 b6496f0c Kostas Papadimitriou
    ASTAKOS_IM_MODULES setting.
118 70e11eaa Sofia Papagiannaki
    """
119 70e11eaa Sofia Papagiannaki
    def decorator(func, *args, **kwargs):
120 70e11eaa Sofia Papagiannaki
        @wraps(func)
121 70e11eaa Sofia Papagiannaki
        def wrapper(request, *args, **kwargs):
122 70e11eaa Sofia Papagiannaki
            provider = auth.get_provider(provider_id)
123 70e11eaa Sofia Papagiannaki
124 70e11eaa Sofia Papagiannaki
            if not provider or not provider.is_active():
125 70e11eaa Sofia Papagiannaki
                raise PermissionDenied
126 70e11eaa Sofia Papagiannaki
127 70e11eaa Sofia Papagiannaki
            for pkey, value in perms.iteritems():
128 70e11eaa Sofia Papagiannaki
                attr = 'get_%s_policy' % pkey.lower()
129 70e11eaa Sofia Papagiannaki
                if getattr(provider, attr) != value:
130 70e11eaa Sofia Papagiannaki
                    #TODO: add session message
131 70e11eaa Sofia Papagiannaki
                    return HttpResponseRedirect(reverse('login'))
132 70e11eaa Sofia Papagiannaki
            return func(request, *args)
133 70e11eaa Sofia Papagiannaki
        return wrapper
134 70e11eaa Sofia Papagiannaki
    return decorator
135 70e11eaa Sofia Papagiannaki
136 70e11eaa Sofia Papagiannaki
137 70e11eaa Sofia Papagiannaki
def requires_anonymous(func):
138 70e11eaa Sofia Papagiannaki
    """
139 70e11eaa Sofia Papagiannaki
    Decorator checkes whether the request.user is not Anonymous and
140 70e11eaa Sofia Papagiannaki
    in that case redirects to `logout`.
141 70e11eaa Sofia Papagiannaki
    """
142 70e11eaa Sofia Papagiannaki
    @wraps(func)
143 70e11eaa Sofia Papagiannaki
    def wrapper(request, *args):
144 70e11eaa Sofia Papagiannaki
        if not request.user.is_anonymous():
145 70e11eaa Sofia Papagiannaki
            next = urlencode({'next': request.build_absolute_uri()})
146 70e11eaa Sofia Papagiannaki
            logout_uri = reverse('astakos.im.views.logout') + '?' + next
147 70e11eaa Sofia Papagiannaki
            return HttpResponseRedirect(logout_uri)
148 70e11eaa Sofia Papagiannaki
        return func(request, *args)
149 70e11eaa Sofia Papagiannaki
    return wrapper
150 70e11eaa Sofia Papagiannaki
151 70e11eaa Sofia Papagiannaki
152 70e11eaa Sofia Papagiannaki
def signed_terms_required(func):
153 70e11eaa Sofia Papagiannaki
    """
154 70e11eaa Sofia Papagiannaki
    Decorator checks whether the request.user is Anonymous and in that case
155 70e11eaa Sofia Papagiannaki
    redirects to `logout`.
156 70e11eaa Sofia Papagiannaki
    """
157 70e11eaa Sofia Papagiannaki
    @wraps(func)
158 70e11eaa Sofia Papagiannaki
    def wrapper(request, *args, **kwargs):
159 70e11eaa Sofia Papagiannaki
        if request.user.is_authenticated() and not request.user.signed_terms:
160 70e11eaa Sofia Papagiannaki
            params = urlencode({'next': request.build_absolute_uri(),
161 70e11eaa Sofia Papagiannaki
                                'show_form': ''})
162 70e11eaa Sofia Papagiannaki
            terms_uri = reverse('latest_terms') + '?' + params
163 70e11eaa Sofia Papagiannaki
            return HttpResponseRedirect(terms_uri)
164 70e11eaa Sofia Papagiannaki
        return func(request, *args, **kwargs)
165 70e11eaa Sofia Papagiannaki
    return wrapper
166 70e11eaa Sofia Papagiannaki
167 70e11eaa Sofia Papagiannaki
168 70e11eaa Sofia Papagiannaki
def required_auth_methods_assigned(allow_access=False):
169 70e11eaa Sofia Papagiannaki
    """
170 70e11eaa Sofia Papagiannaki
    Decorator that checks whether the request.user has all
171 70e11eaa Sofia Papagiannaki
    required auth providers assigned.
172 70e11eaa Sofia Papagiannaki
    """
173 70e11eaa Sofia Papagiannaki
174 70e11eaa Sofia Papagiannaki
    def decorator(func):
175 70e11eaa Sofia Papagiannaki
        @wraps(func)
176 70e11eaa Sofia Papagiannaki
        def wrapper(request, *args, **kwargs):
177 70e11eaa Sofia Papagiannaki
            if request.user.is_authenticated():
178 70e11eaa Sofia Papagiannaki
                missing = request.user.missing_required_providers()
179 70e11eaa Sofia Papagiannaki
                if missing:
180 70e11eaa Sofia Papagiannaki
                    for provider in missing:
181 70e11eaa Sofia Papagiannaki
                        messages.error(request,
182 70e11eaa Sofia Papagiannaki
                                       provider.get_required_msg)
183 70e11eaa Sofia Papagiannaki
                    if not allow_access:
184 70e11eaa Sofia Papagiannaki
                        return HttpResponseRedirect(reverse('edit_profile'))
185 70e11eaa Sofia Papagiannaki
            return func(request, *args, **kwargs)
186 70e11eaa Sofia Papagiannaki
        return wrapper
187 70e11eaa Sofia Papagiannaki
    return decorator
188 70e11eaa Sofia Papagiannaki
189 70e11eaa Sofia Papagiannaki
190 70e11eaa Sofia Papagiannaki
def valid_astakos_user_required(func):
191 70e11eaa Sofia Papagiannaki
    return signed_terms_required(
192 70e11eaa Sofia Papagiannaki
        required_auth_methods_assigned()(login_required(func)))