Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / target / twitter.py @ db7fecd9

History | View | Annotate | Download (10.3 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 64cd4730 Antony Chazapis
# 
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 64cd4730 Antony Chazapis
# 
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 64cd4730 Antony Chazapis
# 
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 64cd4730 Antony Chazapis
# 
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 64cd4730 Antony Chazapis
# 
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 64cd4730 Antony Chazapis
34 64cd4730 Antony Chazapis
# This is based on the docs at: https://github.com/simplegeo/python-oauth2
35 64cd4730 Antony Chazapis
36 64cd4730 Antony Chazapis
import oauth2 as oauth
37 64cd4730 Antony Chazapis
import urlparse
38 64cd4730 Antony Chazapis
39 64cd4730 Antony Chazapis
from django.http import HttpResponse
40 64cd4730 Antony Chazapis
from django.utils import simplejson as json
41 15efc749 Sofia Papagiannaki
from django.contrib import messages
42 64cd4730 Antony Chazapis
43 63ecdd20 Sofia Papagiannaki
from astakos.im.util import get_context, prepare_response
44 f7e8a159 Sofia Papagiannaki
from astakos.im.models import AstakosUser, Invitation
45 f7e8a159 Sofia Papagiannaki
from astakos.im.views import render_response, requires_anonymous
46 15efc749 Sofia Papagiannaki
from astakos.im.forms import LocalUserCreationForm, ThirdPartyUserCreationForm
47 15efc749 Sofia Papagiannaki
from astakos.im.faults import BadRequest
48 f7e8a159 Sofia Papagiannaki
from astakos.im.backends import get_backend
49 92defad4 Sofia Papagiannaki
from astakos.im.settings import TWITTER_KEY, TWITTER_SECRET, INVITATIONS_ENABLED, IM_MODULES
50 64cd4730 Antony Chazapis
51 64cd4730 Antony Chazapis
# It's probably a good idea to put your consumer's OAuth token and
52 64cd4730 Antony Chazapis
# OAuth secret into your project's settings. 
53 92defad4 Sofia Papagiannaki
consumer = oauth.Consumer(TWITTER_KEY, TWITTER_SECRET)
54 64cd4730 Antony Chazapis
client = oauth.Client(consumer)
55 64cd4730 Antony Chazapis
56 64cd4730 Antony Chazapis
request_token_url = 'http://twitter.com/oauth/request_token'
57 64cd4730 Antony Chazapis
access_token_url = 'http://twitter.com/oauth/access_token'
58 64cd4730 Antony Chazapis
59 64cd4730 Antony Chazapis
# This is the slightly different URL used to authenticate/authorize.
60 64cd4730 Antony Chazapis
authenticate_url = 'http://twitter.com/oauth/authenticate'
61 64cd4730 Antony Chazapis
62 15efc749 Sofia Papagiannaki
@requires_anonymous
63 f7e8a159 Sofia Papagiannaki
def login(request, extra_context={}):
64 15efc749 Sofia Papagiannaki
    # store invitation code and email
65 15efc749 Sofia Papagiannaki
    request.session['email'] = request.GET.get('email')
66 15efc749 Sofia Papagiannaki
    request.session['invitation_code'] = request.GET.get('code')
67 15efc749 Sofia Papagiannaki
68 64cd4730 Antony Chazapis
    # Step 1. Get a request token from Twitter.
69 64cd4730 Antony Chazapis
    resp, content = client.request(request_token_url, "GET")
70 64cd4730 Antony Chazapis
    if resp['status'] != '200':
71 64cd4730 Antony Chazapis
        raise Exception("Invalid response from Twitter.")
72 64cd4730 Antony Chazapis
    request_token = dict(urlparse.parse_qsl(content))
73 64cd4730 Antony Chazapis
    if request.GET.get('next'):
74 64cd4730 Antony Chazapis
        request_token['next'] = request.GET['next']
75 64cd4730 Antony Chazapis
    
76 64cd4730 Antony Chazapis
    # Step 2. Store the request token in a session for later use.
77 64cd4730 Antony Chazapis
    response = HttpResponse()
78 15efc749 Sofia Papagiannaki
    request.session['Twitter-Request-Token'] = value=json.dumps(request_token)
79 64cd4730 Antony Chazapis
    
80 64cd4730 Antony Chazapis
    # Step 3. Redirect the user to the authentication URL.
81 64cd4730 Antony Chazapis
    url = "%s?oauth_token=%s" % (authenticate_url, request_token['oauth_token'])
82 64cd4730 Antony Chazapis
    response['Location'] = url
83 64cd4730 Antony Chazapis
    response.status_code = 302
84 64cd4730 Antony Chazapis
    
85 64cd4730 Antony Chazapis
    return response
86 64cd4730 Antony Chazapis
87 15efc749 Sofia Papagiannaki
@requires_anonymous
88 1e685275 Sofia Papagiannaki
def authenticated(request, backend=None, login_template='im/login.html', on_signup_failure='im/signup.html', on_signup_success='im/signup_complete.html', extra_context={}):
89 64cd4730 Antony Chazapis
    # Step 1. Use the request token in the session to build a new client.
90 15efc749 Sofia Papagiannaki
    data = request.session.get('Twitter-Request-Token')
91 64cd4730 Antony Chazapis
    if not data:
92 64cd4730 Antony Chazapis
        raise Exception("Request token cookie not found.")
93 15efc749 Sofia Papagiannaki
    del request.session['Twitter-Request-Token']
94 15efc749 Sofia Papagiannaki
    
95 64cd4730 Antony Chazapis
    request_token = json.loads(data)
96 64cd4730 Antony Chazapis
    if not hasattr(request_token, '__getitem__'):
97 64cd4730 Antony Chazapis
        raise BadRequest('Invalid data formating')
98 64cd4730 Antony Chazapis
    try:
99 64cd4730 Antony Chazapis
        token = oauth.Token(request_token['oauth_token'],
100 64cd4730 Antony Chazapis
                            request_token['oauth_token_secret'])
101 64cd4730 Antony Chazapis
    except:
102 64cd4730 Antony Chazapis
        raise BadRequest('Invalid request token cookie formatting')
103 64cd4730 Antony Chazapis
    client = oauth.Client(consumer, token)
104 64cd4730 Antony Chazapis
    
105 64cd4730 Antony Chazapis
    # Step 2. Request the authorized access token from Twitter.
106 64cd4730 Antony Chazapis
    resp, content = client.request(access_token_url, "GET")
107 64cd4730 Antony Chazapis
    if resp['status'] != '200':
108 64cd4730 Antony Chazapis
        raise Exception("Invalid response from Twitter.")
109 64cd4730 Antony Chazapis
    
110 64cd4730 Antony Chazapis
    """
111 64cd4730 Antony Chazapis
    This is what you'll get back from Twitter. Note that it includes the
112 64cd4730 Antony Chazapis
    user's user_id and screen_name.
113 64cd4730 Antony Chazapis
    {
114 64cd4730 Antony Chazapis
        'oauth_token_secret': 'IcJXPiJh8be3BjDWW50uCY31chyhsMHEhqJVsphC3M',
115 64cd4730 Antony Chazapis
        'user_id': '120889797', 
116 64cd4730 Antony Chazapis
        'oauth_token': '120889797-H5zNnM3qE0iFoTTpNEHIz3noL9FKzXiOxwtnyVOD',
117 64cd4730 Antony Chazapis
        'screen_name': 'heyismysiteup'
118 64cd4730 Antony Chazapis
    }
119 64cd4730 Antony Chazapis
    """
120 64cd4730 Antony Chazapis
    access_token = dict(urlparse.parse_qsl(content))
121 64cd4730 Antony Chazapis
    
122 64cd4730 Antony Chazapis
    # Step 3. Lookup the user or create them if they don't exist.
123 64cd4730 Antony Chazapis
    
124 64cd4730 Antony Chazapis
    # When creating the user I just use their screen_name@twitter.com
125 64cd4730 Antony Chazapis
    # for their email and the oauth_token_secret for their password.
126 64cd4730 Antony Chazapis
    # These two things will likely never be used. Alternatively, you 
127 64cd4730 Antony Chazapis
    # can prompt them for their email here. Either way, the password 
128 64cd4730 Antony Chazapis
    # should never be used.
129 15efc749 Sofia Papagiannaki
    screen_name = access_token['screen_name']
130 15efc749 Sofia Papagiannaki
    next = request_token.get('next')
131 15efc749 Sofia Papagiannaki
    
132 15efc749 Sofia Papagiannaki
    # check first if user with that email is registered
133 15efc749 Sofia Papagiannaki
    # and if not create one
134 15efc749 Sofia Papagiannaki
    user = None
135 15efc749 Sofia Papagiannaki
    email = request.session.pop('email')
136 b90b602c Sofia Papagiannaki
    
137 63ecdd20 Sofia Papagiannaki
    if email: # signup mode
138 15efc749 Sofia Papagiannaki
        if not reserved_screen_name(screen_name): 
139 15efc749 Sofia Papagiannaki
            try:
140 15efc749 Sofia Papagiannaki
                user = AstakosUser.objects.get(email = email)
141 15efc749 Sofia Papagiannaki
            except AstakosUser.DoesNotExist, e:
142 15efc749 Sofia Papagiannaki
                # register a new user
143 15efc749 Sofia Papagiannaki
                post_data = {'provider':'Twitter', 'affiliation':'twitter',
144 15efc749 Sofia Papagiannaki
                                'third_party_identifier':screen_name}
145 15efc749 Sofia Papagiannaki
                form = ThirdPartyUserCreationForm({'email':email})
146 f7e8a159 Sofia Papagiannaki
                return create_user(request, form, backend, post_data, next, on_signup_failure, on_signup_success, extra_context)
147 15efc749 Sofia Papagiannaki
        else:
148 15efc749 Sofia Papagiannaki
            status = messages.ERROR
149 15efc749 Sofia Papagiannaki
            message = '%s@twitter is already registered' % screen_name
150 15efc749 Sofia Papagiannaki
            messages.add_message(request, messages.ERROR, message)
151 f7e8a159 Sofia Papagiannaki
            prefix = 'Invited' if request.session['invitation_code'] else ''
152 f7e8a159 Sofia Papagiannaki
            suffix  = 'UserCreationForm'
153 92defad4 Sofia Papagiannaki
            for provider in IM_MODULES:
154 f7e8a159 Sofia Papagiannaki
                main = provider.capitalize() if provider == 'local' else 'ThirdParty'
155 f7e8a159 Sofia Papagiannaki
                formclass = '%s%s%s' % (prefix, main, suffix)
156 f7e8a159 Sofia Papagiannaki
                extra_context['%s_form' % provider] = globals()[formclass]()
157 f7e8a159 Sofia Papagiannaki
            return render_response(on_signup_failure,
158 f7e8a159 Sofia Papagiannaki
                                   context_instance=get_context(request, extra_context))
159 63ecdd20 Sofia Papagiannaki
    else: # login mode
160 15efc749 Sofia Papagiannaki
        try:
161 15efc749 Sofia Papagiannaki
            user = AstakosUser.objects.get(third_party_identifier = screen_name,
162 15efc749 Sofia Papagiannaki
                                           provider = 'Twitter')
163 15efc749 Sofia Papagiannaki
        except AstakosUser.DoesNotExist:
164 15efc749 Sofia Papagiannaki
            messages.add_message(request, messages.ERROR, 'Not registered user')
165 15efc749 Sofia Papagiannaki
        if user and user.is_active:
166 15efc749 Sofia Papagiannaki
            return prepare_response(request, user, next)
167 15efc749 Sofia Papagiannaki
        elif user and not user.is_active:
168 15efc749 Sofia Papagiannaki
            messages.add_message(request, messages.ERROR, 'Inactive account: %s' % user.email)
169 f7e8a159 Sofia Papagiannaki
    return render_response(login_template,
170 db7fecd9 Sofia Papagiannaki
                   form = LocalUserCreationForm(ip=request.META['REMOTE_ADDR']),
171 15efc749 Sofia Papagiannaki
                   context_instance=get_context(request, extra_context))
172 15efc749 Sofia Papagiannaki
173 15efc749 Sofia Papagiannaki
def reserved_screen_name(screen_name):
174 15efc749 Sofia Papagiannaki
    try:
175 15efc749 Sofia Papagiannaki
        AstakosUser.objects.get(provider='Twitter',
176 15efc749 Sofia Papagiannaki
                                third_party_identifier=screen_name)
177 15efc749 Sofia Papagiannaki
        return True
178 15efc749 Sofia Papagiannaki
    except AstakosUser.DoesNotExist, e:
179 f7e8a159 Sofia Papagiannaki
        return False
180 f7e8a159 Sofia Papagiannaki
181 1e685275 Sofia Papagiannaki
def create_user(request, form, backend=None, post_data={}, next = None, on_failure='im/signup.html', on_success='im/signup_complete.html', extra_context={}): 
182 f7e8a159 Sofia Papagiannaki
    """
183 f7e8a159 Sofia Papagiannaki
    Create a user.
184 f7e8a159 Sofia Papagiannaki
    
185 f7e8a159 Sofia Papagiannaki
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
186 f7e8a159 Sofia Papagiannaki
    if present, otherwise to the ``astakos.im.backends.InvitationBackend``
187 92defad4 Sofia Papagiannaki
    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.backends.SimpleBackend`` if not
188 f7e8a159 Sofia Papagiannaki
    (see backends);
189 f7e8a159 Sofia Papagiannaki
    
190 f7e8a159 Sofia Papagiannaki
    Upon successful user creation if ``next`` url parameter is present the user is redirected there
191 1e685275 Sofia Papagiannaki
    otherwise renders the ``on_success`` template (if exists) or im/signup_complete.html.
192 f7e8a159 Sofia Papagiannaki
    
193 1e685275 Sofia Papagiannaki
    On unsuccessful creation, renders the ``on_failure`` template (if exists) or im/signup.html with an error message.
194 f7e8a159 Sofia Papagiannaki
    
195 f7e8a159 Sofia Papagiannaki
    **Arguments**
196 f7e8a159 Sofia Papagiannaki
    
197 f7e8a159 Sofia Papagiannaki
    ``on_failure``
198 f7e8a159 Sofia Papagiannaki
        A custom template to render in case of failure. This is optional;
199 1e685275 Sofia Papagiannaki
        if not specified, this will default to ``im/signup.html``.
200 f7e8a159 Sofia Papagiannaki
    
201 f7e8a159 Sofia Papagiannaki
    ``on_success``
202 f7e8a159 Sofia Papagiannaki
        A custom template to render in case of success. This is optional;
203 1e685275 Sofia Papagiannaki
        if not specified, this will default to ``im/signup_complete.html``.
204 f7e8a159 Sofia Papagiannaki
    
205 f7e8a159 Sofia Papagiannaki
    ``extra_context``
206 f7e8a159 Sofia Papagiannaki
        An dictionary of variables to add to the template context.
207 f7e8a159 Sofia Papagiannaki
    
208 f7e8a159 Sofia Papagiannaki
    **Template:**
209 f7e8a159 Sofia Papagiannaki
    
210 1e685275 Sofia Papagiannaki
    im/signup.html or ``on_failure`` keyword argument.
211 1e685275 Sofia Papagiannaki
    im/signup_complete.html or ``on_success`` keyword argument.
212 f7e8a159 Sofia Papagiannaki
    """
213 f7e8a159 Sofia Papagiannaki
    try:
214 f7e8a159 Sofia Papagiannaki
        if not backend:
215 f7e8a159 Sofia Papagiannaki
            backend = get_backend(request)
216 f7e8a159 Sofia Papagiannaki
        if form.is_valid():
217 f7e8a159 Sofia Papagiannaki
            status, message, user = backend.signup(form)
218 f7e8a159 Sofia Papagiannaki
            if status == messages.SUCCESS:
219 f7e8a159 Sofia Papagiannaki
                for k,v in post_data.items():
220 f7e8a159 Sofia Papagiannaki
                    setattr(user,k, v)
221 f7e8a159 Sofia Papagiannaki
                user.save()
222 f7e8a159 Sofia Papagiannaki
                if user.is_active:
223 f7e8a159 Sofia Papagiannaki
                    return prepare_response(request, user, next=next)
224 f7e8a159 Sofia Papagiannaki
            messages.add_message(request, status, message)
225 f7e8a159 Sofia Papagiannaki
            return render_response(on_success,
226 f7e8a159 Sofia Papagiannaki
                                   context_instance=get_context(request, extra_context))
227 f7e8a159 Sofia Papagiannaki
        else:
228 f7e8a159 Sofia Papagiannaki
            messages.add_message(request, messages.ERROR, form.errors)
229 f7e8a159 Sofia Papagiannaki
    except (Invitation.DoesNotExist, ValueError), e:
230 f7e8a159 Sofia Papagiannaki
        messages.add_message(request, messages.ERROR, e)
231 92defad4 Sofia Papagiannaki
    for provider in IM_MODULES:
232 f7e8a159 Sofia Papagiannaki
        extra_context['%s_form' % provider] = backend.get_signup_form(provider)
233 f7e8a159 Sofia Papagiannaki
    return render_response(on_failure,
234 db7fecd9 Sofia Papagiannaki
                           form = LocalUserCreationForm(ip=request.META['REMOTE_ADDR']),
235 f7e8a159 Sofia Papagiannaki
                           context_instance=get_context(request, extra_context))