Merge lp:~alecu/ubuntu-sso-client/keep-old-credentials into lp:ubuntu-sso-client

Proposed by Alejandro J. Cura
Status: Merged
Approved by: Natalia Bidart
Approved revision: 577
Merged at revision: 576
Proposed branch: lp:~alecu/ubuntu-sso-client/keep-old-credentials
Merge into: lp:ubuntu-sso-client
Diff against target: 224 lines (+156/-27)
2 files modified
ubuntu_sso/keyring.py (+41/-27)
ubuntu_sso/tests/test_keyring.py (+115/-0)
To merge this branch: bzr merge lp:~alecu/ubuntu-sso-client/keep-old-credentials
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Rodrigo Moya (community) Approve
Review via email: mp+32929@code.launchpad.net

Commit message

Use a proper dictionary with different attributes for each key so old keys are not overwritten nor deleted.

Description of the change

The gnome keyring needs to be passed a dictionary containing attributes to single out each key in a given keyring. An empty dictionary was being used instead, which lead to old keys being deleted.

To solve this issue we are using a proper dictionary with different attributes for each key.

To post a comment you must log in.
Revision history for this message
Rodrigo Moya (rodrigo-moya) :
review: Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

I think we should starting adding some test cases to this module. From a talk with alecu on IRC:

(04:21:22 PM) alecu: nessita, should I add info on how to run it to the merge proposal?
(04:22:03 PM) nessita: alecu: instructions would be nice
(04:22:28 PM) nessita: alecu: but also some unit tests. Could you please explain why is not possible to add unittests?
(04:24:09 PM) alecu: nessita, I can add unit tests, sure. But I can't find a way to add a unit test that checks if this problem is fixed, because checking it requires accessing the gnomekeyring
(04:25:13 PM) nessita: alecu: maybe I don't fully understand this problem :-), so, could you please explain?
(04:34:18 PM) alecu: nessita, the problem was that when adding new credentials to the keyring, old credentials with the same set of attributes (in this case, an empty dict of attributes) were being erased. The way to check if old credentials are kept is by adding a new sets of credentials, and checking that they are still there after adding a second new set of credentials.
(04:38:03 PM) nessita: alecu: I don't see why that can't be tested with a mock. We can create a faked gnome keyring class and add two set of credentials, and assert the both are there
(04:38:30 PM) nessita: and this mock will behave like the real gnome keyirng when the set of attrs is the same (the emtpy dict)
(04:39:02 PM) nessita: we can also assert that the gnomekeyring.item_delete_sync is not called while doing a set_ubuntusso_attr
(04:39:24 PM) nessita: that last check I think is the most important one, if I understood correctly

review: Needs Fixing
577. By Alejandro J. Cura

tests for the keyring module

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

Added tests as per nessita's suggestion.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Very nice, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/keyring.py'
2--- ubuntu_sso/keyring.py 2010-08-06 22:40:02 +0000
3+++ ubuntu_sso/keyring.py 2010-08-19 05:52:55 +0000
4@@ -2,6 +2,7 @@
5 #
6 # Authors:
7 # Andrew Higginson
8+# Alejandro J. Cura <alecu@canonical.com>
9 #
10 # This program is free software; you can redistribute it and/or modify it under
11 # the terms of the GNU General Public License as published by the Free Software
12@@ -39,14 +40,7 @@
13 keyring_names = gnomekeyring.list_keyring_names_sync()
14 if not name in keyring_names:
15 gnomekeyring.create_sync(name)
16-
17- def _item_exists(self, sync, name):
18- """
19- Returns whether a named item exists in a
20- named keyring
21- """
22- return self._get_item_id_from_name(sync, name) != None
23-
24+
25 def _get_item_id_from_name(self, sync, name):
26 """
27 Returns the ID for a named item
28@@ -69,25 +63,25 @@
29 return item_info
30 return None
31
32- def set_ubuntusso_attr(self, attr):
33- """
34- Sets the attributes of the Ubuntu SSO item
35- """
36- # Creates the secret from the attributes
37- secret = urlencode(attr)
38+ def set_ubuntusso_attr(self, cred):
39+ """
40+ Sets the credentials of the Ubuntu SSO item
41+ """
42+ # Creates the secret from the credentials
43+ secret = urlencode(cred)
44
45 # Create the keyring
46 self._create_keyring(self.KEYRING_NAME)
47
48- # If the item already exists, delete it
49- if self._item_exists(self.KEYRING_NAME, self.app_name):
50- item_id = self._get_item_id_from_name(self.KEYRING_NAME,
51- self.app_name)
52- gnomekeyring.item_delete_sync(self.KEYRING_NAME, item_id)
53+ # No need to delete the item if it already exists
54
55- # Add our SSO item
56+ # A set of attributes for this credentials
57+ attr = {"key-type": "Ubuntu SSO credentials",
58+ "token-name": cred["name"].encode("utf8")}
59+
60+ # Add our SSO credentials to the keyring
61 gnomekeyring.item_create_sync(self.KEYRING_NAME,
62- gnomekeyring.ITEM_GENERIC_SECRET, self.app_name, {},
63+ gnomekeyring.ITEM_GENERIC_SECRET, self.app_name, attr,
64 secret, True)
65
66 def get_ubuntusso_attr(self):
67@@ -101,11 +95,31 @@
68 secret = self._get_item_info_from_name(self.KEYRING_NAME,
69 self.app_name).get_secret()
70 return dict(parse_qsl(secret))
71+
72+ def delete_ubuntusso_attr(self):
73+ """
74+ Delete a set of credentials from the keyring
75+ """
76+ item_id = self._get_item_id_from_name(self.KEYRING_NAME,
77+ self.app_name)
78+ if item_id is not None:
79+ gnomekeyring.item_delete_sync(self.KEYRING_NAME, item_id)
80
81 if __name__ == "__main__":
82- SSO_ITEM_NAME = "Software Center UbuntuSSO token"
83- keyring = Keyring(SSO_ITEM_NAME)
84- keyring.set_ubuntusso_attr({"ha":"hehddddeff", "hi":"hggehes", "ho":"he"})
85- print keyring.get_ubuntusso_attr()
86-
87-
88+ kr1 = Keyring("Test key 1")
89+ kr2 = Keyring("Test key 2")
90+
91+ kr1.delete_ubuntusso_attr()
92+ kr2.delete_ubuntusso_attr()
93+
94+ d1 = {"name":"test-1", "ha":"hehddddeff", "hi":"hggehes", "ho":"he"}
95+ d2 = {"name":"test-2", "hi":"ho", "let's":"go"}
96+
97+ kr1.set_ubuntusso_attr(d1)
98+ kr2.set_ubuntusso_attr(d2)
99+
100+ r1 = kr1.get_ubuntusso_attr()
101+ r2 = kr2.get_ubuntusso_attr()
102+
103+ assert r1 == d1
104+ assert r2 == d2
105
106=== added file 'ubuntu_sso/tests/test_keyring.py'
107--- ubuntu_sso/tests/test_keyring.py 1970-01-01 00:00:00 +0000
108+++ ubuntu_sso/tests/test_keyring.py 2010-08-19 05:52:55 +0000
109@@ -0,0 +1,115 @@
110+# test_keyring - tests for ubuntu_sso.keyring
111+#
112+# Author: Alejandro J. Cura <alecu@canonical.com>
113+#
114+# Copyright 2010 Canonical Ltd.
115+#
116+# This program is free software: you can redistribute it and/or modify it
117+# under the terms of the GNU General Public License version 3, as published
118+# by the Free Software Foundation.
119+#
120+# This program is distributed in the hope that it will be useful, but
121+# WITHOUT ANY WARRANTY; without even the implied warranties of
122+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
123+# PURPOSE. See the GNU General Public License for more details.
124+#
125+# You should have received a copy of the GNU General Public License along
126+# with this program. If not, see <http://www.gnu.org/licenses/>.
127+"""Tests for the keyring.py module."""
128+
129+import gnomekeyring
130+from twisted.trial.unittest import TestCase
131+
132+from ubuntu_sso import keyring
133+
134+
135+class MockInfoSync(object):
136+ """A mock object that represents a fake key."""
137+
138+ def __init__(self, (key, key_name, secret)):
139+ """Initialize this instance."""
140+ self.key = key
141+ self.key_name = key_name
142+ self.secret = secret
143+
144+ def get_display_name(self):
145+ """Return info on the mocked object."""
146+ return self.key_name
147+
148+
149+class MockGnomeKeyring(object):
150+ """A mock keyring that stores keys according to a given attr."""
151+
152+ def __init__(self):
153+ """Initialize this instance."""
154+ self.keyrings = set()
155+ self.store = []
156+ self.deleted = []
157+
158+ def item_create_sync(self, keyring_name, item_type, key_name, attr,
159+ secret, update_if_exists):
160+ """Sets a value in the keyring."""
161+ # we'll use the attr as the dict key, so make it a tuple
162+ key = tuple(attr.items())
163+ self.store.append((key, key_name, secret))
164+
165+ def item_delete_sync(self, keyring_name, item_id):
166+ """Makes a list of deleted items."""
167+ key, key_name, secret = self.store.pop(item_id)
168+ self.deleted.append(key_name)
169+
170+ def list_item_ids_sync(self, keyring_name):
171+ """Return a list of ids for all the items."""
172+ return [n for n, v in enumerate(self.store)]
173+
174+ def item_get_info_sync(self, keyring_name, item_id):
175+ """Return an info sync object for the item."""
176+ return MockInfoSync(self.store[item_id])
177+
178+ def list_keyring_names_sync(self):
179+ """The keyring you are looking for may be here."""
180+ return self.keyrings
181+
182+ def create_sync(self, name):
183+ """Thanks, I'll create that keyring for you."""
184+ self.keyrings.add(name)
185+
186+ def is_available(self):
187+ """A very available keyring."""
188+ return True
189+
190+
191+class TestKeyring(TestCase):
192+ """Test the gnome keyring related functions."""
193+ def setUp(self):
194+ """Initialize the mock used in these tests."""
195+ self.mgk = MockGnomeKeyring()
196+ self.patch(gnomekeyring, "item_create_sync", self.mgk.item_create_sync)
197+ self.patch(gnomekeyring, "is_available", self.mgk.is_available)
198+ self.patch(gnomekeyring, "create_sync", self.mgk.create_sync)
199+ self.patch(gnomekeyring, "list_keyring_names_sync",
200+ self.mgk.list_keyring_names_sync)
201+ self.patch(gnomekeyring, "list_item_ids_sync",
202+ self.mgk.list_item_ids_sync)
203+ self.patch(gnomekeyring, "item_get_info_sync",
204+ self.mgk.item_get_info_sync)
205+ self.patch(gnomekeyring, "item_delete_sync", self.mgk.item_delete_sync)
206+
207+ def test_set_ubuntusso(self):
208+ """Test that the set method does not erase previous keys."""
209+ sample_creds = {"name": "sample creds name"}
210+ sample_creds2 = {"name": "sample creds name 2"}
211+ keyring.Keyring("appname").set_ubuntusso_attr(sample_creds)
212+ keyring.Keyring("appname").set_ubuntusso_attr(sample_creds2)
213+
214+ self.assertEqual(len(self.mgk.store), 2)
215+ self.assertEqual(len(self.mgk.deleted), 0)
216+
217+ def test_delete_ubuntusso(self):
218+ """Test that a given key is deleted."""
219+ sample_creds = {"name": "sample creds name"}
220+ keyring.Keyring("appname").set_ubuntusso_attr(sample_creds)
221+ keyring.Keyring("appname").delete_ubuntusso_attr()
222+
223+ self.assertEqual(len(self.mgk.store), 0)
224+ self.assertEqual(len(self.mgk.deleted), 1)

Subscribers

People subscribed via source and target branches