Merge lp:~mbp/bzr/654684-password into lp:bzr

Proposed by Martin Pool
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: no longer in the source branch.
Merged at revision: 5487
Proposed branch: lp:~mbp/bzr/654684-password
Merge into: lp:bzr
Diff against target: 141 lines (+63/-26)
3 files modified
NEWS (+4/-0)
bzrlib/tests/test_http.py (+55/-24)
bzrlib/transport/http/_urllib2_wrappers.py (+4/-2)
To merge this branch: bzr merge lp:~mbp/bzr/654684-password
Reviewer Review Type Date Requested Status
Vincent Ladeuil Approve
Review via email: mp+38185@code.launchpad.net

Commit message

avoid "KeyError: port" in urllib authentication callback

Description of the change

This is a somewhat shallow fix for bug 654684, but it does add a narrow unit test.

It seems that sometimes urllib2 doesn't give us back a 'port' entry in the authentication callback: I would guess this is when there is no port in the url. This just copes with that and adds a test that we do.

Possibly this indicates a missing test at a different level that could provoke urllib2 to give this behaviour. It might be hard to write that test because we can't listen on port 80 in a test to simulate the situation. (Maybe someone can think of a creative way to hit it.)

To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote :

I'm slightly worried that there could other occurrences of the same problem, but since port=None is handled in the rest of the stack, I see no reason to be pedantic either.

Nice unit tests by the way.

review: Approve
Revision history for this message
Martin Pool (mbp) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-10-12 06:18:54 +0000
3+++ NEWS 2010-10-12 07:29:40 +0000
4@@ -28,6 +28,10 @@
5 * Don't force openssh to use protocol=2, since that is now the default.
6 (Neil Martinsen-Burrell, #561061)
7
8+* Fix ``KeyError: 'port'`` when getting the stored password for an http
9+ URL.
10+ (Martin Pool, #654684)
11+
12 * Make ``bzr tag --quiet`` really quiet. (Neil Martinsen-Burrell, #239523)
13
14 Improvements
15
16=== modified file 'bzrlib/tests/test_http.py'
17--- bzrlib/tests/test_http.py 2010-10-11 22:40:23 +0000
18+++ bzrlib/tests/test_http.py 2010-10-12 07:29:40 +0000
19@@ -1604,11 +1604,11 @@
20 ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
21 stderr=tests.StringIOWrapper())
22 # Create a minimal config file with the right password
23- conf = config.AuthenticationConfig()
24- conf._get_config().update(
25- {'httptest': {'scheme': 'http', 'port': self.server.port,
26- 'user': user, 'password': password}})
27- conf._save()
28+ _setup_authentication_config(
29+ scheme='http',
30+ port=self.server.port,
31+ user=user,
32+ password=password)
33 # Issue a request to the server to connect
34 self.assertEqual('contents of a\n',t.get('a').read())
35 # stdin should have been left untouched
36@@ -1616,25 +1616,6 @@
37 # Only one 'Authentication Required' error should occur
38 self.assertEqual(1, self.server.auth_required_errors)
39
40- def test_user_from_auth_conf(self):
41- if self._testing_pycurl():
42- raise tests.TestNotApplicable(
43- 'pycurl does not support authentication.conf')
44- user = 'joe'
45- password = 'foo'
46- self.server.add_user(user, password)
47- # Create a minimal config file with the right password
48- conf = config.AuthenticationConfig()
49- conf._get_config().update(
50- {'httptest': {'scheme': 'http', 'port': self.server.port,
51- 'user': user, 'password': password}})
52- conf._save()
53- t = self.get_user_transport(None, None)
54- # Issue a request to the server to connect
55- self.assertEqual('contents of a\n', t.get('a').read())
56- # Only one 'Authentication Required' error should occur
57- self.assertEqual(1, self.server.auth_required_errors)
58-
59 def test_changing_nonce(self):
60 if self._auth_server not in (http_utils.HTTPDigestAuthServer,
61 http_utils.ProxyDigestAuthServer):
62@@ -1656,6 +1637,56 @@
63 # initial 'who are you' and a second 'who are you' with the new nonce)
64 self.assertEqual(2, self.server.auth_required_errors)
65
66+ def test_user_from_auth_conf(self):
67+ if self._testing_pycurl():
68+ raise tests.TestNotApplicable(
69+ 'pycurl does not support authentication.conf')
70+ user = 'joe'
71+ password = 'foo'
72+ self.server.add_user(user, password)
73+ _setup_authentication_config(
74+ scheme='http',
75+ port=self.server.port,
76+ user=user,
77+ password=password)
78+ t = self.get_user_transport(None, None)
79+ # Issue a request to the server to connect
80+ self.assertEqual('contents of a\n', t.get('a').read())
81+ # Only one 'Authentication Required' error should occur
82+ self.assertEqual(1, self.server.auth_required_errors)
83+
84+
85+def _setup_authentication_config(**kwargs):
86+ conf = config.AuthenticationConfig()
87+ conf._get_config().update({'httptest': kwargs})
88+ conf._save()
89+
90+
91+
92+class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
93+ """Unit tests for glue by which urllib2 asks us for authentication"""
94+
95+ def test_get_user_password_without_port(self):
96+ """We cope if urllib2 doesn't tell us the port.
97+
98+ See https://bugs.launchpad.net/bzr/+bug/654684
99+ """
100+ user = 'joe'
101+ password = 'foo'
102+ _setup_authentication_config(
103+ scheme='http',
104+ host='localhost',
105+ user=user,
106+ password=password)
107+ handler = _urllib2_wrappers.HTTPAuthHandler()
108+ got_pass = handler.get_user_password(dict(
109+ user='joe',
110+ protocol='http',
111+ host='localhost',
112+ path='/',
113+ realm='Realm',
114+ ))
115+ self.assertEquals((user, password), got_pass)
116
117
118 class TestProxyAuth(TestAuth):
119
120=== modified file 'bzrlib/transport/http/_urllib2_wrappers.py'
121--- bzrlib/transport/http/_urllib2_wrappers.py 2010-10-08 10:50:51 +0000
122+++ bzrlib/transport/http/_urllib2_wrappers.py 2010-10-12 07:29:40 +0000
123@@ -1202,15 +1202,17 @@
124 user = auth.get('user', None)
125 password = auth.get('password', None)
126 realm = auth['realm']
127+ port = auth.get('port', None)
128
129 if user is None:
130 user = auth_conf.get_user(auth['protocol'], auth['host'],
131- port=auth['port'], path=auth['path'],
132+ port=port, path=auth['path'],
133 realm=realm, ask=True,
134 prompt=self.build_username_prompt(auth))
135 if user is not None and password is None:
136 password = auth_conf.get_password(
137- auth['protocol'], auth['host'], user, port=auth['port'],
138+ auth['protocol'], auth['host'], user,
139+ port=port,
140 path=auth['path'], realm=realm,
141 prompt=self.build_password_prompt(auth))
142