Merge lp:~sil/ubuntuone-client/no-unknown-login-error into lp:ubuntuone-client

Proposed by Stuart Langridge
Status: Merged
Approved by: dobey
Approved revision: 242
Merged at revision: not available
Proposed branch: lp:~sil/ubuntuone-client/no-unknown-login-error
Merge into: lp:ubuntuone-client
Diff against target: 106 lines
1 file modified
ubuntuone/oauthdesktop/auth.py (+52/-26)
To merge this branch: bzr merge lp:~sil/ubuntuone-client/no-unknown-login-error
Reviewer Review Type Date Requested Status
dobey (community) Approve
Tim Cole (community) Approve
Review via email: mp+12935@code.launchpad.net

Commit message

Remove pycurl from oauthdesktop/auth.py when fetching an oauth token.
Make urllib fail on an invalid SSL certificate.
Do not trap errors; instead, let them propagate up to the top level so we can see them.

To post a comment you must log in.
Revision history for this message
Stuart Langridge (sil) wrote :

Remove pycurl from oauthdesktop/auth.py when fetching an oauth token.
Make urllib fail on an invalid SSL certificate.
Do not trap errors; instead, let them propagate up to the top level so we can see them.

236. By Stuart Langridge

actually do not trap errors

237. By Stuart Langridge

missed half a sentence out

Revision history for this message
Tim Cole (tcole) wrote :

Hmm. Why are we doing the monkeypatching anew every time we auth, and would it be possible to chain to the old implementation in some way rather than overwriting?

review: Approve
Revision history for this message
Tim Cole (tcole) wrote :

Hmm. Why are we doing the monkeypatching anew every time we auth, and would it be possible to chain to the old implementation in some way rather than overwriting?

review: Needs Information
Revision history for this message
Stuart Langridge (sil) wrote :

> Hmm. Why are we doing the monkeypatching anew every time we auth, and would
> it be possible to chain to the old implementation in some way rather than
> overwriting?

I thought about chaining, but ssl.wrap_socket(sock, A, B, C, D) is not the same as ssl.wrap_socket(ssl.wrap_socket(sock, A, B), C, D), so essentially the whole thing needs replacing anyway.

Good point on not re-patching every time we auth, but I thought that having the code close in location to where it takes effect will make it clearer to someone debugging later on, since monkeypatching is invisible unless you know it's happening.

Revision history for this message
Tim Cole (tcole) wrote :

Hm. Ok, it still seems like doing it at import time would make more sense.

review: Approve
Revision history for this message
dobey (dobey) wrote :

./ubuntuone/oauthdesktop/auth.py:
    28: 'pycurl' imported but unused
    29: 'StringIO' imported but unused
    189: undefined name 'socket'
    193: undefined name 'ssl'
    194: undefined name 'ssl'
    196: undefined name 'httplib'
    204: undefined name 'urllib'
    210: undefined name 'data'
    215: undefined name 'data'

make: *** [lint] Error 9

review: Needs Fixing
238. By Stuart Langridge

Move monkeypatching to import time; subclass FancyURLopener to re-add data on redirected POSTs

Revision history for this message
Stuart Langridge (sil) wrote :

> make: *** [lint] Error 9

Bizarre, half the code wasn't committed. Please try again. Also fixed Tim's issue above.

Revision history for this message
dobey (dobey) wrote :

> > make: *** [lint] Error 9
>
> Bizarre, half the code wasn't committed. Please try again. Also fixed Tim's
> issue above.

Still get:

    28: 'StringIO' imported but unused

239. By Stuart Langridge

lint tidyup

Revision history for this message
dobey (dobey) wrote :

94 + opener = FancyURLOpenerWithRedirectedPOST()
95 + fp = opener.open(oauth_request.http_url, oauth_request.to_postdata())
96 + data = fp.read()

It would be great if this block of code could be in a try:/except: clause.

except IOError, e:
    if self.callback_error:
        callback_error(str(e))
    else:
        raise e

Revision history for this message
Stuart Langridge (sil) wrote :

> 94 + opener = FancyURLOpenerWithRedirectedPOST()
> 95 + fp = opener.open(oauth_request.http_url,
> oauth_request.to_postdata())
> 96 + data = fp.read()
>
> It would be great if this block of code could be in a try:/except: clause.
>
> except IOError, e:
> if self.callback_error:
> callback_error(str(e))
> else:
> raise e

Done.

240. By Stuart Langridge

Call callback_error if available

Revision history for this message
dobey (dobey) wrote :

> > 94 + opener = FancyURLOpenerWithRedirectedPOST()
> > 95 + fp = opener.open(oauth_request.http_url,
> > oauth_request.to_postdata())
> > 96 + data = fp.read()
> >
> > It would be great if this block of code could be in a try:/except: clause.
> >
> > except IOError, e:
> > if self.callback_error:
> > callback_error(str(e))
> > else:
> > raise e
>
> Done.

+ callback_error(str(e))

needs to be self.callback_error(str(e))

Sorry! I am dumb sometimes (particularly in the morninsg when not yet fully awake) :)

241. By Stuart Langridge

copy and paste fail

242. By Stuart Langridge

if we hit an error and we notify it upstream with callback_error, bail, don't fall through

Revision history for this message
dobey (dobey) wrote :

OFFICIAL SEAL OF ME.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone/oauthdesktop/auth.py'
2--- ubuntuone/oauthdesktop/auth.py 2009-09-15 15:50:21 +0000
3+++ ubuntuone/oauthdesktop/auth.py 2009-10-07 15:12:12 +0000
4@@ -25,10 +25,9 @@
5
6 import subprocess
7 import random
8-import pycurl
9-import StringIO
10 import dbus
11 import os
12+import socket, ssl, httplib, urllib
13
14 import gnomekeyring
15 from oauth import oauth
16@@ -60,6 +59,40 @@
17 NM_STATE_CONNECTED = 3
18 NM_STATE_DISCONNECTED = 4
19
20+# Monkeypatch httplib so that urllib will fail on invalid certificate
21+def _connect_wrapper(self):
22+ """Override HTTPSConnection.connect to require certificate checks"""
23+ sock = socket.create_connection((self.host, self.port), self.timeout)
24+ if self._tunnel_host:
25+ self.sock = sock
26+ self._tunnel()
27+ self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
28+ cert_reqs=ssl.CERT_REQUIRED,
29+ ca_certs="/etc/ssl/certs/ca-certificates.crt")
30+httplib.HTTPSConnection.connect = _connect_wrapper
31+
32+class FancyURLOpenerWithRedirectedPOST(urllib.FancyURLopener):
33+ """FancyURLopener does not redirect postdata when redirecting POSTs"""
34+ def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
35+ """Actually perform a redirect"""
36+ # All the same as the original, except passing data, below
37+ if 'location' in headers:
38+ newurl = headers['location']
39+ elif 'uri' in headers:
40+ newurl = headers['uri']
41+ else:
42+ return
43+ void = fp.read()
44+ fp.close()
45+ # In case the server sent a relative URL, join with original:
46+ newurl = urllib.basejoin(self.type + ":" + url, newurl)
47+
48+ # pass data if present when we redirect
49+ if data:
50+ return self.open(newurl, data)
51+ else:
52+ return self.open(newurl)
53+
54 class AuthorisationClient(object):
55 """OAuth authorisation client."""
56 def __init__(self, realm, request_token_url, user_authorisation_url,
57@@ -183,32 +216,25 @@
58
59 def make_token_request(self, oauth_request):
60 """Perform the given `OAuthRequest` and return the associated token."""
61- # uses pycurl because that will fail on self-signed certs
62-
63+
64 logger.debug("Making a token request")
65- accum = StringIO.StringIO()
66- c = pycurl.Curl()
67- c.setopt(c.URL, str(oauth_request.http_url)) # no unicode
68- c.setopt(c.WRITEFUNCTION, accum.write)
69- c.setopt(c.POSTFIELDS, oauth_request.to_postdata())
70+ # Note that we monkeypatched httplib above to handle invalid certs
71+ # Ways this urlopen can fail:
72+ # bad certificate
73+ # raises IOError, e.args[1] == SSLError, e.args[1].errno == 1
74+ # No such server
75+ # raises IOError, e.args[1] == SSLError, e.args[1].errno == -2
76 try:
77- c.perform()
78- code = c.getinfo(pycurl.HTTP_CODE)
79- if code == 301:
80- # handle permanent redirect
81- url = c.getinfo(pycurl.REDIRECT_URL)
82- logger.debug('Redirected to: %s', str(url))
83- c.setopt(c.URL, str(url)) # no unicode
84- # reset the StringIO
85- accum.truncate(0)
86- # and retry
87- c.perform()
88- except pycurl.error, e:
89- logger.debug("There was some unknown login error '%s'", e)
90- raise UnknownLoginError(e.message)
91- c.close()
92- accum.seek(0)
93- data = accum.read()
94+ opener = FancyURLOpenerWithRedirectedPOST()
95+ fp = opener.open(oauth_request.http_url, oauth_request.to_postdata())
96+ data = fp.read()
97+ except IOError, e:
98+ if self.callback_error:
99+ self.callback_error(str(e))
100+ return
101+ else:
102+ raise e
103+
104 # we deliberately trap anything that might go wrong when parsing the
105 # token, because we do not want this to explicitly fail
106 # pylint: disable-msg=W0702

Subscribers

People subscribed via source and target branches