Merge lp:~mandel/ubuntu-sso-client/keyring-integration into lp:ubuntu-sso-client

Proposed by Manuel de la Peña
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 872
Merged at revision: 866
Proposed branch: lp:~mandel/ubuntu-sso-client/keyring-integration
Merge into: lp:ubuntu-sso-client
Prerequisite: lp:~mandel/ubuntu-sso-client/creds-dialog
Diff against target: 233 lines (+123/-11)
2 files modified
ubuntu_sso/qt/proxy_dialog.py (+39/-4)
ubuntu_sso/qt/tests/test_proxy_dialog.py (+84/-7)
To merge this branch: bzr merge lp:~mandel/ubuntu-sso-client/keyring-integration
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Alejandro J. Cura (community) Approve
Review via email: mp+92532@code.launchpad.net

Commit message

Allows the creds dialog to store the credentials in the keyring.

Description of the change

Allows the creds dialog to store the credentials in the keyring.

To post a comment you must log in.
866. By Manuel de la Peña

Merged creds-dialog into keyring-integration.

867. By Manuel de la Peña

Merged creds-dialog into keyring-integration.

868. By Manuel de la Peña

Merged creds-dialog into keyring-integration.

869. By Manuel de la Peña

Typos...

Revision history for this message
Alejandro J. Cura (alecu) wrote :

Looks just fine.

review: Approve
870. By Manuel de la Peña

Use unicode.

Revision history for this message
Roberto Alsina (ralsina) wrote :

There is a problem in that using str(widget.text()) will cause a unicode exception if the user types a unicode character.

OTOH, the keyring uses urllib.urlencode on the credentials, which means we can't pass unicode to it:

>>> urllib.urlencode({u'á':1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/urllib.py", line 1310, in urlencode
    k = quote_plus(str(k))
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 0: ordinal not in range(128)

Also, it seems we are doing the same thing in other places. So, maybe this branch should land and a new bug be opened for that issue.

If that's the chosen path, feel free to land it with one review, since everything else looks ok.

871. By Manuel de la Peña

Ensure that we do pass bytes to the keyring.

872. By Manuel de la Peña

Merged with parent and fixed issues.

Revision history for this message
Roberto Alsina (ralsina) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/qt/proxy_dialog.py'
2--- ubuntu_sso/qt/proxy_dialog.py 2012-02-13 16:19:18 +0000
3+++ ubuntu_sso/qt/proxy_dialog.py 2012-02-13 16:19:18 +0000
4@@ -17,7 +17,10 @@
5
6
7 from PyQt4.QtGui import QDialog, QIcon
8+from twisted.internet import defer
9
10+from ubuntu_sso.logger import setup_logging
11+from ubuntu_sso.keyring import Keyring
12 from ubuntu_sso.qt.ui.proxy_credentials_dialog_ui import Ui_ProxyCredsDialog
13 from ubuntu_sso.utils.ui import (
14 PROXY_CREDS_DIALOG_TITLE,
15@@ -32,6 +35,12 @@
16 PROXY_CREDS_SAVE_BUTTON,
17 )
18
19+CREDS_ACQUIRED = 0
20+USER_CANCELATION = -1
21+EXCEPTION_RAISED = -2
22+
23+logger = setup_logging("ubuntu_sso.qt.proxy_dialog")
24+
25
26 class ProxyCredsDialog(QDialog):
27 """"Dialog used to require the proxy credentials."""
28@@ -39,6 +48,12 @@
29 def __init__(self, is_error=False, domain=None):
30 """Create a new instance."""
31 QDialog.__init__(self)
32+
33+ if domain is None:
34+ logger.debug('Domain passed as None.')
35+ domain = ''
36+ self.domain = domain
37+ self.keyring = Keyring()
38 self.ui = Ui_ProxyCredsDialog()
39 self.ui.setupUi(self)
40 # lets set the different basic contents for the ui
41@@ -49,10 +64,6 @@
42 self.ui.error_label.setVisible(True)
43 else:
44 self.ui.error_label.setVisible(False)
45- if domain is None:
46- self.ui.domain_label.setText('')
47- else:
48- self.ui.domain_label.setText(domain)
49
50 def _set_labels(self):
51 """Set the labels translations."""
52@@ -65,12 +76,36 @@
53 % PROXY_CREDS_ERROR)
54 self.ui.username_label.setText(PROXY_CREDS_USER_LABEL)
55 self.ui.password_label.setText(PROXY_CREDS_PSWD_LABEL)
56+ self.ui.domain_label.setText(self.domain)
57+
58+ @defer.inlineCallbacks
59+ def _on_save_clicked(self, *args):
60+ """Save the new credentials."""
61+ username = unicode(self.ui.username_entry.text()).encode('utf8')
62+ password = unicode(self.ui.password_entry.text()).encode('utf8')
63+ creds = dict(username=username, password=password)
64+ # pylint: disable=W0703, W0612
65+ try:
66+ logger.debug('Save credentials as for domain %s.', self.domain)
67+ yield self.keyring.set_credentials(self.domain, creds)
68+ except Exception, e:
69+ logger.error('Could not retrieve credentials.')
70+ self.done(EXCEPTION_RAISED)
71+ # pylint: disable=W0703, W0612
72+ self.done(CREDS_ACQUIRED)
73+
74+ def _on_cancel_clicked(self, *args):
75+ """End the dialog."""
76+ logger.debug('User canceled credentials dialog.')
77+ self.done(USER_CANCELATION)
78
79 def _set_buttons(self):
80 """Set the labels of the buttons."""
81 self.ui.help_button.setText(PROXY_CREDS_HELP_BUTTON)
82 self.ui.cancel_button.setText(PROXY_CREDS_CANCEL_BUTTON)
83+ self.ui.cancel_button.clicked.connect(self._on_cancel_clicked)
84 self.ui.save_button.setText(PROXY_CREDS_SAVE_BUTTON)
85+ self.ui.save_button.clicked.connect(self._on_save_clicked)
86
87 def _set_icon(self):
88 """Set the icon used in the dialog."""
89
90=== modified file 'ubuntu_sso/qt/tests/test_proxy_dialog.py'
91--- ubuntu_sso/qt/tests/test_proxy_dialog.py 2012-02-13 16:19:18 +0000
92+++ ubuntu_sso/qt/tests/test_proxy_dialog.py 2012-02-13 16:19:18 +0000
93@@ -20,25 +20,48 @@
94
95 from ubuntu_sso.qt import proxy_dialog
96
97-# pylint: disable=E1101,
98-
99-
100-class FakeTextWidget(object):
101+# pylint: disable=E1101,W0212,
102+
103+
104+class FakeSignal(object):
105+ """A fake qt signal."""
106+
107+ def __init__(self):
108+ """Create a new instance."""
109+ self.called = []
110+
111+ def connect(self, cb):
112+ """Connect signals."""
113+ self.called.append(('connect', cb))
114+
115+
116+class FakeWidget(object):
117 """A fake widget that contains text."""
118
119- def __init__(self, object_name, called):
120+ def __init__(self, object_name, called, text=''):
121 """Create a new instance."""
122 self.object_name = object_name
123 self.called = called
124+ self.internal_text = text
125+ self.clicked = FakeSignal()
126
127 # pylint: disable=C0103
128 def setText(self, text):
129 """Set the text of the widget."""
130+ self.internal_text = text
131 if not self.object_name in self.called:
132 self.called[self.object_name] = [('setText', text)]
133 else:
134 self.called[self.object_name].append(('setText', text))
135
136+ def text(self):
137+ """Return the text."""
138+ if not self.object_name in self.called:
139+ self.called[self.object_name] = [('text',)]
140+ else:
141+ self.called[self.object_name].append(('text',))
142+ return self.internal_text
143+
144 def setPixmap(self, pixmap):
145 """Set a pixmap."""
146 if not self.object_name in self.called:
147@@ -52,6 +75,7 @@
148 self.called[self.object_name] = [('setVisible', visible)]
149 else:
150 self.called[self.object_name].append(('setVisible', visible))
151+
152 # pylint: enable=C0103
153
154
155@@ -62,12 +86,13 @@
156 'error_label', 'username_label', 'password_label',
157 'domain_label', 'logo_label')
158 ui_buttons = ('help_button', 'cancel_button', 'save_button')
159+ ui_entries = ('username_entry', 'password_entry')
160
161 def __init__(self):
162 """Create a new instance."""
163 self.called = {}
164- for name in self.ui_labels + self.ui_buttons:
165- setattr(self, name, FakeTextWidget(name, self.called))
166+ for name in self.ui_labels + self.ui_buttons + self.ui_entries:
167+ setattr(self, name, FakeWidget(name, self.called))
168
169 # pylint: disable=C0103
170 def setupUi(self, dialog):
171@@ -121,6 +146,11 @@
172 dialog = proxy_dialog.ProxyCredsDialog()
173 for label in dialog.ui.ui_buttons:
174 self.assertIn(label, dialog.ui.called)
175+ # assert that the clicked was set
176+ self.assertIn(('connect', dialog._on_save_clicked),
177+ dialog.ui.save_button.clicked.called)
178+ self.assertIn(('connect', dialog._on_cancel_clicked),
179+ dialog.ui.cancel_button.clicked.called)
180
181 def test_domain_empty(self):
182 """Test that if the domain is not passed we set ''"""
183@@ -156,3 +186,50 @@
184 self.icon.called)
185 self.assertIn(('setPixmap', self.icon),
186 dialog.ui.called['logo_label'])
187+
188+ def test_on_cancel_clicked(self):
189+ """Test the on cancel clicked event."""
190+ dialog = proxy_dialog.ProxyCredsDialog()
191+ called = []
192+
193+ def fake_done(result):
194+ """Fake the done method."""
195+ called.append(('done', result))
196+
197+ self.patch(dialog, 'done', fake_done)
198+
199+ dialog._on_cancel_clicked()
200+ self.assertIn(('done', proxy_dialog.USER_CANCELATION), called)
201+
202+ def assert_save_button(self, set_creds_callback, result_number):
203+ """Test the save button execution."""
204+ dialog = proxy_dialog.ProxyCredsDialog()
205+ called = []
206+
207+ def fake_done(result):
208+ """Fake the done method."""
209+ called.append(('done', result))
210+
211+ self.patch(dialog, 'done', fake_done)
212+
213+ def fake_set_credentials(app_name, creds):
214+ """Fake an error."""
215+ called.append(('set_credentials', app_name, creds))
216+ return set_creds_callback()
217+
218+ self.patch(dialog.keyring, 'set_credentials', fake_set_credentials)
219+
220+ dialog._on_save_clicked()
221+ self.assertIn(('done', result_number), called)
222+
223+ def test_on_save_clicked_exception(self):
224+ """Test that the exception is handled correctly."""
225+ set_creds_cb = lambda: defer.fail(Exception())
226+ result_number = proxy_dialog.EXCEPTION_RAISED
227+ self.assert_save_button(set_creds_cb, result_number)
228+
229+ def test_on_save_clicked_correct(self):
230+ """Test that we do save the creds."""
231+ set_creds_cb = lambda: defer.succeed(True)
232+ result_number = proxy_dialog.CREDS_ACQUIRED
233+ self.assert_save_button(set_creds_cb, result_number)

Subscribers

People subscribed via source and target branches