Additional messages in third party registration/login process
[astakos] / snf-astakos-app / astakos / im / target / twitter.py
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.http import HttpResponseBadRequest
37 from django.utils.translation import ugettext as _
38 from django.contrib import messages
39 from django.template import RequestContext
40 from django.views.decorators.http import require_http_methods
41 from django.http import HttpResponseRedirect
42 from django.core.urlresolvers import reverse
43 from django.core.exceptions import ImproperlyConfigured
44 from django.shortcuts import get_object_or_404
45
46 from urlparse import urlunsplit, urlsplit
47
48 from astakos.im.util import prepare_response, get_context
49 from astakos.im.views import requires_anonymous, render_response, \
50         requires_auth_provider
51 from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
52 from astakos.im.models import AstakosUser, PendingThirdPartyUser
53 from astakos.im.forms import LoginForm
54 from astakos.im.activation_backends import get_backend, SimpleBackend
55 from astakos.im import settings
56 from astakos.im import auth_providers
57
58 import astakos.im.messages as astakos_messages
59
60 import logging
61
62 logger = logging.getLogger(__name__)
63
64 import oauth2 as oauth
65 import cgi
66
67 consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
68 client = oauth.Client(consumer)
69
70 request_token_url = 'http://twitter.com/oauth/request_token'
71 access_token_url = 'http://twitter.com/oauth/access_token'
72 authenticate_url = 'http://twitter.com/oauth/authenticate'
73
74
75 @requires_auth_provider('twitter', login=True)
76 @require_http_methods(["GET", "POST"])
77 def login(request):
78     resp, content = client.request(request_token_url, "GET")
79     if resp['status'] != '200':
80         messages.error(request, 'Invalid Twitter response')
81         return HttpResponseRedirect(reverse('edit_profile'))
82
83     request.session['request_token'] = dict(cgi.parse_qsl(content))
84     url = "%s?oauth_token=%s" % (authenticate_url,
85         request.session['request_token']['oauth_token'])
86
87     return HttpResponseRedirect(url)
88
89
90 @requires_auth_provider('twitter', login=True)
91 @require_http_methods(["GET", "POST"])
92 def authenticated(
93     request,
94     template='im/third_party_check_local.html',
95     extra_context={}
96 ):
97
98     if not 'request_token' in request.session:
99         messages.error(request, 'Twitter handshake failed')
100         return HttpResponseRedirect(reverse('edit_profile'))
101
102     token = oauth.Token(request.session['request_token']['oauth_token'],
103         request.session['request_token']['oauth_token_secret'])
104     client = oauth.Client(consumer, token)
105
106     # Step 2. Request the authorized access token from Twitter.
107     resp, content = client.request(access_token_url, "GET")
108     if resp['status'] != '200':
109         try:
110           del request.session['request_token']
111         except:
112           pass
113         messages.error(request, 'Invalid Twitter response')
114         return HttpResponseRedirect(reverse('edit_profile'))
115
116     access_token = dict(cgi.parse_qsl(content))
117     userid = access_token['user_id']
118     username = access_token.get('screen_name', userid)
119     provider_info = {'screen_name': username}
120     affiliation = 'Twitter.com'
121
122     # an existing user accessed the view
123     if request.user.is_authenticated():
124         if request.user.has_auth_provider('twitter', identifier=userid):
125             return HttpResponseRedirect(reverse('edit_profile'))
126
127         # automatically add eppn provider to user
128         user = request.user
129         if not request.user.can_add_auth_provider('twitter',
130                                                   identifier=userid):
131             messages.error(request, 'Account already exists.')
132             return HttpResponseRedirect(reverse('edit_profile'))
133
134         user.add_auth_provider('twitter', identifier=userid,
135                                affiliation=affiliation,
136                                provider_info=provider_info)
137         messages.success(request, 'Account assigned.')
138         return HttpResponseRedirect(reverse('edit_profile'))
139
140     try:
141         # astakos user exists ?
142         user = AstakosUser.objects.get_auth_provider_user(
143             'twitter',
144             identifier=userid
145         )
146         if user.is_active:
147             # authenticate user
148             return prepare_response(request,
149                                     user,
150                                     request.GET.get('next'),
151                                     'renew' in request.GET)
152         else:
153             message = user.get_inactive_message()
154             messages.error(request, message)
155             return HttpResponseRedirect(reverse('login'))
156
157     except AstakosUser.DoesNotExist, e:
158         provider = auth_providers.get_provider('twitter')
159         if not provider.is_available_for_create():
160             messages.error(request,
161                            _(astakos_messages.AUTH_PROVIDER_NOT_ACTIVE) % provider.get_title_display)
162             return HttpResponseRedirect(reverse('login'))
163
164         # eppn not stored in astakos models, create pending profile
165         user, created = PendingThirdPartyUser.objects.get_or_create(
166             third_party_identifier=userid,
167             provider='twitter',
168             info=json.dumps(provider_info)
169         )
170         # update pending user
171         user.affiliation = affiliation
172         user.generate_token()
173         user.save()
174
175         extra_context['provider'] = 'twitter'
176         extra_context['provider_title'] = 'Twitter'
177         extra_context['token'] = user.token
178         extra_context['signup_url'] = reverse('signup') + \
179                                     "?third_party_token=%s" % user.token
180
181         return render_response(
182             template,
183             context_instance=get_context(request, extra_context)
184         )
185