~ubuntu-branches/ubuntu/natty/ubuntu-sso-client/natty

« back to all changes in this revision

Viewing changes to ubuntu_sso/utils/tests/test_txsecrets.py

  • Committer: Daniel Holbach
  • Date: 2010-11-30 13:57:05 UTC
  • mfrom: (19.1.1 ubuntu-sso-client-1.1.5)
  • Revision ID: daniel.holbach@canonical.com-20101130135705-9iw0623qjcpuvpuq
Tags: 1.1.5-0ubuntu1
* New upstream release (1.1.5):
    * Use "org.freedesktop.secrets" dbus service instead of
    "org.gnome.keyring" (LP: #683088).
* New upstream release (1.1.4):
    * Added a gtk.Notebook to ensure proper window resize at startup
      (LP: #682669).
    * Enabled window resizing to be more user friendly.
    * Remove outdated references to gnome keyring from docstrings.
* New upstream release (1.1.3):
    * Make UI more friendly to resizes and big fonts (LP: #627496).
    * Splitting GUI code out of backend (LP: #677518).
    * Credentials can now be stored using a DBus called (LP: #680253).
    * Status from SSO server is now case sensitive (LP: #653165).
    * Credentials should not be cleared if the ping wasn't made due to empty
      ping url (LP: #676679).
   * Add the utils sub-package to the packages declaration so it installs
   (LP: #680593).
    * Fully async keyring access thru DBus. Drops dependency with
    gnomekeyring (LP: #656545).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# Author: Alejandro J. Cura <alecu@canonical.com>
 
4
#
 
5
# Copyright 2010 Canonical Ltd.
 
6
#
 
7
# This program is free software: you can redistribute it and/or modify it
 
8
# under the terms of the GNU General Public License version 3, as published
 
9
# by the Free Software Foundation.
 
10
#
 
11
# This program is distributed in the hope that it will be useful, but
 
12
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
13
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
14
# PURPOSE.  See the GNU General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License along
 
17
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
"""Tests for txkeyring."""
 
19
 
 
20
import uuid
 
21
 
 
22
import dbus.service
 
23
 
 
24
from twisted.internet.defer import inlineCallbacks, returnValue
 
25
from ubuntuone.devtools.testcase import DBusTestCase
 
26
 
 
27
from ubuntu_sso.utils import txsecrets
 
28
 
 
29
# DBus exported objects have different naming conventions than vanilla python
 
30
# pylint: disable=C0103
 
31
 
 
32
# Dear pylint, my clock says... 2010. Do you need a tutorial on decorators?
 
33
# pylint: disable=C0322
 
34
 
 
35
# pylint complains when things are a little too dynamic
 
36
# pylint: disable=E1101
 
37
 
 
38
 
 
39
class SampleMiscException(Exception):
 
40
    """An exception that will be turned into a DBus Exception."""
 
41
 
 
42
 
 
43
class ItemMock(dbus.service.Object):
 
44
    """An item contains a secret, lookup attributes and has a label."""
 
45
    get_secret_fail = False
 
46
    delete_fail = False
 
47
    delete_prompt = False
 
48
    dismissed = False
 
49
 
 
50
    def __init__(self, collection, label, attributes, value, *args, **kwargs):
 
51
        """Initialize this instance."""
 
52
        super(ItemMock, self).__init__(*args, **kwargs)
 
53
        self.collection = collection
 
54
        self.label = label
 
55
        self.attributes = attributes
 
56
        self.value = value
 
57
 
 
58
    @dbus.service.method(dbus_interface=txsecrets.ITEM_IFACE,
 
59
                         out_signature="o")
 
60
    def Delete(self):
 
61
        """Delete this item."""
 
62
        if self.delete_fail:
 
63
            raise SampleMiscException()
 
64
        self.collection.items.remove(self)
 
65
        if self.delete_prompt:
 
66
            prompt_path = create_object_path("/org/freedesktop/secrets/prompt")
 
67
            prompt = self.dbus_publish(prompt_path, PromptMock,
 
68
                                       result="",
 
69
                                       dismissed=self.dismissed)
 
70
            return prompt
 
71
        else:
 
72
            return "/"
 
73
 
 
74
    @dbus.service.method(dbus_interface=txsecrets.ITEM_IFACE,
 
75
                         in_signature="o", out_signature="(oayay)")
 
76
    def GetSecret(self, session):
 
77
        """Retrieve the secret for this item."""
 
78
        if self.get_secret_fail:
 
79
            raise SampleMiscException()
 
80
        return (session, "", self.value)
 
81
 
 
82
    def matches(self, search_attr):
 
83
        """See if this item matches a given search."""
 
84
        for k, val in search_attr.items():
 
85
            if k not in self.attributes:
 
86
                return False
 
87
            if self.attributes[k] != val:
 
88
                return False
 
89
        return True
 
90
 
 
91
 
 
92
class PromptMock(dbus.service.Object):
 
93
    """A prompt necessary to complete an operation."""
 
94
 
 
95
    def __init__(self, dismissed=True,
 
96
                 result=dbus.String("", variant_level=1), *args, **kwargs):
 
97
        """Initialize this instance."""
 
98
        super(PromptMock, self).__init__(*args, **kwargs)
 
99
        self.dismissed = dismissed
 
100
        self.result = result
 
101
 
 
102
    @dbus.service.method(dbus_interface=txsecrets.PROMPT_IFACE,
 
103
                         in_signature="s")
 
104
    def Prompt(self, window_id):
 
105
        """Perform the prompt."""
 
106
        self.Completed(self.dismissed, self.result)
 
107
 
 
108
    @dbus.service.signal(dbus_interface=txsecrets.PROMPT_IFACE,
 
109
                         signature="bv")
 
110
    def Completed(self, dismissed, result):
 
111
        """The prompt and operation completed."""
 
112
 
 
113
 
 
114
class CollectionMock(dbus.service.Object):
 
115
    """A collection of items containing secrets."""
 
116
    SUPPORTS_MULTIPLE_OBJECT_PATHS = True
 
117
    SUPPORTS_MULTIPLE_CONNECTIONS = True
 
118
    create_item_prompt = False
 
119
    dismissed = False
 
120
    create_item_fail = False
 
121
    locked = False
 
122
    unlock_prompts = False
 
123
 
 
124
    def __init__(self, label, *args, **kwargs):
 
125
        """Initialize this instance."""
 
126
        super(CollectionMock, self).__init__(*args, **kwargs)
 
127
        self.items = []
 
128
        self.label = label
 
129
 
 
130
    @dbus.service.method(dbus_interface=txsecrets.COLLECTION_IFACE,
 
131
                         in_signature="a{sv}(oayay)b", out_signature="oo",
 
132
                         byte_arrays=True)
 
133
    def CreateItem(self, properties, secret, replace):
 
134
        """Create an item with the given attributes, secret and label.
 
135
 
 
136
        If replace is set, then it replaces an item already present with the
 
137
        same values for the attributes.
 
138
        """
 
139
        if self.create_item_fail:
 
140
            raise SampleMiscException()
 
141
        attributes = properties[txsecrets.ATTRIBUTES_PROPERTY]
 
142
        item_label = properties[txsecrets.LABEL_PROPERTY]
 
143
        session, parameters, value = secret
 
144
        item_path = create_object_path("/org/freedesktop/secrets/collection/" +
 
145
                                                                    self.label)
 
146
        item = self.dbus_publish(item_path, ItemMock, self, item_label,
 
147
                                 attributes, value)
 
148
        self.items.append(item)
 
149
        if self.create_item_prompt:
 
150
            prompt_path = create_object_path("/org/freedesktop/secrets/prompt")
 
151
            prompt = self.dbus_publish(prompt_path, PromptMock,
 
152
                                       result=item,
 
153
                                       dismissed=self.dismissed)
 
154
            return "/", prompt
 
155
        else:
 
156
            return item, "/"
 
157
 
 
158
 
 
159
class SessionMock(dbus.service.Object):
 
160
    """A session tracks state between the service and a client application."""
 
161
 
 
162
    @dbus.service.method(dbus_interface=txsecrets.SESSION_IFACE)
 
163
    def Close(self):
 
164
        """Close this session."""
 
165
 
 
166
 
 
167
class SecretServiceMock(dbus.service.Object):
 
168
    """The Secret Service manages all the sessions and collections."""
 
169
    create_collection_prompt = False
 
170
    create_collection_fail = False
 
171
    open_session_fail = False
 
172
    dismissed = False
 
173
 
 
174
    def __init__(self, *args, **kwargs):
 
175
        """Initialize this instance."""
 
176
        super(SecretServiceMock, self).__init__(*args, **kwargs)
 
177
        self.sessions = {}
 
178
        self.collections = {}
 
179
 
 
180
    @dbus.service.method(dbus_interface=txsecrets.SERVICE_IFACE,
 
181
                         in_signature="sv", out_signature="vo")
 
182
    def OpenSession(self, algorithm, algorithm_parameters):
 
183
        """Open a unique session for the caller application."""
 
184
        if self.open_session_fail:
 
185
            raise SampleMiscException()
 
186
        session_path = create_object_path("/org/freedesktop/secrets/session")
 
187
        session = self.dbus_publish(session_path, SessionMock)
 
188
        self.sessions[session_path] = session
 
189
        return True, session
 
190
 
 
191
    def make_coll_path(self, label):
 
192
        """Make the path to a collection with its label"""
 
193
        return "/org/freedesktop/secrets/collection/" + label
 
194
 
 
195
    @dbus.service.method(dbus_interface=txsecrets.SERVICE_IFACE,
 
196
                         in_signature="a{sv}", out_signature="oo")
 
197
    def CreateCollection(self, properties):
 
198
        """Create a new collection with the specified properties."""
 
199
        if self.create_collection_fail:
 
200
            raise SampleMiscException()
 
201
        label = str(properties[txsecrets.LABEL_PROPERTY])
 
202
        if len(self.collections):
 
203
            coll_path = self.make_coll_path(label)
 
204
        else:
 
205
            coll_path = txsecrets.DEFAULT_COLLECTION
 
206
        collection = self.dbus_publish(coll_path, CollectionMock, label)
 
207
        self.collections[label] = collection
 
208
 
 
209
        if self.create_collection_prompt:
 
210
            prompt_path = create_object_path("/org/freedesktop/secrets/prompt")
 
211
            prompt = self.dbus_publish(prompt_path, PromptMock,
 
212
                                       result=collection,
 
213
                                       dismissed=self.dismissed)
 
214
            return "/", prompt
 
215
        else:
 
216
            return collection, "/"
 
217
 
 
218
    @dbus.service.method(dbus_interface=txsecrets.SERVICE_IFACE,
 
219
                         in_signature="a{ss}", out_signature="aoao")
 
220
    def SearchItems(self, attributes):
 
221
        """Find items in any collection."""
 
222
        unlocked_items = []
 
223
        locked_items = []
 
224
        for c in self.collections.values():
 
225
            if c.locked:
 
226
                append_item = locked_items.append
 
227
            else:
 
228
                append_item = unlocked_items.append
 
229
            for i in c.items:
 
230
                if i.matches(attributes):
 
231
                    append_item(i)
 
232
 
 
233
        return unlocked_items, locked_items
 
234
 
 
235
    def unlock_objects(self, objects):
 
236
        """Unlock the objects or its containers."""
 
237
        for c in self.collections.values():
 
238
            if c.__dbus_object_path__ in objects:
 
239
                c.locked = False
 
240
            for i in c.items:
 
241
                if i.__dbus_object_path__ in objects:
 
242
                    c.locked = False
 
243
 
 
244
    @dbus.service.method(dbus_interface=txsecrets.SERVICE_IFACE,
 
245
                         in_signature="ao", out_signature="aoo")
 
246
    def Unlock(self, objects):
 
247
        """Unlock the specified objects."""
 
248
        locked = []
 
249
        unlocked = []
 
250
        for c in self.collections.values():
 
251
            for i in c.items:
 
252
                path = i.__dbus_object_path__
 
253
                if path in objects:
 
254
                    if c.unlock_prompts:
 
255
                        locked.append(path)
 
256
                    else:
 
257
                        unlocked.append(path)
 
258
        if locked:
 
259
            prompt_path = create_object_path("/org/freedesktop/secrets/prompt")
 
260
            self.unlock_objects(objects)
 
261
            prompt = self.dbus_publish(prompt_path, PromptMock,
 
262
                                       result=locked,
 
263
                                       dismissed=self.dismissed)
 
264
            return unlocked, prompt
 
265
        else:
 
266
            self.unlock_objects(objects)
 
267
            return objects, "/"
 
268
 
 
269
    @dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties",
 
270
                         in_signature="ss", out_signature="v")
 
271
    def Get(self, interface, propname):
 
272
        """The only property implemented is Collections."""
 
273
        if interface == txsecrets.SERVICE_IFACE and propname == "Collections":
 
274
            coll_paths = [self.make_coll_path(l) for l in self.collections]
 
275
            return dbus.Array(coll_paths, signature="o", variant_level=1)
 
276
 
 
277
 
 
278
def create_object_path(base):
 
279
    """Create a random object path given a base path."""
 
280
    random = uuid.uuid4().hex
 
281
    return base + "/" + random
 
282
 
 
283
 
 
284
class BaseTestCase(DBusTestCase):
 
285
    """Base class for DBus tests."""
 
286
    timeout = 10
 
287
 
 
288
    def setUp(self):
 
289
        super(BaseTestCase, self).setUp()
 
290
        self.session_bus = dbus.SessionBus()
 
291
        self.mock_service = self.dbus_publish(txsecrets.SECRETS_SERVICE,
 
292
                                              SecretServiceMock)
 
293
        self.secretservice = txsecrets.SecretService()
 
294
 
 
295
    def dbus_publish(self, object_path, object_class, *args, **kwargs):
 
296
        """Create an object and publish it on the bus."""
 
297
        name = self.session_bus.request_name(txsecrets.BUS_NAME,
 
298
                                             dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
 
299
        self.assertNotEqual(name, dbus.bus.REQUEST_NAME_REPLY_EXISTS)
 
300
        mock_object = object_class(*args, object_path=object_path,
 
301
                                   conn=self.session_bus, **kwargs)
 
302
        self.addCleanup(mock_object.remove_from_connection)
 
303
        mock_object.dbus_publish = self.dbus_publish
 
304
        return mock_object
 
305
 
 
306
    @inlineCallbacks
 
307
    def create_sample_collection(self, label):
 
308
        """Create a collection with a given label."""
 
309
        coll = yield self.secretservice.create_collection(label)
 
310
        returnValue(coll)
 
311
 
 
312
 
 
313
class SecretServiceTestCase(BaseTestCase):
 
314
    """Test the Secret Service class."""
 
315
 
 
316
    @inlineCallbacks
 
317
    def test_open_session(self):
 
318
        """The secret service session is opened."""
 
319
        result = yield self.secretservice.open_session()
 
320
        self.assertEqual(result, self.secretservice)
 
321
 
 
322
    @inlineCallbacks
 
323
    def test_open_session_throws_dbus_error_as_failure(self):
 
324
        """The secret service open session throws a dbus error as a failure."""
 
325
        d = self.secretservice.open_session()
 
326
        self.mock_service.open_session_fail = True
 
327
        yield self.assertFailure(d, dbus.exceptions.DBusException)
 
328
 
 
329
    @inlineCallbacks
 
330
    def test_open_session_fails_before_opening_as_failure(self):
 
331
        """A dbus error before opening the session is thrown as a failure."""
 
332
 
 
333
        def fail(*args, **kwargs):
 
334
            """Throw a DBus exception."""
 
335
            raise dbus.exceptions.DBusException()
 
336
 
 
337
        self.patch(txsecrets.dbus, "SessionBus", fail)
 
338
        d = self.secretservice.open_session()
 
339
        self.mock_service.open_session_fail = True
 
340
        yield self.assertFailure(d, dbus.exceptions.DBusException)
 
341
 
 
342
    @inlineCallbacks
 
343
    def test_create_collection(self):
 
344
        """The secret service creates a collection."""
 
345
        yield self.secretservice.open_session()
 
346
        collection_label = "sample_keyring"
 
347
        yield self.create_sample_collection(collection_label)
 
348
        self.assertIn(collection_label, self.mock_service.collections)
 
349
 
 
350
    @inlineCallbacks
 
351
    def test_create_collection_prompt(self):
 
352
        """The secret service creates a collection after a prompt."""
 
353
        yield self.secretservice.open_session()
 
354
        self.mock_service.create_collection_prompt = True
 
355
        collection_label = "sample_keyring"
 
356
        yield self.create_sample_collection(collection_label)
 
357
        self.assertIn(collection_label, self.mock_service.collections)
 
358
 
 
359
    @inlineCallbacks
 
360
    def test_create_collection_prompt_dismissed(self):
 
361
        """The service fails to create collection when prompt dismissed."""
 
362
        yield self.secretservice.open_session()
 
363
        self.mock_service.create_collection_prompt = True
 
364
        self.mock_service.dismissed = True
 
365
        collection_label = "sample_keyring"
 
366
        yield self.assertFailure(
 
367
                           self.create_sample_collection(collection_label),
 
368
                           txsecrets.UserCancelled)
 
369
 
 
370
    @inlineCallbacks
 
371
    def test_create_collection_throws_dbus_error(self):
 
372
        """The service fails to create collection on a DBus error."""
 
373
        yield self.secretservice.open_session()
 
374
        self.mock_service.create_collection_fail = True
 
375
        collection_label = "sample_keyring"
 
376
        yield self.assertFailure(
 
377
                           self.create_sample_collection(collection_label),
 
378
                           dbus.exceptions.DBusException)
 
379
 
 
380
    @inlineCallbacks
 
381
    def test_prompt_accepted(self):
 
382
        """A prompt is accepted."""
 
383
        yield self.secretservice.open_session()
 
384
        expected_result = "hello world"
 
385
        prompt_path = "/prompt"
 
386
        self.dbus_publish(prompt_path, PromptMock, result=expected_result,
 
387
                          dismissed=False)
 
388
        result = yield self.secretservice.do_prompt(prompt_path)
 
389
        self.assertEqual(result, expected_result)
 
390
 
 
391
    @inlineCallbacks
 
392
    def test_prompt_dismissed(self):
 
393
        """A prompt is dismissed with a UserCancelled failure."""
 
394
        yield self.secretservice.open_session()
 
395
        expected_result = "hello world2"
 
396
        prompt_path = "/prompt"
 
397
        self.dbus_publish(prompt_path, PromptMock, result=expected_result,
 
398
                          dismissed=True)
 
399
        d = self.secretservice.do_prompt(prompt_path)
 
400
        self.assertFailure(d, txsecrets.UserCancelled)
 
401
 
 
402
    @inlineCallbacks
 
403
    def test_search_unlocked_items(self):
 
404
        """The secret service searchs for unlocked items."""
 
405
        yield self.secretservice.open_session()
 
406
        coll = yield self.create_sample_collection("sample_keyring")
 
407
        attr = {"key-type": "Ubuntu SSO credentials"}
 
408
        sample_secret = "secret83!"
 
409
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
410
        items = yield self.secretservice.search_items(attr)
 
411
        self.assertEqual(len(items), 1)
 
412
        value = yield items[0].get_value()
 
413
        self.assertEqual(value, sample_secret)
 
414
 
 
415
    @inlineCallbacks
 
416
    def test_search_locked_items(self):
 
417
        """The secret service searchs for locked items."""
 
418
        yield self.secretservice.open_session()
 
419
        collection_name = "sample_keyring"
 
420
        coll = yield self.create_sample_collection(collection_name)
 
421
        mock_collection = self.mock_service.collections[collection_name]
 
422
        mock_collection.locked = True
 
423
        attr = {"key-type": "Ubuntu SSO credentials"}
 
424
        sample_secret = "secret99!"
 
425
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
426
        items = yield self.secretservice.search_items(attr)
 
427
        self.assertEqual(len(items), 1)
 
428
        value = yield items[0].get_value()
 
429
        self.assertEqual(value, sample_secret)
 
430
 
 
431
    @inlineCallbacks
 
432
    def test_search_locked_items_prompts(self):
 
433
        """The secret service searchs for locked items after a prompt."""
 
434
        yield self.secretservice.open_session()
 
435
        collection_name = "sample_keyring"
 
436
        coll = yield self.create_sample_collection(collection_name)
 
437
        mock_collection = self.mock_service.collections[collection_name]
 
438
        mock_collection.locked = True
 
439
        mock_collection.unlock_prompts = True
 
440
        attr = {"key-type": "Ubuntu SSO credentials"}
 
441
        sample_secret = "secret99!"
 
442
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
443
        items = yield self.secretservice.search_items(attr)
 
444
        self.assertEqual(len(items), 1)
 
445
        value = yield items[0].get_value()
 
446
        self.assertEqual(value, sample_secret)
 
447
 
 
448
    @inlineCallbacks
 
449
    def test_search_locked_items_prompts_dismissed(self):
 
450
        """Service fails search for locked items after dismissed prompt."""
 
451
        yield self.secretservice.open_session()
 
452
        collection_name = "sample_keyring"
 
453
        coll = yield self.create_sample_collection(collection_name)
 
454
        mock_collection = self.mock_service.collections[collection_name]
 
455
        mock_collection.locked = True
 
456
        mock_collection.unlock_prompts = True
 
457
        self.mock_service.dismissed = True
 
458
        attr = {"key-type": "Ubuntu SSO credentials"}
 
459
        sample_secret = "secret99!"
 
460
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
461
        d = self.secretservice.search_items(attr)
 
462
        yield self.assertFailure(d, txsecrets.UserCancelled)
 
463
 
 
464
    @inlineCallbacks
 
465
    def test_search_items_merges_unlocked_and_locked_items(self):
 
466
        """search_items merges unlocked and locked items."""
 
467
        yield self.secretservice.open_session()
 
468
        attr = {"key-type": "Ubuntu SSO credentials"}
 
469
 
 
470
        collection_name = "coll1"
 
471
        coll = yield self.create_sample_collection(collection_name)
 
472
        mock_coll1 = self.mock_service.collections[collection_name]
 
473
        mock_coll1.locked = False
 
474
        mock_coll1.unlock_prompts = False
 
475
        unlocked_secret = "coll 1 secret!"
 
476
        yield coll.create_item("Cucaracha", attr, unlocked_secret)
 
477
 
 
478
        collection_name = "coll2"
 
479
        coll = yield self.create_sample_collection(collection_name)
 
480
        mock_coll2 = self.mock_service.collections[collection_name]
 
481
        mock_coll2.locked = True
 
482
        mock_coll2.unlock_prompts = False
 
483
        locked_secret = "coll 2 secret!"
 
484
        yield coll.create_item("Cucaracha", attr, locked_secret)
 
485
 
 
486
        result = yield self.secretservice.search_items(attr)
 
487
        self.assertEqual(len(result), 2)
 
488
 
 
489
    @inlineCallbacks
 
490
    def test_search_items_merges_unlocked_locked_and_prompt_items(self):
 
491
        """search_items merges unlocked, locked and prompt items."""
 
492
        yield self.secretservice.open_session()
 
493
        attr = {"key-type": "Ubuntu SSO credentials"}
 
494
 
 
495
        collection_name = "coll1"
 
496
        coll = yield self.create_sample_collection(collection_name)
 
497
        mock_coll1 = self.mock_service.collections[collection_name]
 
498
        mock_coll1.locked = False
 
499
        mock_coll1.unlock_prompts = False
 
500
        unlocked_secret = "coll 1 secret!"
 
501
        yield coll.create_item("Cucaracha", attr, unlocked_secret)
 
502
 
 
503
        collection_name = "coll2"
 
504
        coll = yield self.create_sample_collection(collection_name)
 
505
        mock_coll2 = self.mock_service.collections[collection_name]
 
506
        mock_coll2.locked = True
 
507
        mock_coll2.unlock_prompts = False
 
508
        locked_secret = "coll 2 secret!"
 
509
        yield coll.create_item("Cucaracha", attr, locked_secret)
 
510
 
 
511
        collection_name = "coll3"
 
512
        coll = yield self.create_sample_collection(collection_name)
 
513
        mock_coll3 = self.mock_service.collections[collection_name]
 
514
        mock_coll3.locked = True
 
515
        mock_coll3.unlock_prompts = True
 
516
        locked_secret = "coll 3 secret!"
 
517
        yield coll.create_item("Cucaracha", attr, locked_secret)
 
518
 
 
519
        result = yield self.secretservice.search_items(attr)
 
520
        self.assertEqual(len(result), 3)
 
521
 
 
522
    @inlineCallbacks
 
523
    def test_get_default_collection(self):
 
524
        """The default collection is returned."""
 
525
        yield self.secretservice.open_session()
 
526
        collection_name = "sample_keyring"
 
527
        yield self.create_sample_collection(collection_name)
 
528
        self.assertEqual(len(self.mock_service.collections), 1)
 
529
        yield self.secretservice.get_default_collection()
 
530
        self.assertEqual(len(self.mock_service.collections), 1)
 
531
 
 
532
    @inlineCallbacks
 
533
    def test_get_default_collection_created_if_nonexistent(self):
 
534
        """The default collection is created if it doesn't exist yet."""
 
535
        yield self.secretservice.open_session()
 
536
        self.assertEqual(len(self.mock_service.collections), 0)
 
537
        yield self.secretservice.get_default_collection()
 
538
        self.assertEqual(len(self.mock_service.collections), 1)
 
539
 
 
540
 
 
541
class CollectionTestCase(BaseTestCase):
 
542
    """Test the Collection class."""
 
543
 
 
544
    @inlineCallbacks
 
545
    def test_create_item(self):
 
546
        """The collection creates an item."""
 
547
        yield self.secretservice.open_session()
 
548
        collection_label = "sample_keyring"
 
549
        yield self.create_sample_collection(collection_label)
 
550
        coll = yield self.secretservice.get_default_collection()
 
551
        mock_collection = self.mock_service.collections[collection_label]
 
552
        attr = {"key-type": "Ubuntu 242 credentials"}
 
553
        sample_secret = "secret!"
 
554
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
555
        self.assertEqual(len(mock_collection.items), 1)
 
556
        self.assertEqual(mock_collection.items[0].value, sample_secret)
 
557
 
 
558
    @inlineCallbacks
 
559
    def test_create_item_prompt(self):
 
560
        """The collection creates an item after a prompt."""
 
561
        yield self.secretservice.open_session()
 
562
        collection_label = "sample_keyring"
 
563
        yield self.create_sample_collection(collection_label)
 
564
        coll = yield self.secretservice.get_default_collection()
 
565
        mock_collection = self.mock_service.collections[collection_label]
 
566
        mock_collection.create_item_prompt = True
 
567
        attr = {"key-type": "Ubuntu 242 credentials"}
 
568
        sample_secret = "secret2!"
 
569
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
570
        self.assertEqual(len(mock_collection.items), 1)
 
571
        self.assertEqual(mock_collection.items[0].value, sample_secret)
 
572
 
 
573
    @inlineCallbacks
 
574
    def test_create_item_prompt_dismissed(self):
 
575
        """The collection fails to create an item when prompt is dismissed."""
 
576
        yield self.secretservice.open_session()
 
577
        collection_label = "sample_keyring"
 
578
        yield self.create_sample_collection(collection_label)
 
579
        coll = yield self.secretservice.get_default_collection()
 
580
        mock_collection = self.mock_service.collections[collection_label]
 
581
        mock_collection.create_item_prompt = True
 
582
        mock_collection.dismissed = True
 
583
        attr = {"key-type": "Ubuntu 242 credentials"}
 
584
        sample_secret = "secret3!"
 
585
        yield self.assertFailure(coll.create_item("Cuca", attr, sample_secret),
 
586
                                 txsecrets.UserCancelled)
 
587
 
 
588
    @inlineCallbacks
 
589
    def test_create_item_throws_dbus_error(self):
 
590
        """The collection fails to create an item when DBus fails."""
 
591
        yield self.secretservice.open_session()
 
592
        collection_label = "sample_keyring"
 
593
        yield self.create_sample_collection(collection_label)
 
594
        coll = yield self.secretservice.get_default_collection()
 
595
        mock_collection = self.mock_service.collections[collection_label]
 
596
        mock_collection.create_item_fail = True
 
597
        attr = {"key-type": "Ubuntu 242 credentials"}
 
598
        sample_secret = "secret4!"
 
599
        yield self.assertFailure(coll.create_item("Cuca", attr, sample_secret),
 
600
                                 dbus.exceptions.DBusException)
 
601
 
 
602
 
 
603
class ItemTestCase(BaseTestCase):
 
604
    """Test the Item class."""
 
605
 
 
606
    @inlineCallbacks
 
607
    def test_get_value(self):
 
608
        """The secret value is retrieved from the item."""
 
609
        yield self.secretservice.open_session()
 
610
        coll = yield self.create_sample_collection("sample_keyring")
 
611
        attr = {"key-type": "Ubuntu SSO credentials"}
 
612
        sample_secret = "secret83!"
 
613
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
614
        items = yield self.secretservice.search_items(attr)
 
615
        self.assertEqual(len(items), 1)
 
616
        value = yield items[0].get_value()
 
617
        self.assertEqual(value, sample_secret)
 
618
 
 
619
    @inlineCallbacks
 
620
    def test_get_value_throws_dbus_error(self):
 
621
        """The secret value is not retrieved if DBus fails."""
 
622
        yield self.secretservice.open_session()
 
623
        collection_label = "sample_keyring"
 
624
        coll = yield self.create_sample_collection(collection_label)
 
625
        attr = {"key-type": "Ubuntu SSO credentials"}
 
626
        sample_secret = "secret83!"
 
627
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
628
        items = yield self.secretservice.search_items(attr)
 
629
        self.assertEqual(len(items), 1)
 
630
        mock = self.mock_service.collections[collection_label].items[0]
 
631
        mock.get_secret_fail = True
 
632
        yield self.assertFailure(items[0].get_value(),
 
633
                                 dbus.exceptions.DBusException)
 
634
 
 
635
    @inlineCallbacks
 
636
    def test_delete(self):
 
637
        """The item is deleted."""
 
638
        yield self.secretservice.open_session()
 
639
        coll = yield self.create_sample_collection("sample_keyring")
 
640
        attr = {"key-type": "Ubuntu SSO credentials"}
 
641
        sample_secret = "secret83!"
 
642
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
643
        items = yield self.secretservice.search_items(attr)
 
644
        self.assertEqual(len(items), 1)
 
645
        yield items[0].delete()
 
646
        items = yield self.secretservice.search_items(attr)
 
647
        self.assertEqual(len(items), 0)
 
648
 
 
649
    @inlineCallbacks
 
650
    def test_delete_prompt(self):
 
651
        """The item is deleted after a prompt."""
 
652
        yield self.secretservice.open_session()
 
653
        collection_label = "sample_keyring"
 
654
        coll = yield self.create_sample_collection(collection_label)
 
655
        attr = {"key-type": "Ubuntu SSO credentials"}
 
656
        sample_secret = "secret83!"
 
657
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
658
        items = yield self.secretservice.search_items(attr)
 
659
        self.assertEqual(len(items), 1)
 
660
        mock_item = self.mock_service.collections[collection_label].items[0]
 
661
        mock_item.delete_prompt = True
 
662
        yield items[0].delete()
 
663
        items = yield self.secretservice.search_items(attr)
 
664
        self.assertEqual(len(items), 0)
 
665
 
 
666
    @inlineCallbacks
 
667
    def test_delete_prompt_dismissed(self):
 
668
        """The item is not deleted after a dismissed prompt."""
 
669
        yield self.secretservice.open_session()
 
670
        collection_label = "sample_keyring"
 
671
        coll = yield self.create_sample_collection(collection_label)
 
672
        attr = {"key-type": "Ubuntu SSO credentials"}
 
673
        sample_secret = "secret83!"
 
674
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
675
        items = yield self.secretservice.search_items(attr)
 
676
        self.assertEqual(len(items), 1)
 
677
        mock_item = self.mock_service.collections[collection_label].items[0]
 
678
        mock_item.delete_prompt = True
 
679
        mock_item.dismissed = True
 
680
        yield self.assertFailure(items[0].delete(), txsecrets.UserCancelled)
 
681
 
 
682
    @inlineCallbacks
 
683
    def test_delete_throws_dbus_error(self):
 
684
        """The item is not deleted when a DBus error happens."""
 
685
        yield self.secretservice.open_session()
 
686
        collection_label = "sample_keyring"
 
687
        coll = yield self.create_sample_collection(collection_label)
 
688
        attr = {"key-type": "Ubuntu SSO credentials"}
 
689
        sample_secret = "secret83!"
 
690
        yield coll.create_item("Cucaracha", attr, sample_secret)
 
691
        items = yield self.secretservice.search_items(attr)
 
692
        self.assertEqual(len(items), 1)
 
693
        mock_item = self.mock_service.collections[collection_label].items[0]
 
694
        mock_item.delete_fail = True
 
695
        yield self.assertFailure(items[0].delete(),
 
696
                                 dbus.exceptions.DBusException)