Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views / target / __init__.py @ 28456640

History | View | Annotate | Download (10.2 kB)

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

    
34
import json
35

    
36
from django.contrib import messages
37
from django.http import HttpResponseRedirect
38
from django.core.urlresolvers import reverse
39
from django.core.validators import ValidationError
40
from django.db import transaction
41

    
42
from astakos.im.models import PendingThirdPartyUser, AstakosUser
43
from astakos.im.util import get_query, login_url
44
from astakos.im import messages as astakos_messages
45
from astakos.im import auth_providers as auth
46
from astakos.im.util import prepare_response
47

    
48
import logging
49

    
50
logger = logging.getLogger(__name__)
51

    
52

    
53
def init_third_party_session(request):
54
    params = dict(request.GET.items())
55
    request.session['third_party_request_params'] = params
56

    
57

    
58
def get_third_party_session_params(request):
59
    if 'third_party_request_params' in request.session:
60
        params = request.session['third_party_request_params']
61
        del request.session['third_party_request_params']
62
        return params
63
    return {}
64

    
65

    
66
def add_pending_auth_provider(request, third_party_token, provider):
67
    if third_party_token:
68
        # use requests to assign the account he just authenticated with with
69
        # a third party provider account
70
        try:
71
            pending = PendingThirdPartyUser.objects.get(
72
                token=third_party_token,
73
                provider=provider.module)
74
            provider = pending.get_provider()
75
            provider.add_to_user()
76
            pending.delete()
77
        except PendingThirdPartyUser.DoesNotExist:
78
            messages.error(request, provider.get_add_failed_msg)
79

    
80

    
81
def get_pending_key(request):
82
    third_party_token = get_query(request).get(
83
        'key', request.session.get('pending_key', False))
84
    if 'pending_key' in request.session:
85
        del request.session['pending_key']
86
    return third_party_token
87

    
88

    
89
def handle_third_party_signup(request, userid, provider_module,
90
                              third_party_key,
91
                              provider_info=None,
92
                              pending_user_params=None,
93
                              template="im/third_party_check_local.html",
94
                              extra_context=None):
95

    
96
    if provider_info is None:
97
        provider_info = {}
98

    
99
    if pending_user_params is None:
100
        pending_user_params = {}
101

    
102
    if extra_context is None:
103
        extra_context = {}
104

    
105
    # build provider module object
106
    provider_data = {
107
        'affiliation': pending_user_params.get('affiliation', provider_module),
108
        'info_data': provider_info
109
    }
110
    provider = auth.get_provider(provider_module, request.user, userid,
111
                                 **provider_data)
112

    
113
    # user wants to add another third party login method
114
    if third_party_key:
115
        messages.error(request, provider.get_invalid_login_msg)
116
        return HttpResponseRedirect(reverse('login') + "?key=%s" %
117
                                    third_party_key)
118

    
119
    if not provider.get_create_policy:
120
        messages.error(request, provider.get_disabled_for_create_msg)
121
        return HttpResponseRedirect(reverse('login'))
122

    
123
    # TODO: this could be stored in session
124
    # TODO: create a management command to clean old PendingThirdPartyUser
125
    user, created = PendingThirdPartyUser.objects.get_or_create(
126
        third_party_identifier=userid,
127
        provider=provider_module,
128
    )
129

    
130
    # update pending user
131
    for param, value in pending_user_params.iteritems():
132
        setattr(user, param, value)
133

    
134
    user.info = json.dumps(provider_info)
135
    user.generate_token()
136

    
137
    # skip non required fields validation errors. Reset the field instead of
138
    # raising a validation exception.
139
    try:
140
        user.full_clean()
141
    except ValidationError, e:
142
        non_required_fields = ['email', 'first_name',
143
                               'last_name', 'affiliation']
144
        for field in e.message_dict.keys():
145
            if field in non_required_fields:
146
                setattr(user, field, None)
147

    
148
    user.save()
149

    
150
    extra_context['provider'] = provider.module
151
    extra_context['provider_title'] = provider.get_title_msg
152
    extra_context['token'] = user.token
153
    extra_context['signup_url'] = reverse('signup') + \
154
        "?third_party_token=%s" % user.token
155
    extra_context['add_url'] = reverse('index') + \
156
        "?key=%s#other-login-methods" % user.token
157
    extra_context['can_create'] = provider.get_create_policy
158
    extra_context['can_add'] = provider.get_add_policy
159

    
160
    return HttpResponseRedirect(extra_context['signup_url'])
161

    
162

    
163
@transaction.commit_on_success
164
def handle_third_party_login(request, provider_module, identifier,
165
                             provider_info=None, affiliation=None,
166
                             third_party_key=None):
167

    
168
    if not provider_info:
169
        provider_info = {}
170

    
171
    if not affiliation:
172
        affiliation = provider_module.title()
173

    
174
    next_redirect = request.GET.get(
175
        'next', request.session.get('next_url', None))
176

    
177
    if 'next_url' in request.session:
178
        del request.session['next_url']
179

    
180
    third_party_request_params = get_third_party_session_params(request)
181
    from_login = third_party_request_params.get('from_login', False)
182
    switch_from = third_party_request_params.get('switch_from', False)
183
    provider_data = {
184
        'affiliation': affiliation,
185
        'info': provider_info
186
    }
187

    
188
    provider = auth.get_provider(provider_module, request.user, identifier,
189
                                 **provider_data)
190

    
191
    # an existing user accessed the view
192
    if request.user.is_authenticated():
193
        if request.user.has_auth_provider(provider.module,
194
                                          identifier=identifier):
195
            return HttpResponseRedirect(reverse('edit_profile'))
196

    
197
        if provider.verified_exists():
198
            provider.log("add failed (identifier exists to another user)")
199
            messages.error(request, provider.get_add_exists_msg)
200
            return HttpResponseRedirect(reverse('edit_profile'))
201

    
202
        # automatically add identifier provider to user
203
        if not switch_from and not provider.get_add_policy:
204
            # TODO: handle existing uuid message separately
205
            provider.log("user cannot add provider")
206
            messages.error(request, provider.get_add_failed_msg)
207
            return HttpResponseRedirect(reverse('edit_profile'))
208

    
209
        user = request.user
210
        if switch_from:
211
            existing_provider = \
212
                request.user.auth_providers.active().get(
213
                    pk=int(switch_from), module=provider_module).settings
214

    
215
            # this is not a provider removal so we don't not use
216
            # provider.remove_from_user. Use low level access to the provider
217
            # db instance.
218
            if not provider.verified_exists():
219
                if provider.get_add_policy:
220
                    existing_provider._instance.delete()
221
                    existing_provider.log("removed")
222
                    provider.add_to_user()
223
                    provider.log("added")
224
            else:
225
                messages.error(request, provider.get_add_exists_msg)
226
                return HttpResponseRedirect(reverse('edit_profile'))
227

    
228
            messages.success(request, provider.get_switch_success_msg)
229
            return HttpResponseRedirect(reverse('edit_profile'))
230

    
231
        provider.add_to_user()
232
        provider.log("added")
233
        provider = user.get_auth_provider(provider_module, identifier)
234
        messages.success(request, provider.get_added_msg)
235
        return HttpResponseRedirect(reverse('edit_profile'))
236

    
237
    # astakos user exists ?
238
    try:
239
        user = AstakosUser.objects.get_auth_provider_user(
240
            provider_module,
241
            identifier=identifier,
242
            user__email_verified=True,
243
        )
244
    except AstakosUser.DoesNotExist:
245
        # TODO: add a message ? redirec to login ?
246
        if astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN:
247
            messages.warning(request,
248
                             astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN)
249
        raise
250

    
251
    if not third_party_key:
252
        third_party_key = get_pending_key(request)
253

    
254
    provider = user.get_auth_provider(provider_module, identifier)
255
    if user.is_active:
256
        if not provider.get_login_policy:
257
            messages.error(request, provider.get_login_disabled_msg)
258
            return HttpResponseRedirect(reverse('login'))
259

    
260
        # authenticate user
261
        response = prepare_response(request, user, next_redirect,
262
                                    'renew' in request.GET)
263

    
264
        messages.success(request, provider.get_login_success_msg)
265
        add_pending_auth_provider(request, third_party_key, provider)
266
        response.set_cookie('astakos_last_login_method', provider_module)
267
        return response
268
    else:
269
        message = user.get_inactive_message(provider_module, identifier)
270
        messages.error(request, message)
271
        return HttpResponseRedirect(login_url(request))