Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / target / google.py @ 64492c49

History | View | Annotate | Download (8.3 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.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, login_url
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
from astakos.im.target import add_pending_auth_provider, get_pending_key, \
58
    handle_third_party_signup
59

    
60
import logging
61
import time
62
import astakos.im.messages as astakos_messages
63
import urlparse
64
import urllib
65

    
66
logger = logging.getLogger(__name__)
67

    
68
import oauth2 as oauth
69
import cgi
70

    
71
signature_method = oauth.SignatureMethod_HMAC_SHA1()
72

    
73
OAUTH_CONSUMER_KEY = settings.GOOGLE_CLIENT_ID
74
OAUTH_CONSUMER_SECRET = settings.GOOGLE_SECRET
75

    
76
consumer = oauth.Consumer(key=OAUTH_CONSUMER_KEY, secret=OAUTH_CONSUMER_SECRET)
77
client = oauth.Client(consumer)
78

    
79
token_scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
80
authenticate_url = 'https://accounts.google.com/o/oauth2/auth'
81
access_token_url = 'https://www.googleapis.com/oauth2/v1/tokeninfo'
82
request_token_url = 'https://accounts.google.com/o/oauth2/token'
83

    
84

    
85
def get_redirect_uri():
86
    return "%s%s" % (settings.BASEURL,
87
                   reverse('astakos.im.target.google.authenticated'))
88

    
89
@requires_auth_provider('google', login=True)
90
@require_http_methods(["GET", "POST"])
91
def login(request):
92
    params = {
93
        'scope': token_scope,
94
        'response_type': 'code',
95
        'redirect_uri': get_redirect_uri(),
96
        'client_id': settings.GOOGLE_CLIENT_ID
97
    }
98
    force_login = request.GET.get('force_login', False)
99
    if force_login:
100
        params['approval_prompt'] = 'force'
101

    
102
    if request.GET.get('key', None):
103
        request.session['pending_key'] = request.GET.get('key')
104

    
105
    if request.GET.get('next', None):
106
        request.session['next_url'] = request.GET.get('next')
107

    
108
    url = "%s?%s" % (authenticate_url, urllib.urlencode(params))
109
    return HttpResponseRedirect(url)
110

    
111

    
112
@requires_auth_provider('google', login=True)
113
@require_http_methods(["GET", "POST"])
114
def authenticated(
115
    request,
116
    template='im/third_party_check_local.html',
117
    extra_context={}
118
):
119

    
120
    next_url = None
121
    if 'next_url' in request.session:
122
        next_url = request.session['next_url']
123
        del request.session['next_url']
124

    
125
    if request.GET.get('error', None):
126
        return HttpResponseRedirect(reverse('edit_profile'))
127

    
128
    # TODO: Handle errors, e.g. error=access_denied
129
    try:
130
        code = request.GET.get('code', None)
131
        params = {
132
            'code': code,
133
            'client_id': settings.GOOGLE_CLIENT_ID,
134
            'client_secret': settings.GOOGLE_SECRET,
135
            'redirect_uri': get_redirect_uri(),
136
            'grant_type': 'authorization_code'
137
        }
138
        get_token_url = "%s" % (request_token_url,)
139
        resp, content = client.request(get_token_url, "POST",
140
                                       body=urllib.urlencode(params))
141
        token = json.loads(content).get('access_token', None)
142

    
143
        resp, content = client.request("%s?access_token=%s" % (access_token_url,
144
                                                               token) , "GET")
145
        access_token_data = json.loads(content)
146
    except Exception, e:
147
        messages.error(request, 'Invalid Google response. Please contact support')
148
        return HttpResponseRedirect(reverse('edit_profile'))
149

    
150
    if not access_token_data.get('user_id', None):
151
        messages.error(request, 'Invalid Google response. Please contact support')
152
        return HttpResponseRedirect(reverse('edit_profile'))
153

    
154
    userid = access_token_data['user_id']
155
    username = access_token_data.get('email', None)
156
    provider_info = access_token_data
157
    affiliation = 'Google.com'
158

    
159
    third_party_key = get_pending_key(request)
160

    
161
    # an existing user accessed the view
162
    if request.user.is_authenticated():
163
        if request.user.has_auth_provider('google', identifier=userid):
164
            return HttpResponseRedirect(reverse('edit_profile'))
165

    
166
        # automatically add eppn provider to user
167
        user = request.user
168
        if not request.user.can_add_auth_provider('google',
169
                                                  identifier=userid):
170
            # TODO: handle existing uuid message separately
171
            messages.error(request, _(astakos_messages.AUTH_PROVIDER_ADD_FAILED) +
172
                          u' ' + _(astakos_messages.AUTH_PROVIDER_ADD_EXISTS))
173
            return HttpResponseRedirect(reverse('edit_profile'))
174

    
175
        user.add_auth_provider('google', identifier=userid,
176
                               affiliation=affiliation,
177
                               provider_info=provider_info)
178
        messages.success(request, astakos_messages.AUTH_PROVIDER_ADDED)
179
        return HttpResponseRedirect(reverse('edit_profile'))
180

    
181
    try:
182
        # astakos user exists ?
183
        user = AstakosUser.objects.get_auth_provider_user(
184
            'google',
185
            identifier=userid
186
        )
187
        if user.is_active:
188
            # authenticate user
189
            response = prepare_response(request,
190
                                    user,
191
                                    next_url,
192
                                    'renew' in request.GET)
193

    
194
            provider = auth_providers.get_provider('google')
195
            messages.success(request, _(astakos_messages.LOGIN_SUCCESS) %
196
                             _(provider.get_login_message_display))
197
            add_pending_auth_provider(request, third_party_key)
198
            response.set_cookie('astakos_last_login_method', 'google')
199
            return response
200
        else:
201
            message = user.get_inactive_message()
202
            messages.error(request, message)
203
            return HttpResponseRedirect(login_url(request))
204

    
205
    except AstakosUser.DoesNotExist, e:
206
        user_info = {'affiliation': affiliation}
207
        return handle_third_party_signup(request, userid, 'google',
208
                                         third_party_key,
209
                                         provider_info,
210
                                         user_info,
211
                                         template,
212
                                         extra_context)
213