Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / target / google.py @ 9f2d1323

History | View | Annotate | Download (8 kB)

1 74796dd8 Kostas Papadimitriou
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 74796dd8 Kostas Papadimitriou
#
3 74796dd8 Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 74796dd8 Kostas Papadimitriou
# without modification, are permitted provided that the following
5 74796dd8 Kostas Papadimitriou
# conditions are met:
6 74796dd8 Kostas Papadimitriou
#
7 74796dd8 Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 74796dd8 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 74796dd8 Kostas Papadimitriou
#      disclaimer.
10 74796dd8 Kostas Papadimitriou
#
11 74796dd8 Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 74796dd8 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 74796dd8 Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 74796dd8 Kostas Papadimitriou
#      provided with the distribution.
15 74796dd8 Kostas Papadimitriou
#
16 74796dd8 Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 74796dd8 Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 74796dd8 Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 74796dd8 Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 74796dd8 Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 74796dd8 Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 74796dd8 Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 74796dd8 Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 74796dd8 Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 74796dd8 Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 74796dd8 Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 74796dd8 Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 74796dd8 Kostas Papadimitriou
#
29 74796dd8 Kostas Papadimitriou
# The views and conclusions contained in the software and
30 74796dd8 Kostas Papadimitriou
# documentation are those of the authors and should not be
31 74796dd8 Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 74796dd8 Kostas Papadimitriou
# or implied, of GRNET S.A.
33 74796dd8 Kostas Papadimitriou
34 74796dd8 Kostas Papadimitriou
import json
35 74796dd8 Kostas Papadimitriou
36 74796dd8 Kostas Papadimitriou
from django.http import HttpResponseBadRequest
37 74796dd8 Kostas Papadimitriou
from django.utils.translation import ugettext as _
38 74796dd8 Kostas Papadimitriou
from django.contrib import messages
39 74796dd8 Kostas Papadimitriou
from django.template import RequestContext
40 74796dd8 Kostas Papadimitriou
from django.views.decorators.http import require_http_methods
41 74796dd8 Kostas Papadimitriou
from django.http import HttpResponseRedirect
42 74796dd8 Kostas Papadimitriou
from django.core.urlresolvers import reverse
43 74796dd8 Kostas Papadimitriou
from django.core.exceptions import ImproperlyConfigured
44 74796dd8 Kostas Papadimitriou
from django.shortcuts import get_object_or_404
45 74796dd8 Kostas Papadimitriou
46 74796dd8 Kostas Papadimitriou
from urlparse import urlunsplit, urlsplit
47 74796dd8 Kostas Papadimitriou
48 dd5f8f4d Kostas Papadimitriou
from astakos.im.util import prepare_response, get_context, login_url
49 74796dd8 Kostas Papadimitriou
from astakos.im.views import requires_anonymous, render_response, \
50 74796dd8 Kostas Papadimitriou
        requires_auth_provider
51 74796dd8 Kostas Papadimitriou
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
52 74796dd8 Kostas Papadimitriou
from astakos.im.models import AstakosUser, PendingThirdPartyUser
53 74796dd8 Kostas Papadimitriou
from astakos.im.forms import LoginForm
54 74796dd8 Kostas Papadimitriou
from astakos.im.activation_backends import get_backend, SimpleBackend
55 74796dd8 Kostas Papadimitriou
from astakos.im import settings
56 74796dd8 Kostas Papadimitriou
from astakos.im import auth_providers
57 dd5f8f4d Kostas Papadimitriou
from astakos.im.target import add_pending_auth_provider, get_pending_key, \
58 dd5f8f4d Kostas Papadimitriou
    handle_third_party_signup
59 74796dd8 Kostas Papadimitriou
60 74796dd8 Kostas Papadimitriou
import logging
61 74796dd8 Kostas Papadimitriou
import time
62 74796dd8 Kostas Papadimitriou
import astakos.im.messages as astakos_messages
63 74796dd8 Kostas Papadimitriou
import urlparse
64 74796dd8 Kostas Papadimitriou
import urllib
65 74796dd8 Kostas Papadimitriou
66 74796dd8 Kostas Papadimitriou
logger = logging.getLogger(__name__)
67 74796dd8 Kostas Papadimitriou
68 74796dd8 Kostas Papadimitriou
import oauth2 as oauth
69 74796dd8 Kostas Papadimitriou
import cgi
70 74796dd8 Kostas Papadimitriou
71 74796dd8 Kostas Papadimitriou
signature_method = oauth.SignatureMethod_HMAC_SHA1()
72 74796dd8 Kostas Papadimitriou
73 74796dd8 Kostas Papadimitriou
OAUTH_CONSUMER_KEY = settings.GOOGLE_CLIENT_ID
74 74796dd8 Kostas Papadimitriou
OAUTH_CONSUMER_SECRET = settings.GOOGLE_SECRET
75 74796dd8 Kostas Papadimitriou
76 74796dd8 Kostas Papadimitriou
consumer = oauth.Consumer(key=OAUTH_CONSUMER_KEY, secret=OAUTH_CONSUMER_SECRET)
77 74796dd8 Kostas Papadimitriou
client = oauth.Client(consumer)
78 74796dd8 Kostas Papadimitriou
79 74796dd8 Kostas Papadimitriou
token_scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
80 74796dd8 Kostas Papadimitriou
authenticate_url = 'https://accounts.google.com/o/oauth2/auth'
81 74796dd8 Kostas Papadimitriou
access_token_url = 'https://www.googleapis.com/oauth2/v1/tokeninfo'
82 74796dd8 Kostas Papadimitriou
request_token_url = 'https://accounts.google.com/o/oauth2/token'
83 74796dd8 Kostas Papadimitriou
84 74796dd8 Kostas Papadimitriou
85 74796dd8 Kostas Papadimitriou
def get_redirect_uri():
86 74796dd8 Kostas Papadimitriou
    return "%s%s" % (settings.BASEURL,
87 74796dd8 Kostas Papadimitriou
                   reverse('astakos.im.target.google.authenticated'))
88 74796dd8 Kostas Papadimitriou
89 74796dd8 Kostas Papadimitriou
@requires_auth_provider('google', login=True)
90 74796dd8 Kostas Papadimitriou
@require_http_methods(["GET", "POST"])
91 74796dd8 Kostas Papadimitriou
def login(request):
92 74796dd8 Kostas Papadimitriou
    params = {
93 74796dd8 Kostas Papadimitriou
        'scope': token_scope,
94 74796dd8 Kostas Papadimitriou
        'response_type': 'code',
95 74796dd8 Kostas Papadimitriou
        'redirect_uri': get_redirect_uri(),
96 74796dd8 Kostas Papadimitriou
        'client_id': settings.GOOGLE_CLIENT_ID
97 74796dd8 Kostas Papadimitriou
    }
98 e339bd10 Kostas Papadimitriou
    force_login = request.GET.get('force_login', False)
99 e339bd10 Kostas Papadimitriou
    if force_login:
100 e339bd10 Kostas Papadimitriou
        params['approval_prompt'] = 'force'
101 e339bd10 Kostas Papadimitriou
102 dd5f8f4d Kostas Papadimitriou
    if request.GET.get('key', None):
103 dd5f8f4d Kostas Papadimitriou
        request.session['pending_key'] = request.GET.get('key')
104 dd5f8f4d Kostas Papadimitriou
105 74796dd8 Kostas Papadimitriou
    url = "%s?%s" % (authenticate_url, urllib.urlencode(params))
106 74796dd8 Kostas Papadimitriou
    return HttpResponseRedirect(url)
107 74796dd8 Kostas Papadimitriou
108 74796dd8 Kostas Papadimitriou
109 74796dd8 Kostas Papadimitriou
@requires_auth_provider('google', login=True)
110 74796dd8 Kostas Papadimitriou
@require_http_methods(["GET", "POST"])
111 74796dd8 Kostas Papadimitriou
def authenticated(
112 74796dd8 Kostas Papadimitriou
    request,
113 74796dd8 Kostas Papadimitriou
    template='im/third_party_check_local.html',
114 74796dd8 Kostas Papadimitriou
    extra_context={}
115 74796dd8 Kostas Papadimitriou
):
116 74796dd8 Kostas Papadimitriou
117 e339bd10 Kostas Papadimitriou
    if request.GET.get('error', None):
118 e339bd10 Kostas Papadimitriou
        return HttpResponseRedirect(reverse('edit_profile'))
119 e339bd10 Kostas Papadimitriou
120 74796dd8 Kostas Papadimitriou
    # TODO: Handle errors, e.g. error=access_denied
121 74796dd8 Kostas Papadimitriou
    try:
122 74796dd8 Kostas Papadimitriou
        code = request.GET.get('code', None)
123 74796dd8 Kostas Papadimitriou
        params = {
124 74796dd8 Kostas Papadimitriou
            'code': code,
125 74796dd8 Kostas Papadimitriou
            'client_id': settings.GOOGLE_CLIENT_ID,
126 74796dd8 Kostas Papadimitriou
            'client_secret': settings.GOOGLE_SECRET,
127 74796dd8 Kostas Papadimitriou
            'redirect_uri': get_redirect_uri(),
128 74796dd8 Kostas Papadimitriou
            'grant_type': 'authorization_code'
129 74796dd8 Kostas Papadimitriou
        }
130 74796dd8 Kostas Papadimitriou
        get_token_url = "%s" % (request_token_url,)
131 74796dd8 Kostas Papadimitriou
        resp, content = client.request(get_token_url, "POST",
132 74796dd8 Kostas Papadimitriou
                                       body=urllib.urlencode(params))
133 74796dd8 Kostas Papadimitriou
        token = json.loads(content).get('access_token', None)
134 74796dd8 Kostas Papadimitriou
135 74796dd8 Kostas Papadimitriou
        resp, content = client.request("%s?access_token=%s" % (access_token_url,
136 74796dd8 Kostas Papadimitriou
                                                               token) , "GET")
137 74796dd8 Kostas Papadimitriou
        access_token_data = json.loads(content)
138 74796dd8 Kostas Papadimitriou
    except Exception, e:
139 74796dd8 Kostas Papadimitriou
        messages.error(request, 'Invalid Google response. Please contact support')
140 9f12cd1c Kostas Papadimitriou
        return HttpResponseRedirect(reverse('edit_profile'))
141 9f12cd1c Kostas Papadimitriou
142 9f12cd1c Kostas Papadimitriou
    if not access_token_data.get('user_id', None):
143 9f12cd1c Kostas Papadimitriou
        messages.error(request, 'Invalid Google response. Please contact support')
144 74796dd8 Kostas Papadimitriou
        return HttpResponseRedirect(reverse('edit_profile'))
145 74796dd8 Kostas Papadimitriou
146 74796dd8 Kostas Papadimitriou
    userid = access_token_data['user_id']
147 74796dd8 Kostas Papadimitriou
    username = access_token_data.get('email', None)
148 74796dd8 Kostas Papadimitriou
    provider_info = access_token_data
149 74796dd8 Kostas Papadimitriou
    affiliation = 'Google.com'
150 74796dd8 Kostas Papadimitriou
151 dd5f8f4d Kostas Papadimitriou
    third_party_key = get_pending_key(request)
152 dd5f8f4d Kostas Papadimitriou
153 74796dd8 Kostas Papadimitriou
    # an existing user accessed the view
154 74796dd8 Kostas Papadimitriou
    if request.user.is_authenticated():
155 74796dd8 Kostas Papadimitriou
        if request.user.has_auth_provider('google', identifier=userid):
156 74796dd8 Kostas Papadimitriou
            return HttpResponseRedirect(reverse('edit_profile'))
157 74796dd8 Kostas Papadimitriou
158 74796dd8 Kostas Papadimitriou
        # automatically add eppn provider to user
159 74796dd8 Kostas Papadimitriou
        user = request.user
160 74796dd8 Kostas Papadimitriou
        if not request.user.can_add_auth_provider('google',
161 74796dd8 Kostas Papadimitriou
                                                  identifier=userid):
162 91bbc1a4 Kostas Papadimitriou
            # TODO: handle existing uuid message separately
163 74796dd8 Kostas Papadimitriou
            messages.error(request, _(astakos_messages.AUTH_PROVIDER_ADD_FAILED) +
164 74796dd8 Kostas Papadimitriou
                          u' ' + _(astakos_messages.AUTH_PROVIDER_ADD_EXISTS))
165 74796dd8 Kostas Papadimitriou
            return HttpResponseRedirect(reverse('edit_profile'))
166 74796dd8 Kostas Papadimitriou
167 74796dd8 Kostas Papadimitriou
        user.add_auth_provider('google', identifier=userid,
168 74796dd8 Kostas Papadimitriou
                               affiliation=affiliation,
169 74796dd8 Kostas Papadimitriou
                               provider_info=provider_info)
170 74796dd8 Kostas Papadimitriou
        messages.success(request, astakos_messages.AUTH_PROVIDER_ADDED)
171 74796dd8 Kostas Papadimitriou
        return HttpResponseRedirect(reverse('edit_profile'))
172 74796dd8 Kostas Papadimitriou
173 74796dd8 Kostas Papadimitriou
    try:
174 74796dd8 Kostas Papadimitriou
        # astakos user exists ?
175 74796dd8 Kostas Papadimitriou
        user = AstakosUser.objects.get_auth_provider_user(
176 74796dd8 Kostas Papadimitriou
            'google',
177 74796dd8 Kostas Papadimitriou
            identifier=userid
178 74796dd8 Kostas Papadimitriou
        )
179 74796dd8 Kostas Papadimitriou
        if user.is_active:
180 74796dd8 Kostas Papadimitriou
            # authenticate user
181 74796dd8 Kostas Papadimitriou
            response = prepare_response(request,
182 74796dd8 Kostas Papadimitriou
                                    user,
183 74796dd8 Kostas Papadimitriou
                                    request.GET.get('next'),
184 74796dd8 Kostas Papadimitriou
                                    'renew' in request.GET)
185 564a2292 Kostas Papadimitriou
186 564a2292 Kostas Papadimitriou
            provider = auth_providers.get_provider('google')
187 564a2292 Kostas Papadimitriou
            messages.success(request, _(astakos_messages.LOGIN_SUCCESS) %
188 564a2292 Kostas Papadimitriou
                             _(provider.get_login_message_display))
189 dd5f8f4d Kostas Papadimitriou
            add_pending_auth_provider(request, third_party_key)
190 74796dd8 Kostas Papadimitriou
            response.set_cookie('astakos_last_login_method', 'google')
191 74796dd8 Kostas Papadimitriou
            return response
192 74796dd8 Kostas Papadimitriou
        else:
193 74796dd8 Kostas Papadimitriou
            message = user.get_inactive_message()
194 74796dd8 Kostas Papadimitriou
            messages.error(request, message)
195 dd5f8f4d Kostas Papadimitriou
            return HttpResponseRedirect(login_url(request))
196 74796dd8 Kostas Papadimitriou
197 74796dd8 Kostas Papadimitriou
    except AstakosUser.DoesNotExist, e:
198 dd5f8f4d Kostas Papadimitriou
        user_info = {'affiliation': affiliation}
199 dd5f8f4d Kostas Papadimitriou
        return handle_third_party_signup(request, userid, 'google',
200 dd5f8f4d Kostas Papadimitriou
                                         third_party_key,
201 dd5f8f4d Kostas Papadimitriou
                                         provider_info,
202 dd5f8f4d Kostas Papadimitriou
                                         user_info,
203 dd5f8f4d Kostas Papadimitriou
                                         template,
204 dd5f8f4d Kostas Papadimitriou
                                         extra_context)