Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / target / linkedin.py @ 564a2292

History | View | Annotate | Download (8 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 astakos.im.messages as astakos_messages
61

    
62
import logging
63

    
64
logger = logging.getLogger(__name__)
65

    
66
import oauth2 as oauth
67
import cgi
68

    
69
consumer = oauth.Consumer(settings.LINKEDIN_TOKEN, settings.LINKEDIN_SECRET)
70
client = oauth.Client(consumer)
71

    
72
request_token_url      = 'https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress'
73
access_token_url       = 'https://api.linkedin.com/uas/oauth/accessToken'
74
authenticate_url       = 'https://www.linkedin.com/uas/oauth/authorize'
75

    
76

    
77
@requires_auth_provider('linkedin', login=True)
78
@require_http_methods(["GET", "POST"])
79
def login(request):
80
    resp, content = client.request(request_token_url, "GET")
81
    if resp['status'] != '200':
82
        messages.error(request, 'Invalid linkedin response')
83
        return HttpResponseRedirect(reverse('edit_profile'))
84

    
85
    request_token = dict(cgi.parse_qsl(content))
86
    request.session['request_token'] = request_token
87

    
88
    url = request_token.get('xoauth_request_auth_url') + "?oauth_token=%s" % request_token.get('oauth_token')
89

    
90
    if request.GET.get('key', None):
91
        request.session['pending_key'] = request.GET.get('key')
92

    
93
    return HttpResponseRedirect(url)
94

    
95

    
96
@requires_auth_provider('linkedin', login=True)
97
@require_http_methods(["GET", "POST"])
98
def authenticated(
99
    request,
100
    template='im/third_party_check_local.html',
101
    extra_context={}
102
):
103

    
104
    if request.GET.get('denied'):
105
        return HttpResponseRedirect(reverse('edit_profile'))
106

    
107
    if not 'request_token' in request.session:
108
        messages.error(request, 'linkedin handshake failed')
109
        return HttpResponseRedirect(reverse('edit_profile'))
110

    
111
    token = oauth.Token(request.session['request_token']['oauth_token'],
112
        request.session['request_token']['oauth_token_secret'])
113
    token.set_verifier(request.GET.get('oauth_verifier'))
114
    client = oauth.Client(consumer, token)
115
    resp, content = client.request(access_token_url, "POST")
116
    if resp['status'] != '200':
117
        try:
118
            del request.session['request_token']
119
        except:
120
            pass
121
        messages.error(request, 'Invalid linkedin token response')
122
        return HttpResponseRedirect(reverse('edit_profile'))
123
    access_token = dict(cgi.parse_qsl(content))
124

    
125
    token = oauth.Token(access_token['oauth_token'],
126
        access_token['oauth_token_secret'])
127
    client = oauth.Client(consumer, token)
128
    resp, content = client.request("http://api.linkedin.com/v1/people/~:(id,first-name,last-name,industry,email-address)?format=json", "GET")
129
    if resp['status'] != '200':
130
        try:
131
            del request.session['request_token']
132
        except:
133
            pass
134
        messages.error(request, 'Invalid linkedin profile response')
135
        return HttpResponseRedirect(reverse('edit_profile'))
136

    
137
    profile_data = json.loads(content)
138
    userid = profile_data['id']
139
    username = profile_data.get('emailAddress', None)
140
    realname = profile_data.get('firstName', '') + ' ' + profile_data.get('lastName', '')
141
    provider_info = profile_data
142
    affiliation = 'LinkedIn.com'
143

    
144
    third_party_key = get_pending_key(request)
145

    
146
    # an existing user accessed the view
147
    if request.user.is_authenticated():
148
        if request.user.has_auth_provider('linkedin', identifier=userid):
149
            return HttpResponseRedirect(reverse('edit_profile'))
150

    
151
        # automatically add eppn provider to user
152
        user = request.user
153
        if not request.user.can_add_auth_provider('linkedin',
154
                                                  identifier=userid):
155
            # TODO: handle existing uuid message separately
156
            messages.error(request, _(astakos_messages.AUTH_PROVIDER_ADD_FAILED) +
157
                          u' ' + _(astakos_messages.AUTH_PROVIDER_ADD_EXISTS))
158
            return HttpResponseRedirect(reverse('edit_profile'))
159

    
160
        user.add_auth_provider('linkedin', identifier=userid,
161
                               affiliation=affiliation,
162
                               provider_info=provider_info)
163
        messages.success(request, astakos_messages.AUTH_PROVIDER_ADDED)
164
        return HttpResponseRedirect(reverse('edit_profile'))
165

    
166
    try:
167
        # astakos user exists ?
168
        user = AstakosUser.objects.get_auth_provider_user(
169
            'linkedin',
170
            identifier=userid
171
        )
172
        if user.is_active:
173
            # authenticate user
174
            response = prepare_response(request,
175
                                    user,
176
                                    request.GET.get('next'),
177
                                    'renew' in request.GET)
178
            provider = auth_providers.get_provider('linkedin')
179
            messages.success(request, _(astakos_messages.LOGIN_SUCCESS) %
180
                             _(provider.get_login_message_display))
181
            add_pending_auth_provider(request, third_party_key)
182
            response.set_cookie('astakos_last_login_method', 'linkedin')
183
            return response
184
        else:
185
            message = user.get_inactive_message()
186
            messages.error(request, message)
187
            return HttpResponseRedirect(login_url(request))
188

    
189
    except AstakosUser.DoesNotExist, e:
190
        user_info = {'affiliation': affiliation, 'realname': realname}
191
        return handle_third_party_signup(request, userid, 'linkedin',
192
                                         third_party_key,
193
                                         provider_info,
194
                                         user_info,
195
                                         template,
196
                                         extra_context)
197