Merge lp:~mikemc/ubuntuone-control-panel/fix-1248326 into lp:ubuntuone-control-panel

Proposed by Mike McCracken on 2013-11-27
Status: Merged
Approved by: Mike McCracken on 2013-11-28
Approved revision: 415
Merged at revision: 409
Proposed branch: lp:~mikemc/ubuntuone-control-panel/fix-1248326
Merge into: lp:ubuntuone-control-panel
Diff against target: 343 lines (+123/-13)
9 files modified
ubuntuone/controlpanel/gui/qt/account.py (+13/-2)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+21/-4)
ubuntuone/controlpanel/gui/qt/device.py (+9/-1)
ubuntuone/controlpanel/gui/qt/devices.py (+10/-2)
ubuntuone/controlpanel/gui/qt/folders.py (+12/-4)
ubuntuone/controlpanel/gui/qt/tests/test_account.py (+16/-0)
ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+8/-0)
ubuntuone/controlpanel/gui/qt/tests/test_devices.py (+18/-0)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+16/-0)
To merge this branch: bzr merge lp:~mikemc/ubuntuone-control-panel/fix-1248326
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve on 2013-11-28
dobey (community) 2013-11-27 Approve on 2013-11-27
Review via email: mp+196800@code.launchpad.net

Commit message

- Prompt user to re-login with SSO if UnauthorizedError is raised by web client. (LP: #1248326)

Description of the change

- Prompt user to re-login with SSO if UnauthorizedError is raised by web client. (LP: #1248326)

To test: log in and then invalidate the token using the web site.
Then trigger a web call by changing to either the devices or account tabs.
The SSO login window should pop up immediately.
Previously, the uncaught exception traceback was just shown to the user.

To post a comment you must log in.
dobey (dobey) wrote :

No tests?

review: Needs Fixing
dobey (dobey) :
review: Approve
Roberto Alsina (ralsina) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone/controlpanel/gui/qt/account.py'
2--- ubuntuone/controlpanel/gui/qt/account.py 2012-02-22 15:35:13 +0000
3+++ ubuntuone/controlpanel/gui/qt/account.py 2013-11-27 19:55:46 +0000
4@@ -19,6 +19,8 @@
5
6 """The user interface for the control panel for Ubuntu One."""
7
8+from PyQt4 import QtCore
9+
10 from twisted.internet import defer
11
12 from ubuntuone.controlpanel.logger import setup_logging, log_call
13@@ -33,6 +35,7 @@
14 from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
15 from ubuntuone.controlpanel.gui.qt.ui import account_ui
16
17+from ubuntu_sso.utils.webclient.common import UnauthorizedError
18
19 logger = setup_logging('qt.account')
20
21@@ -43,6 +46,8 @@
22 ui_class = account_ui
23 logger = logger
24
25+ credentialsCleared = QtCore.pyqtSignal()
26+
27 def _setup(self):
28 """Do some extra setupping for the UI."""
29 super(AccountPanel, self)._setup()
30@@ -58,8 +63,14 @@
31 def load(self):
32 """Load info."""
33 self.is_processing = True
34- info = yield self.backend.account_info()
35- self.process_info(info)
36+ try:
37+ info = yield self.backend.account_info()
38+ except UnauthorizedError:
39+ self.is_processing = False
40+ logger.debug("handling UnauthorizedError from account_info()")
41+ self.credentialsCleared.emit()
42+ else:
43+ self.process_info(info)
44
45 @log_call(logger.debug)
46 def process_info(self, info):
47
48=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
49--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2013-03-11 18:46:14 +0000
50+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2013-11-27 19:55:46 +0000
51@@ -46,6 +46,7 @@
52 from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
53 from ubuntuone.controlpanel.gui.qt.ui import controlpanel_ui
54
55+from ubuntu_sso.utils.webclient.common import UnauthorizedError
56
57 logger = setup_logging('qt.controlpanel')
58
59@@ -96,6 +97,10 @@
60 # account info
61 self.ui.tab_widget.show_overlay = False
62
63+ for tab in [self.ui.account_tab, self.ui.folders_tab,
64+ self.ui.devices_tab]:
65+ tab.credentialsCleared.connect(self.on_credentials_cleared)
66+
67 @defer.inlineCallbacks
68 def connect_file_sync(self):
69 """Connect file sync service if the setting autoconnect is enabled."""
70@@ -122,14 +127,26 @@
71 self.connect_file_sync()
72 yield self.show_management_ui()
73
74+ @log_call(logger.debug)
75+ @defer.inlineCallbacks
76+ def on_credentials_cleared(self):
77+ """Prompt user to log back in after credentials have been deleted."""
78+ yield self.backend.login()
79+ # ignore returned credentials from login(), load() re-gets them.
80+ yield self.load()
81+
82 @defer.inlineCallbacks
83 def show_management_ui(self):
84 """Show mgmt UI. File sync is already connected if desired."""
85 self.ui.switcher.setCurrentWidget(self.ui.management)
86- info = yield self.backend.account_info()
87- self.process_info(info)
88- self.is_processing = False
89- self.ui.tab_widget.show_overlay = True
90+ try:
91+ info = yield self.backend.account_info()
92+ except UnauthorizedError:
93+ self.on_credentials_cleared()
94+ else:
95+ self.process_info(info)
96+ self.is_processing = False
97+ self.ui.tab_widget.show_overlay = True
98
99 # pylint: disable=E0202
100 @defer.inlineCallbacks
101
102=== modified file 'ubuntuone/controlpanel/gui/qt/device.py'
103--- ubuntuone/controlpanel/gui/qt/device.py 2012-03-08 20:46:13 +0000
104+++ ubuntuone/controlpanel/gui/qt/device.py 2013-11-27 19:55:46 +0000
105@@ -37,6 +37,8 @@
106 from ubuntuone.controlpanel.gui.qt.ui import device_ui
107 from ubuntuone.controlpanel.logger import setup_logging
108
109+from ubuntu_sso.utils.webclient.common import UnauthorizedError
110+
111 COMPUTER_ICON = "computer"
112 PHONE_ICON = "phone"
113 DEFAULT_ICON = COMPUTER_ICON
114@@ -105,7 +107,13 @@
115 response = QtGui.QMessageBox.warning(self, '', msg, buttons, NO)
116
117 if response == YES:
118- yield self.backend.remove_device(device_id=self.id)
119+ try:
120+ yield self.backend.remove_device(device_id=self.id)
121+ except UnauthorizedError:
122+ # Ignore UnauthorizedError because it means our
123+ # local device was invalidated remotely, which we can
124+ # handle the same way
125+ pass
126 self.removed.emit()
127 else:
128 self.removeCanceled.emit()
129
130=== modified file 'ubuntuone/controlpanel/gui/qt/devices.py'
131--- ubuntuone/controlpanel/gui/qt/devices.py 2012-03-08 20:46:13 +0000
132+++ ubuntuone/controlpanel/gui/qt/devices.py 2013-11-27 19:55:46 +0000
133@@ -35,6 +35,7 @@
134 from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
135 from ubuntuone.controlpanel.gui.qt import device
136
137+from ubuntu_sso.utils.webclient.common import UnauthorizedError
138
139 logger = setup_logging('qt.devices')
140
141@@ -45,6 +46,7 @@
142 ui_class = devices_ui
143 logger = logger
144
145+ credentialsCleared = QtCore.pyqtSignal()
146 localDeviceRemoved = QtCore.pyqtSignal()
147
148 def _setup(self):
149@@ -60,8 +62,14 @@
150 def load(self):
151 """Load info."""
152 self.is_processing = True
153- info = yield self.backend.device_names_info()
154- self.process_info(info)
155+ try:
156+ info = yield self.backend.device_names_info()
157+ except UnauthorizedError:
158+ self.is_processing = False
159+ logger.debug("handling UnauthorizedError from device_names_info()")
160+ self.credentialsCleared.emit()
161+ else:
162+ self.process_info(info)
163
164 @log_call(logger.debug)
165 def process_info(self, info):
166
167=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
168--- ubuntuone/controlpanel/gui/qt/folders.py 2012-11-29 20:15:08 +0000
169+++ ubuntuone/controlpanel/gui/qt/folders.py 2013-11-27 19:55:46 +0000
170@@ -63,6 +63,7 @@
171 from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
172 from ubuntuone.controlpanel.gui.qt.ui import folders_ui, local_folders_ui
173
174+from ubuntu_sso.utils.webclient.common import UnauthorizedError
175
176 logger = setup_logging('qt.folders')
177
178@@ -118,6 +119,8 @@
179 ui_class = folders_ui
180 widget_items = {}
181
182+ credentialsCleared = QtCore.pyqtSignal()
183+
184 def _setup(self):
185 """Do some extra setupping for the UI."""
186 super(FoldersPanel, self)._setup()
187@@ -157,7 +160,6 @@
188 @log_call(logger.info)
189 def on_folder_created(self, new_folder):
190 """Reload folder info after folder creation."""
191- self.is_processing = True
192 self.load()
193
194 # pylint: disable=E0202
195@@ -165,9 +167,15 @@
196 def load(self):
197 """Load specific tab info."""
198 self.is_processing = True
199- info = yield self.backend.volumes_info(with_storage_info=False,
200- refresh=self.remote_folders)
201- self.process_info(info)
202+ try:
203+ info = yield self.backend.volumes_info(with_storage_info=False,
204+ refresh=self.remote_folders)
205+ except UnauthorizedError:
206+ self.is_processing = False
207+ logger.debug("handling UnauthorizedError from volumes_info")
208+ self.credentialsCleared.emit()
209+ else:
210+ self.process_info(info)
211
212 @handle_errors(logger=logger)
213 @log_call(logger.debug)
214
215=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_account.py'
216--- ubuntuone/controlpanel/gui/qt/tests/test_account.py 2012-03-12 16:58:16 +0000
217+++ ubuntuone/controlpanel/gui/qt/tests/test_account.py 2013-11-27 19:55:46 +0000
218@@ -27,6 +27,9 @@
219 from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
220 UbuntuOneBinTestCase,
221 )
222+from ubuntuone.controlpanel.gui.tests import FakeSignal
223+
224+from ubuntu_sso.utils.webclient.common import UnauthorizedError
225
226
227 class AccountPanelTestCase(UbuntuOneBinTestCase):
228@@ -59,6 +62,19 @@
229 yield self.ui.load()
230 self.assert_backend_called('account_info')
231
232+ @defer.inlineCallbacks
233+ def test_load_signals_on_unauthorized(self):
234+ """Emit credentialsCleared when UnauthorizedError is raised"""
235+ def account_info_error(*args, **kwargs):
236+ raise UnauthorizedError()
237+ fake_cleared_signal = FakeSignal()
238+ fake_cleared_signal.connect(self._set_called)
239+ self.patch(self.ui.backend, 'account_info', account_info_error)
240+ self.patch(self.ui, 'credentialsCleared', fake_cleared_signal)
241+
242+ yield self.ui.load()
243+ self.assertEqual(self._called, ((), {}))
244+
245 def test_process_info(self):
246 """The info is processed when ready."""
247 self.ui.process_info(SAMPLE_ACCOUNT_INFO)
248
249=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py'
250--- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2013-03-12 19:25:09 +0000
251+++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2013-11-27 19:55:46 +0000
252@@ -107,6 +107,14 @@
253 self.assertEqual(self._called, ((), {}))
254
255 @defer.inlineCallbacks
256+ def test_on_credentials_cleared(self):
257+ """Call backend.login and self.load."""
258+ self.patch(self.ui, 'load', self._set_called)
259+ yield self.ui.on_credentials_cleared()
260+ self.assert_backend_called('login')
261+ self.assertEqual(self._called, ((), {}))
262+
263+ @defer.inlineCallbacks
264 def test_info_is_requested_on_load(self):
265 """The info is requested to the backend."""
266 self.patch(self.ui, 'process_info', self._set_called)
267
268=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_devices.py'
269--- ubuntuone/controlpanel/gui/qt/tests/test_devices.py 2012-03-12 16:53:02 +0000
270+++ ubuntuone/controlpanel/gui/qt/tests/test_devices.py 2013-11-27 19:55:46 +0000
271@@ -26,6 +26,10 @@
272 UbuntuOneBinTestCase,
273 )
274
275+from ubuntuone.controlpanel.gui.tests import FakeSignal
276+
277+from ubuntu_sso.utils.webclient.common import UnauthorizedError
278+
279
280 class DevicesPanelTestCase(UbuntuOneBinTestCase):
281 """Test the qt control panel."""
282@@ -58,6 +62,20 @@
283 yield self.ui.load()
284 self.assert_backend_called('device_names_info')
285
286+ @defer.inlineCallbacks
287+ def test_load_signals_on_unauthorized(self):
288+ """Emit credentialsCleared when UnauthorizedError is raised"""
289+ def device_names_info_error(*args, **kwargs):
290+ raise UnauthorizedError()
291+ fake_cleared_signal = FakeSignal()
292+ fake_cleared_signal.connect(self._set_called)
293+ self.patch(self.ui.backend, 'device_names_info',
294+ device_names_info_error)
295+ self.patch(self.ui, 'credentialsCleared', fake_cleared_signal)
296+
297+ yield self.ui.load()
298+ self.assertEqual(self._called, ((), {}))
299+
300 def test_no_devices_at_startup(self):
301 """The UI is reset at startup."""
302 self.assertEqual(self.ui.ui.list_devices.count(), 0)
303
304=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
305--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-11-30 17:55:58 +0000
306+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2013-11-27 19:55:46 +0000
307@@ -31,6 +31,7 @@
308
309 from ubuntuone.controlpanel.tests import helper_fail
310 from ubuntuone.controlpanel.gui.tests import (
311+ FakeSignal,
312 FAKE_VOLUMES_INFO,
313 FAKE_VOLUMES_MINIMAL_INFO,
314 FAKE_VOLUMES_NO_FREE_SPACE_INFO,
315@@ -49,6 +50,8 @@
316 UbuntuOneBinTestCase,
317 )
318
319+from ubuntu_sso.utils.webclient.common import UnauthorizedError
320+
321 # Access to a protected member
322 # Instance of 'ControlBackend' has no '_called' member
323 # pylint: disable=W0212, E1103
324@@ -730,6 +733,19 @@
325 parent = super(RemoteFoldersPanelTestCase, self)
326 parent.test_process_info_with_music_folder(volumes=volumes)
327
328+ @defer.inlineCallbacks
329+ def test_load_signals_on_unauthorized(self):
330+ """Emit credentialsCleared when UnauthorizedError is raised"""
331+ def volumes_info_error(*args, **kwargs):
332+ raise UnauthorizedError()
333+ fake_cleared_signal = FakeSignal()
334+ fake_cleared_signal.connect(self._set_called)
335+ self.patch(self.ui.backend, 'volumes_info', volumes_info_error)
336+ self.patch(self.ui, 'credentialsCleared', fake_cleared_signal)
337+
338+ yield self.ui.load()
339+ self.assertEqual(self._called, ((), {}))
340+
341 def test_share_publish_button(self):
342 """When clicking the share/publish button, the proper url is opened."""
343 self.assertFalse(self.ui.ui.share_publish_button.isVisible())

Subscribers

People subscribed via source and target branches