Merge lp:~nataliabidart/magicicada-client/no-sso-client into lp:magicicada-client

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 1439
Merged at revision: 1419
Proposed branch: lp:~nataliabidart/magicicada-client/no-sso-client
Merge into: lp:magicicada-client
Diff against target: 13717 lines (+8626/-3329)
119 files modified
.travis.yml (+1/-0)
bin/ubuntuone-login (+0/-80)
contrib/check-reactor-import (+4/-5)
contrib/draw-fsm (+0/-1)
contrib/login_email_password.py (+0/-62)
contrib/testing/testcase.py (+2/-1)
data/com.ubuntuone.Credentials.service.in (+0/-4)
dependencies-devel.txt (+1/-0)
dependencies.txt (+2/-1)
po/POTFILES.in (+0/-1)
run-tests (+4/-4)
setup.py (+0/-7)
tests/platform/credentials/__init__.py (+0/-28)
tests/platform/credentials/test_ipc_service.py (+0/-328)
tests/platform/credentials/test_linux.py (+0/-1119)
tests/platform/test_credentials.py (+0/-502)
ubuntuone/__init__.py (+6/-1)
ubuntuone/clientdefs.py.in (+18/-0)
ubuntuone/keyring.py (+145/-0)
ubuntuone/networkstate/__init__.py (+55/-0)
ubuntuone/networkstate/darwin.py (+305/-0)
ubuntuone/networkstate/linux.py (+130/-0)
ubuntuone/networkstate/networkstates.py (+59/-0)
ubuntuone/networkstate/tests/__init__.py (+31/-0)
ubuntuone/networkstate/tests/test_darwin.py (+186/-0)
ubuntuone/networkstate/tests/test_linux.py (+366/-0)
ubuntuone/networkstate/tests/test_windows.py (+191/-0)
ubuntuone/networkstate/windows.py (+212/-0)
ubuntuone/platform/credentials/__init__.py (+0/-404)
ubuntuone/platform/credentials/dbus_service.py (+0/-285)
ubuntuone/platform/credentials/ipc_service.py (+0/-174)
ubuntuone/platform/filesystem_notifications/monitor/darwin/fsevents_daemon.py (+7/-9)
ubuntuone/platform/ipc/linux.py (+2/-2)
ubuntuone/platform/ipc/perspective_broker.py (+2/-3)
ubuntuone/platform/ipc/unix.py (+3/-3)
ubuntuone/platform/ipc/windows.py (+2/-4)
ubuntuone/platform/launcher/__init__.py (+3/-4)
ubuntuone/platform/launcher/linux.py (+6/-6)
ubuntuone/platform/launcher/windows.py (+6/-6)
ubuntuone/platform/notification/__init__.py (+0/-2)
ubuntuone/platform/notification/linux.py (+3/-3)
ubuntuone/platform/notification/windows.py (+2/-3)
ubuntuone/platform/session/__init__.py (+0/-1)
ubuntuone/platform/session/linux.py (+3/-3)
ubuntuone/platform/sync_menu/linux.py (+11/-11)
ubuntuone/platform/tests/__init__.py (+2/-2)
ubuntuone/platform/tests/filesystem_notifications/__init__.py (+1/-4)
ubuntuone/platform/tests/filesystem_notifications/test_darwin.py (+4/-2)
ubuntuone/platform/tests/filesystem_notifications/test_linux.py (+1/-4)
ubuntuone/platform/tests/filesystem_notifications/test_windows.py (+3/-1)
ubuntuone/platform/tests/ipc/test_external_interface.py (+1/-1)
ubuntuone/platform/tests/ipc/test_linux.py (+2/-6)
ubuntuone/platform/tests/ipc/test_perspective_broker.py (+2/-2)
ubuntuone/platform/tests/ipc/test_unix.py (+1/-1)
ubuntuone/platform/tests/launcher/test_linux.py (+1/-1)
ubuntuone/platform/tests/linux/test_vm.py (+4/-9)
ubuntuone/platform/tests/os_helper/test_darwin.py (+2/-2)
ubuntuone/platform/tests/os_helper/test_linux.py (+1/-1)
ubuntuone/platform/tests/os_helper/test_windows.py (+1/-3)
ubuntuone/platform/tests/test_tools.py (+1/-1)
ubuntuone/platform/tests/test_u1sdtool.py (+1/-1)
ubuntuone/proxy/__init__.py (+1/-1)
ubuntuone/proxy/tests/__init__.py (+0/-3)
ubuntuone/proxy/tests/test_tunnel_client.py (+1/-1)
ubuntuone/proxy/tests/test_tunnel_server.py (+1/-1)
ubuntuone/proxy/tunnel_client.py (+2/-1)
ubuntuone/proxy/tunnel_server.py (+5/-4)
ubuntuone/status/__init__.py (+0/-2)
ubuntuone/status/aggregator.py (+13/-19)
ubuntuone/status/notification.py (+0/-2)
ubuntuone/status/tests/test_aggregator.py (+19/-19)
ubuntuone/syncdaemon/__init__.py (+1/-2)
ubuntuone/syncdaemon/action_queue.py (+6/-5)
ubuntuone/syncdaemon/filesystem_manager.py (+3/-2)
ubuntuone/syncdaemon/interaction_interfaces.py (+22/-38)
ubuntuone/syncdaemon/logger.py (+2/-3)
ubuntuone/syncdaemon/main.py (+4/-3)
ubuntuone/syncdaemon/tests/fsm/test_fsm.py (+0/-1)
ubuntuone/syncdaemon/tests/fsm/test_run_hello.py (+0/-2)
ubuntuone/syncdaemon/tests/test_action_queue.py (+0/-2)
ubuntuone/syncdaemon/tests/test_eq_inotify.py (+1/-1)
ubuntuone/syncdaemon/tests/test_eventqueue.py (+0/-1)
ubuntuone/syncdaemon/tests/test_fileshelf.py (+0/-2)
ubuntuone/syncdaemon/tests/test_fsm.py (+0/-18)
ubuntuone/syncdaemon/tests/test_hashqueue.py (+0/-4)
ubuntuone/syncdaemon/tests/test_interaction_interfaces.py (+30/-71)
ubuntuone/syncdaemon/tests/test_localrescan.py (+0/-3)
ubuntuone/syncdaemon/tests/test_tunnel_runner.py (+1/-1)
ubuntuone/syncdaemon/tests/test_vm.py (+4/-4)
ubuntuone/syncdaemon/tests/test_vm_helper.py (+1/-2)
ubuntuone/syncdaemon/volume_manager.py (+2/-2)
ubuntuone/tests/__init__.py (+62/-1)
ubuntuone/tests/test_keyring.py (+234/-0)
ubuntuone/utils/__init__.py (+154/-0)
ubuntuone/utils/compat.py (+49/-0)
ubuntuone/utils/ipc.py (+416/-0)
ubuntuone/utils/locale.py (+49/-0)
ubuntuone/utils/tcpactivation.py (+183/-0)
ubuntuone/utils/tests/__init__.py (+31/-0)
ubuntuone/utils/tests/test_common.py (+240/-0)
ubuntuone/utils/tests/test_ipc.py (+682/-0)
ubuntuone/utils/tests/test_locale.py (+98/-0)
ubuntuone/utils/tests/test_tcpactivation.py (+483/-0)
ubuntuone/utils/tests/test_translation.py (+224/-0)
ubuntuone/utils/tests/test_txsecrets.py (+967/-0)
ubuntuone/utils/translation.py (+93/-0)
ubuntuone/utils/txsecrets.py (+395/-0)
ubuntuone/utils/webclient/__init__.py (+49/-0)
ubuntuone/utils/webclient/common.py (+189/-0)
ubuntuone/utils/webclient/gsettings.py (+114/-0)
ubuntuone/utils/webclient/libsoup.py (+137/-0)
ubuntuone/utils/webclient/restful.py (+94/-0)
ubuntuone/utils/webclient/tests/__init__.py (+65/-0)
ubuntuone/utils/webclient/tests/test_gsettings.py (+292/-0)
ubuntuone/utils/webclient/tests/test_restful.py (+233/-0)
ubuntuone/utils/webclient/tests/test_timestamp.py (+140/-0)
ubuntuone/utils/webclient/tests/test_webclient.py (+769/-0)
ubuntuone/utils/webclient/timestamp.py (+93/-0)
ubuntuone/utils/webclient/txweb.py (+176/-0)
To merge this branch: bzr merge lp:~nataliabidart/magicicada-client/no-sso-client
Reviewer Review Type Date Requested Status
Facundo Batista Approve
Review via email: mp+295999@code.launchpad.net

Commit message

- Decouple client code from ubuntu-sso-client code. Copied and made an initial cleanup on the networkstate, utils and keyring modules.
- Removed completely dependencies with oauthlibs.
- Moved tests/ folder to inside ubuntuone/ proper folders.

Description of the change

To post a comment you must log in.
1429. By Natalia Bidart

Removed debug print.

1430. By Natalia Bidart

Fixed copyright notices.

1431. By Natalia Bidart

Removed almost every mention of Magicicada.

1432. By Natalia Bidart

Fixed missing imports.

1433. By Natalia Bidart

More tests fixed.

1434. By Natalia Bidart

Moved tests inside ubuntuone folder.

1435. By Natalia Bidart

Proper tests paths in run-tests.

1436. By Natalia Bidart

No need for multiple support for keyring. No need for runner.

1437. By Natalia Bidart

All tests passing.

1438. By Natalia Bidart

Revert changes to run-tests.

1439. By Natalia Bidart

Add chicharreros copyright, fixed typo.

Revision history for this message
Facundo Batista (facundo) wrote :

Go go

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.travis.yml'
2--- .travis.yml 2015-09-19 20:49:35 +0000
3+++ .travis.yml 2016-05-30 13:04:07 +0000
4@@ -1,5 +1,6 @@
5 before_install:
6 - sudo apt-get update
7+ - sudo apt-get dist-upgrade -y
8 - make clean
9 - make bootstrap
10
11
12=== removed file 'bin/ubuntuone-login'
13--- bin/ubuntuone-login 2015-09-29 21:05:26 +0000
14+++ bin/ubuntuone-login 1970-01-01 00:00:00 +0000
15@@ -1,80 +0,0 @@
16-#!/usr/bin/python
17-# -*- coding: utf-8 -*-
18-#
19-# Copyright 2010-2012 Canonical Ltd.
20-#
21-# This program is free software: you can redistribute it and/or modify it
22-# under the terms of the GNU General Public License version 3, as published
23-# by the Free Software Foundation.
24-#
25-# This program is distributed in the hope that it will be useful, but
26-# WITHOUT ANY WARRANTY; without even the implied warranties of
27-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
28-# PURPOSE. See the GNU General Public License for more details.
29-#
30-# You should have received a copy of the GNU General Public License along
31-# with this program. If not, see <http://www.gnu.org/licenses/>.
32-#
33-# In addition, as a special exception, the copyright holders give
34-# permission to link the code of portions of this program with the
35-# OpenSSL library under certain conditions as described in each
36-# individual source file, and distribute linked combinations
37-# including the two.
38-# You must obey the GNU General Public License in all respects
39-# for all of the code used other than OpenSSL. If you modify
40-# file(s) with this exception, you may extend this exception to your
41-# version of the file(s), but you are not obligated to do so. If you
42-# do not wish to do so, delete this exception statement from your
43-# version. If you delete this exception statement from all source
44-# files in the program, then also delete it here.
45-"""The script tu run the Login D-Bus service."""
46-
47-# Invalid name "ubuntuone-login"
48-
49-import logging
50-import os
51-import sys
52-
53-import dbus.mainloop.glib
54-import dbus.service
55-
56-from gi.repository import GLib
57-from ubuntuone.logger import basic_formatter
58-from ubuntuone.platform.credentials import logger
59-from ubuntuone.platform.credentials.dbus_service import (
60- DBUS_BUS_NAME, DBUS_CREDENTIALS_PATH,
61- CredentialsManagement,
62-)
63-
64-dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
65-
66-
67-if __name__ == "__main__":
68- # Register DBus service for making sure we run only one instance
69- try:
70- bus = dbus.SessionBus()
71- name = bus.request_name(DBUS_BUS_NAME,
72- dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
73- if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
74- logger.error(Login manager already running, quitting.")
75- sys.exit(0)
76- except dbus.DBusException:
77- pass
78- else:
79- if os.environ.get('U1_DEBUG'):
80- debug_handler = logging.StreamHandler(sys.stderr)
81- debug_handler.setFormatter(basic_formatter)
82- debug_handler.setLevel(logging.DEBUG)
83- logger.setLevel(logging.DEBUG)
84- logger.addHandler(debug_handler)
85-
86- logger.info("Starting login manager for bus %r.",
87- DBUS_BUS_NAME)
88- bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=bus)
89- mainloop = GLib.MainLoop()
90- CredentialsManagement(timeout_func=GLib.timeout_add,
91- shutdown_func=mainloop.quit,
92- bus_name=bus_name,
93- object_path=DBUS_CREDENTIALS_PATH)
94-
95- mainloop.run()
96
97=== modified file 'contrib/check-reactor-import'
98--- contrib/check-reactor-import 2013-06-10 21:21:11 +0000
99+++ contrib/check-reactor-import 2016-05-30 13:04:07 +0000
100@@ -31,10 +31,9 @@
101 # NOTE: the goal of this script is to avoid a bug that affects
102 # ubuntuone-control-panel on windows and darwin. Those platforms use
103 # the qt4reactor, and will break if the default reactor is installed
104-# first. This can happen if a module used by control-panel (such as
105-# ubuntuone.platform.credentials), imports reactor. Only sub-modules
106-# that are not used by ubuntuone-control-panel can safely import
107-# reactor at module-level.
108+# first. This can happen if a module used by control-panel imports reactor.
109+# Only sub-modules that are not used by ubuntuone-control-panel can safely
110+# import reactor at module-level.
111
112 from __future__ import (unicode_literals, print_function)
113
114@@ -72,6 +71,6 @@
115 real_import = __builtin__.__import__
116 __builtin__.__import__ = fake_import
117
118- subs = ["", ".tools", ".logger", ".credentials"]
119+ subs = ["", ".tools", ".logger"]
120 for module in ["ubuntuone.platform" + p for p in subs]:
121 m = __import__(module)
122
123=== modified file 'contrib/draw-fsm'
124--- contrib/draw-fsm 2013-06-07 15:22:31 +0000
125+++ contrib/draw-fsm 2016-05-30 13:04:07 +0000
126@@ -35,7 +35,6 @@
127 import time
128 import sys
129
130-# pylint: disable-msg=F0401
131 from gi.repository import Gtk as gtk
132
133 import xdot
134
135=== removed file 'contrib/login_email_password.py'
136--- contrib/login_email_password.py 2013-01-28 18:10:43 +0000
137+++ contrib/login_email_password.py 1970-01-01 00:00:00 +0000
138@@ -1,62 +0,0 @@
139-# -*- coding: utf-8 -*-
140-#
141-# Copyright 2011-2013 Canonical Ltd.
142-#
143-# This program is free software: you can redistribute it and/or modify it
144-# under the terms of the GNU General Public License version 3, as published
145-# by the Free Software Foundation.
146-#
147-# This program is distributed in the hope that it will be useful, but
148-# WITHOUT ANY WARRANTY; without even the implied warranties of
149-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
150-# PURPOSE. See the GNU General Public License for more details.
151-#
152-# You should have received a copy of the GNU General Public License along
153-# with this program. If not, see <http://www.gnu.org/licenses/>.
154-#
155-# In addition, as a special exception, the copyright holders give
156-# permission to link the code of portions of this program with the
157-# OpenSSL library under certain conditions as described in each
158-# individual source file, and distribute linked combinations
159-# including the two.
160-# You must obey the GNU General Public License in all respects
161-# for all of the code used other than OpenSSL. If you modify
162-# file(s) with this exception, you may extend this exception to your
163-# version of the file(s), but you are not obligated to do so. If you
164-# do not wish to do so, delete this exception statement from your
165-# version. If you delete this exception statement from all source
166-# files in the program, then also delete it here.
167-"""Script that shows the qt gui."""
168-
169-import sys
170-
171-if sys.platform != 'win32':
172- from twisted.internet import gireactor
173- gireactor.install()
174-
175-from twisted.internet import reactor
176-from twisted.internet.defer import inlineCallbacks
177-from ubuntuone.platform.credentials import CredentialsManagementTool
178-
179-
180-@inlineCallbacks
181-def main(email='whomever@canonical.com', password='whatever'):
182- """Perform a client request to be logged in."""
183- credtool = CredentialsManagementTool()
184- print 'Trying to get credentials for email:', email
185- try:
186- creds = yield credtool.login_email_password(email=email,
187- password=password)
188- print "creds found!", creds
189- except Exception, e:
190- print "creds error!", e
191- reactor.stop()
192-
193-
194-if __name__ == '__main__':
195- print 'Enter email:'
196- email = raw_input()
197- print 'Enter password:'
198- password = raw_input()
199- main(email=email, password=password)
200- reactor.run()
201
202=== modified file 'contrib/testing/testcase.py'
203--- contrib/testing/testcase.py 2015-05-18 21:20:05 +0000
204+++ contrib/testing/testcase.py 2016-05-30 13:04:07 +0000
205@@ -1,6 +1,7 @@
206 # -*- coding: utf-8 -*-
207 #
208 # Copyright 2009-2015 Canonical Ltd.
209+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
210 #
211 # This program is free software: you can redistribute it and/or modify it
212 # under the terms of the GNU General Public License version 3, as published
213@@ -679,7 +680,7 @@
214
215 def __init__(self, main, send_events=False, interface=None):
216 super(FakedService, self).__init__(main, send_events, interface)
217- self.oauth_credentials = ('foo', 'bar')
218+ self.auth_credentials = ('foo', 'bar')
219
220 def _create_children(self):
221 """Override parent's method to have fakes for children."""
222
223=== removed file 'data/com.ubuntuone.Credentials.service.in'
224--- data/com.ubuntuone.Credentials.service.in 2011-01-04 13:09:01 +0000
225+++ data/com.ubuntuone.Credentials.service.in 1970-01-01 00:00:00 +0000
226@@ -1,4 +0,0 @@
227-[D-BUS Service]
228-Name=com.ubuntuone.Credentials
229-Exec=@libexecdir@/ubuntuone-login
230-
231
232=== modified file 'dependencies-devel.txt'
233--- dependencies-devel.txt 2015-09-19 21:11:52 +0000
234+++ dependencies-devel.txt 2016-05-30 13:04:07 +0000
235@@ -1,6 +1,7 @@
236 bzr
237 make
238 python-distutils-extra
239+python-flake8
240 python-mocker
241 python-virtualenv
242 ubuntuone-dev-tools
243
244=== modified file 'dependencies.txt'
245--- dependencies.txt 2015-09-20 00:21:25 +0000
246+++ dependencies.txt 2016-05-30 13:04:07 +0000
247@@ -1,10 +1,11 @@
248 gir1.2-notify
249+gir1.2-soup-2.4
250 protobuf-compiler
251 python-configglue
252 python-gi
253+python-httplib2
254 python-protobuf
255 python-pyinotify
256 python-qt4-dbus
257 python-qt4reactor
258 python-twisted
259-python-ubuntu-sso-client
260
261=== modified file 'po/POTFILES.in'
262--- po/POTFILES.in 2013-06-05 20:09:11 +0000
263+++ po/POTFILES.in 2016-05-30 13:04:07 +0000
264@@ -1,4 +1,3 @@
265 ubuntuone/clientdefs.py.in
266 ubuntuone/status/aggregator.py
267-ubuntuone/platform/credentials/__init__.py
268 ubuntuone/platform/sync_menu/linux.py
269
270=== modified file 'run-tests'
271--- run-tests 2015-09-20 19:24:59 +0000
272+++ run-tests 2016-05-30 13:04:07 +0000
273@@ -27,7 +27,7 @@
274 # version. If you delete this exception statement from all source
275 # files in the program, then also delete it here.
276
277-PROXY_TESTS_PATH="tests/proxy"
278+PROXY_TESTS_PATH="ubuntuone/proxy/tests"
279
280 # Allow alternative python executable via environment variable. This is
281 # useful for virtualenv testing.
282@@ -39,19 +39,19 @@
283 MODULE="$@"
284 else
285 # run all tests, useful for tarmac and reviews
286- MODULE="tests"
287+ MODULE="ubuntuone"
288 fi
289
290 SYSNAME=`uname -s`
291
292 if [ "$SYSNAME" == "Darwin" ]; then
293 IGNORE_FILES="test_linux.py,test_windows.py"
294- IGNORE_PATHS="tests/platform/linux"
295+ IGNORE_PATHS="ubuntuone/platform/tests/linux"
296 REACTOR=qt4
297 else
298 # Linux
299 IGNORE_FILES="test_darwin.py,test_fsevents_daemon.py,test_windows.py"
300- IGNORE_PATHS="tests/platform/windows"
301+ IGNORE_PATHS="ubuntuone/platform/tests/windows"
302 REACTOR=gi
303 fi
304
305
306=== modified file 'setup.py'
307--- setup.py 2015-09-17 02:20:40 +0000
308+++ setup.py 2016-05-30 13:04:07 +0000
309@@ -170,13 +170,9 @@
310 def set_py2exe_paths():
311 """Set the path so that py2exe finds the required modules."""
312 # Pylint does not understand same spaced imports
313- # pylint: disable=F0401
314 import win32com
315- # pylint: enable=F0401
316 try:
317- # pylint: disable=F0401
318 import py2exe.mf as modulefinder
319- # pylint: enable=F0401
320 except ImportError:
321 import modulefinder
322
323@@ -194,8 +190,6 @@
324 modulefinder.AddPackagePath(extra_mod, module_path)
325
326
327-# pylint: disable=C0103
328-
329 cmdclass = {
330 'install': Install,
331 'build': Build,
332@@ -209,7 +203,6 @@
333 ]
334
335 libexec_scripts = [
336- 'bin/ubuntuone-login',
337 'bin/ubuntuone-proxy-tunnel',
338 'bin/ubuntuone-syncdaemon',
339 ]
340
341=== removed directory 'tests/api'
342=== removed directory 'tests/platform/credentials'
343=== removed file 'tests/platform/credentials/__init__.py'
344--- tests/platform/credentials/__init__.py 2015-09-19 23:15:50 +0000
345+++ tests/platform/credentials/__init__.py 1970-01-01 00:00:00 +0000
346@@ -1,28 +0,0 @@
347-# Copyright 2012 Canonical Ltd.
348-#
349-# This program is free software: you can redistribute it and/or modify it
350-# under the terms of the GNU General Public License version 3, as published
351-# by the Free Software Foundation.
352-#
353-# This program is distributed in the hope that it will be useful, but
354-# WITHOUT ANY WARRANTY; without even the implied warranties of
355-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
356-# PURPOSE. See the GNU General Public License for more details.
357-#
358-# You should have received a copy of the GNU General Public License along
359-# with this program. If not, see <http://www.gnu.org/licenses/>.
360-#
361-# In addition, as a special exception, the copyright holders give
362-# permission to link the code of portions of this program with the
363-# OpenSSL library under certain conditions as described in each
364-# individual source file, and distribute linked combinations
365-# including the two.
366-# You must obey the GNU General Public License in all respects
367-# for all of the code used other than OpenSSL. If you modify
368-# file(s) with this exception, you may extend this exception to your
369-# version of the file(s), but you are not obligated to do so. If you
370-# do not wish to do so, delete this exception statement from your
371-# version. If you delete this exception statement from all source
372-# files in the program, then also delete it here.
373-
374-"""Credentials test code."""
375
376=== removed file 'tests/platform/credentials/test_ipc_service.py'
377--- tests/platform/credentials/test_ipc_service.py 2015-09-17 02:20:40 +0000
378+++ tests/platform/credentials/test_ipc_service.py 1970-01-01 00:00:00 +0000
379@@ -1,328 +0,0 @@
380-# -*- coding: utf-8 -*-
381-#
382-# Author: Alejandro J. Cura <alecu@canonical.com>
383-#
384-# Copyright 2011-2012 Canonical Ltd.
385-#
386-# This program is free software: you can redistribute it and/or modify it
387-# under the terms of the GNU General Public License version 3, as published
388-# by the Free Software Foundation.
389-#
390-# This program is distributed in the hope that it will be useful, but
391-# WITHOUT ANY WARRANTY; without even the implied warranties of
392-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
393-# PURPOSE. See the GNU General Public License for more details.
394-#
395-# You should have received a copy of the GNU General Public License along
396-# with this program. If not, see <http://www.gnu.org/licenses/>.
397-#
398-# In addition, as a special exception, the copyright holders give
399-# permission to link the code of portions of this program with the
400-# OpenSSL library under certain conditions as described in each
401-# individual source file, and distribute linked combinations
402-# including the two.
403-# You must obey the GNU General Public License in all respects
404-# for all of the code used other than OpenSSL. If you modify
405-# file(s) with this exception, you may extend this exception to your
406-# version of the file(s), but you are not obligated to do so. If you
407-# do not wish to do so, delete this exception statement from your
408-# version. If you delete this exception statement from all source
409-# files in the program, then also delete it here.
410-"""Tests for the Magicicada credentials management IPC service."""
411-
412-import logging
413-
414-from twisted.internet import defer
415-from twisted.trial.unittest import TestCase
416-
417-from contrib.testing.testcase import FAKED_CREDENTIALS
418-from ubuntuone.devtools.handlers import MementoHandler
419-from ubuntuone.platform.credentials import APP_NAME
420-from ubuntuone.platform.credentials.ipc_service import (
421- CredentialsManagement,
422- RemovableSignal,
423- logger,
424-)
425-
426-TEST_APP_NAME = "test application"
427-TEST_ERROR_DICT = {}
428-TEST_CREDENTIALS = FAKED_CREDENTIALS
429-
430-
431-class FakeSSOProxy(object):
432- """A fake SSOProxy."""
433-
434- def __init__(self):
435- """Initialize this fake."""
436- signals = [
437- "credentials_stored",
438- "credentials_cleared",
439- "credentials_found",
440- "credentials_not_found",
441- "authorization_denied",
442- "credentials_error",
443- ]
444- for s in signals:
445- handler_name = "on_%s" % s
446- callback_name = "on_%s_cb" % s
447-
448- def make_handler(callback_name):
449- """Create a handler for a given callback_name."""
450-
451- def handler(*args):
452- """A signal was called."""
453- callback = getattr(self, callback_name, None)
454- if callback is not None:
455- callback(*args)
456-
457- return handler
458-
459- setattr(self, handler_name, make_handler(callback_name))
460- setattr(self, callback_name, None)
461-
462-
463- def find_credentials(self, app_name, options):
464- """Ask the U1 credentials."""
465- return defer.succeed(TEST_CREDENTIALS)
466-
467- def clear_credentials(self, app_name, options):
468- """Clear the U1 credentials."""
469- return defer.succeed(None)
470-
471- def store_credentials(self, app_name, options):
472- """Store the U1 credentials."""
473- return defer.succeed(None)
474-
475- def register(self, app_name, options):
476- """Register."""
477- return defer.succeed(None)
478-
479- def login(self, app_name, options):
480- """Login."""
481- return defer.succeed(None)
482-
483- def login_email_password(self, app_name, options):
484- """Login using email and password."""
485- return defer.succeed(None)
486-
487-
488-class RemovableSignalTestCase(TestCase):
489- """Tests for RemovableSignal."""
490-
491- @defer.inlineCallbacks
492- def setUp(self):
493- yield super(RemovableSignalTestCase, self).setUp()
494- self.proxy = FakeSSOProxy()
495-
496- def test_creation(self):
497- """When creating, bind properly to self.proxy."""
498- rs = RemovableSignal(self.proxy, "test", lambda *a: None)
499- self.assertIs(self.proxy.test, rs)
500-
501- def test_dunder_callable(self):
502- """__call__ works as expected."""
503- sample_store = []
504- expected = object()
505- test_cb = lambda res: sample_store.append(res)
506- rs = RemovableSignal(self.proxy, "on_credentials_found_cb", test_cb)
507- rs(APP_NAME, expected)
508- self.assertEqual(sample_store, [expected])
509-
510- def test_callable_does_not_log_args(self):
511- """__call__ does not log its arguments."""
512- self.handler = MementoHandler()
513- self.handler.setLevel(logging.DEBUG)
514- logger.addHandler(self.handler)
515- self.addCleanup(logger.removeHandler, self.handler)
516-
517- secret_token = "secret token!"
518- test_cb = lambda _: None
519- rs = RemovableSignal(self.proxy, "on_credentials_found_cb", test_cb)
520-
521- rs(APP_NAME, {"secret": secret_token})
522- for record in self.handler.records:
523- self.assertNotIn(secret_token, record.message)
524-
525- def test_dunder_filters_other_apps(self):
526- """__call__ filters by app_name."""
527- sample_store = []
528- test_cb = lambda res: sample_store.append(res)
529- rs = RemovableSignal(self.proxy, "on_credentials_found_cb", test_cb)
530- rs('other app name', object())
531- self.assertEqual(sample_store, [])
532-
533- def test_remove(self):
534- """The signal has a .remove that removes the callback."""
535- sample_store = []
536- test_cb = lambda app_name, creds: sample_store.append(creds)
537- rs = RemovableSignal(self.proxy, "on_credentials_found_cb", test_cb)
538- rs.remove()
539- rs(TEST_APP_NAME, TEST_CREDENTIALS)
540- self.assertEqual(len(sample_store), 0)
541-
542-
543-class CredentialsManagementTestCase(TestCase):
544- """Tests for CredentialsManagement."""
545-
546- timeout = 5
547- app_name = APP_NAME
548-
549- @defer.inlineCallbacks
550- def setUp(self):
551- yield super(CredentialsManagementTestCase, self).setUp()
552- self._called = False
553- self.proxy = FakeSSOProxy()
554- self.cm = CredentialsManagement(self.proxy)
555-
556- def _set_called(self, *args, **kwargs):
557- """Helper to keep track calls."""
558- self._called = (args, kwargs)
559-
560- def assert_callback_called(self, expected):
561- """Test that _called helper holds 'expected'."""
562- self.assertEqual(self._called, expected)
563-
564- def test_find_credentials(self):
565- """Test the find_credentials method."""
566- d = defer.Deferred()
567- ok = lambda: d.callback("ok")
568- error = lambda *args: d.errback(args)
569- self.cm.find_credentials(reply_handler=ok, error_handler=error)
570- return d
571-
572- def test_clear_credentials(self):
573- """Test the clear_credentials method."""
574- d = defer.Deferred()
575- ok = lambda: d.callback("ok")
576- error = lambda *args: d.errback(args)
577- self.cm.clear_credentials(reply_handler=ok, error_handler=error)
578- return d
579-
580- def test_store_credentials(self):
581- """Test the store_credentials method."""
582- d = defer.Deferred()
583- ok = lambda: d.callback("ok")
584- error = lambda *args: d.errback(args)
585- self.cm.store_credentials(TEST_CREDENTIALS, reply_handler=ok,
586- error_handler=error)
587- return d
588-
589- def test_register(self):
590- """Test the register method."""
591- d = defer.Deferred()
592- ok = lambda: d.callback("ok")
593- error = lambda *args: d.errback(args)
594- self.cm.register({}, reply_handler=ok, error_handler=error)
595- return d
596-
597- def test_login(self):
598- """Test the login method."""
599- d = defer.Deferred()
600- ok = lambda: d.callback("ok")
601- error = lambda *args: d.errback(args)
602- self.cm.login({}, reply_handler=ok, error_handler=error)
603- return d
604-
605- def test_login_email_password(self):
606- """Test the login_email_password method."""
607- d = defer.Deferred()
608- ok = lambda: d.callback("ok")
609- error = lambda *args: d.errback(args)
610- self.cm.login_email_password({'email': 'foo', 'password': 'bar'},
611- reply_handler=ok, error_handler=error)
612- return d
613-
614- def test_register_to_credentials_found(self):
615- """Test the register_to_credentials_found method."""
616- signal = self.cm.register_to_credentials_found(self._set_called)
617- signal(self.app_name, TEST_CREDENTIALS)
618- self.assert_callback_called(((TEST_CREDENTIALS,), {}))
619-
620- def test_register_to_credentials_not_found(self):
621- """Test the register_to_credentials_not_found method."""
622- signal = self.cm.register_to_credentials_not_found(self._set_called)
623- signal(self.app_name)
624- self.assert_callback_called(((), {}))
625-
626- def test_register_to_credentials_stored(self):
627- """Test the register_to_credentials_stored method."""
628- signal = self.cm.register_to_credentials_stored(self._set_called)
629- signal(self.app_name)
630- self.assert_callback_called(((), {}))
631-
632- def test_register_to_credentials_cleared(self):
633- """Test the register_to_credentials_cleared method."""
634- signal = self.cm.register_to_credentials_cleared(self._set_called)
635- signal(self.app_name)
636- self.assert_callback_called(((), {}))
637-
638- def test_register_to_credentials_error(self):
639- """Test the register_to_credentials_error method."""
640- signal = self.cm.register_to_credentials_error(self._set_called)
641- signal(self.app_name)
642- self.assert_callback_called(((), {}))
643-
644- def test_register_to_authorization_denied(self):
645- """Test the register_to_authorization_denied method."""
646- signal = self.cm.register_to_authorization_denied(self._set_called)
647- signal(self.app_name, TEST_ERROR_DICT)
648- self.assert_callback_called(((TEST_ERROR_DICT,), {}))
649-
650- def _verify_not_called_twice(self, signal_name, *args):
651- """Test that the callback is not called twice."""
652- d = defer.Deferred()
653-
654- def signal_handler(*args):
655- """Fake the behaviour of CredentialsManagementTool."""
656- d.callback(args[0] if len(args) > 0 else None)
657-
658- register = getattr(self.cm, "register_to_" + signal_name)
659- signal = register(signal_handler)
660- proxy_cb = getattr(self.proxy, "on_" + signal_name)
661- proxy_cb(*args)
662- if getattr(signal, "remove", False):
663- signal.remove()
664- proxy_cb(*args)
665-
666- def test_not_called_twice_credentials_stored(self):
667- """Test that on_credentials_stored is not called twice."""
668- self._verify_not_called_twice("credentials_stored")
669-
670- def test_not_called_twice_credentials_cleared(self):
671- """Test that on_credentials_cleared is not called twice."""
672- self._verify_not_called_twice("credentials_cleared")
673-
674- def test_not_called_twice_credentials_found(self):
675- """Test that on_credentials_found is not called twice."""
676- self._verify_not_called_twice("credentials_found", self.app_name,
677- TEST_CREDENTIALS)
678-
679- def test_not_called_twice_credentials_not_found(self):
680- """Test that on_credentials_not_found is not called twice."""
681- self._verify_not_called_twice("credentials_not_found")
682-
683- def test_not_called_twice_authorization_denied(self):
684- """Test that on_authorization_denied is not called twice."""
685- self._verify_not_called_twice("authorization_denied")
686-
687- def test_not_called_twice_credentials_error(self):
688- """Test that on_credentials_error is not called twice."""
689- self._verify_not_called_twice("credentials_error", TEST_ERROR_DICT)
690-
691- def test_connect_to_signal(self):
692- """The connect_to_signal method is correct."""
693- for signal_name in self.cm._SIGNAL_TO_CALLBACK_MAPPING:
694- match = self.cm.connect_to_signal(signal_name, self._set_called)
695- expected = object()
696- match(APP_NAME, expected)
697- self.assertEqual(self._called, ((expected,), {}))
698-
699-
700-class CredentialsManagementOtherAppNameTestCase(CredentialsManagementTestCase):
701- """Tests for CredentialsManagement when the app name differs."""
702-
703- app_name = 'other app name'
704-
705- def assert_callback_called(self, expected):
706- """Test that _called helper does not hold 'expected'."""
707- self.assertEqual(self._called, False)
708
709=== removed file 'tests/platform/credentials/test_linux.py'
710--- tests/platform/credentials/test_linux.py 2015-09-20 20:52:48 +0000
711+++ tests/platform/credentials/test_linux.py 1970-01-01 00:00:00 +0000
712@@ -1,1119 +0,0 @@
713-# -*- coding: utf-8 -*-
714-#
715-# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
716-#
717-# Copyright 2010-2012 Canonical Ltd.
718-#
719-# This program is free software: you can redistribute it and/or modify it
720-# under the terms of the GNU General Public License version 3, as published
721-# by the Free Software Foundation.
722-#
723-# This program is distributed in the hope that it will be useful, but
724-# WITHOUT ANY WARRANTY; without even the implied warranties of
725-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
726-# PURPOSE. See the GNU General Public License for more details.
727-#
728-# You should have received a copy of the GNU General Public License along
729-# with this program. If not, see <http://www.gnu.org/licenses/>.
730-#
731-# In addition, as a special exception, the copyright holders give
732-# permission to link the code of portions of this program with the
733-# OpenSSL library under certain conditions as described in each
734-# individual source file, and distribute linked combinations
735-# including the two.
736-# You must obey the GNU General Public License in all respects
737-# for all of the code used other than OpenSSL. If you modify
738-# file(s) with this exception, you may extend this exception to your
739-# version of the file(s), but you are not obligated to do so. If you
740-# do not wish to do so, delete this exception statement from your
741-# version. If you delete this exception statement from all source
742-# files in the program, then also delete it here.
743-"""Tests for the Magicicada credentials management dbus service."""
744-
745-import logging
746-
747-from functools import wraps
748-
749-import dbus.service
750-
751-from twisted.internet.defer import Deferred, inlineCallbacks
752-from ubuntuone.devtools.handlers import MementoHandler
753-try:
754- from ubuntuone.devtools.testcases import skipTest
755- from ubuntuone.devtools.testcases.dbus import DBusTestCase
756-except ImportError:
757- from ubuntuone.devtools.testcase import DBusTestCase, skipTest
758-
759-from ubuntuone.platform.credentials import (
760- CredentialsError,
761- CredentialsManagementTool,
762- logger,
763- UI_PARAMS,
764-)
765-from ubuntuone.platform.credentials.dbus_service import (
766- APP_NAME,
767- CredentialsManagement,
768- dbus,
769- DBUS_BUS_NAME,
770- DBUS_CREDENTIALS_IFACE,
771- DBUS_CREDENTIALS_PATH,
772- TIMEOUT_INTERVAL,
773- ubuntu_sso,
774-)
775-
776-FAKED_CREDENTIALS = {
777- 'consumer_key': 'faked_consumer_key',
778- 'consumer_secret': 'faked_consumer_secret',
779- 'token': 'faked_token',
780- 'token_secret': 'faked_token_secret',
781- 'token_name': 'Woohoo test',
782-}
783-
784-
785-class FakedSSOService(dbus.service.Object):
786- """Faked DBus object that manages credentials."""
787-
788- error_dict = None
789- app_name = None
790-
791- def __init__(self, *args, **kwargs):
792- super(FakedSSOService, self).__init__(*args, **kwargs)
793- self._credentials = {}
794- self._args = None
795- self._kwargs = None
796-
797- def maybe_emit_error(f):
798- """Decorator to fake a CredentialsError signal."""
799-
800- @wraps(f)
801- def inner(self, *args, **kwargs):
802- """Fake a CredentialsError signal."""
803- if FakedSSOService.error_dict is not None:
804- if 'error_handler' in kwargs:
805- error_handler = kwargs['error_handler']
806- exc = dbus.service.DBusException(FakedSSOService.error_dict)
807- error_handler(exc)
808- else:
809- self.CredentialsError(FakedSSOService.app_name,
810- FakedSSOService.error_dict)
811- else:
812- return f(self, *args, **kwargs)
813-
814- return inner
815-
816- def store_args(f):
817- """Decorator to store arguments to check correct calls."""
818-
819- @wraps(f)
820- def inner(self, app_name, args, **kwargs):
821- """Store arguments to check correct calls."""
822- self._app_name = app_name
823- self._args = args
824- self._kwargs = kwargs
825- return f(self, app_name, args, **kwargs)
826-
827- return inner
828-
829- @dbus.service.signal(ubuntu_sso.DBUS_CREDENTIALS_IFACE, signature='s')
830- def AuthorizationDenied(self, app_name):
831- """Signal thrown when the user denies the authorization."""
832-
833- @dbus.service.signal(ubuntu_sso.DBUS_CREDENTIALS_IFACE, signature='sa{ss}')
834- def CredentialsFound(self, app_name, credentials):
835- """Signal thrown when the credentials are found."""
836-
837- @dbus.service.signal(ubuntu_sso.DBUS_CREDENTIALS_IFACE, signature='s')
838- def CredentialsNotFound(self, app_name):
839- """Signal thrown when the credentials are not found."""
840-
841- @dbus.service.signal(ubuntu_sso.DBUS_CREDENTIALS_IFACE, signature='s')
842- def CredentialsCleared(self, app_name):
843- """Signal thrown when the credentials were cleared."""
844-
845- @dbus.service.signal(ubuntu_sso.DBUS_CREDENTIALS_IFACE, signature='s')
846- def CredentialsStored(self, app_name):
847- """Signal thrown when the credentials were cleared."""
848-
849- @dbus.service.signal(ubuntu_sso.DBUS_CREDENTIALS_IFACE, signature='sa{ss}')
850- def CredentialsError(self, app_name, error_dict):
851- """Signal thrown when there is a problem getting the credentials."""
852-
853- @store_args
854- @maybe_emit_error
855- @dbus.service.method(dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE,
856- in_signature='sa{ss}', out_signature='')
857- def find_credentials(self, app_name, args):
858- """Look for the credentials for an application."""
859- creds = self._credentials.get(FakedSSOService.app_name, None)
860- if creds is not None:
861- self.CredentialsFound(FakedSSOService.app_name, creds)
862- else:
863- self.CredentialsNotFound(FakedSSOService.app_name)
864-
865- @store_args
866- @maybe_emit_error
867- @dbus.service.method(dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE,
868- in_signature='sa{ss}', out_signature='a{ss}',
869- async_callbacks=('reply_handler', 'error_handler'))
870- def find_credentials_sync(self, app_name, args,
871- reply_handler=None, error_handler=None):
872- """Look for the credentials for an application."""
873- creds = self._credentials.get(FakedSSOService.app_name, None)
874- if creds is None:
875- creds = {}
876- if reply_handler is not None:
877- reply_handler(creds)
878- else:
879- return creds
880-
881- @store_args
882- @maybe_emit_error
883- @dbus.service.method(dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE,
884- in_signature='sa{ss}', out_signature='')
885- def clear_credentials(self, app_name, args):
886- """Clear the credentials for an application."""
887- self._credentials.pop(FakedSSOService.app_name, None)
888- self.CredentialsCleared(FakedSSOService.app_name)
889-
890- @store_args
891- @maybe_emit_error
892- @dbus.service.method(dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE,
893- in_signature='sa{ss}', out_signature='')
894- def store_credentials(self, app_name, args):
895- """Store the token for an application."""
896- self._credentials[FakedSSOService.app_name] = args
897- self.CredentialsStored(FakedSSOService.app_name)
898-
899- @store_args
900- @maybe_emit_error
901- @dbus.service.method(dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE,
902- in_signature='sa{ss}', out_signature='')
903- def register(self, app_name, args):
904- """Get credentials if found else prompt GUI to register."""
905- creds = self._credentials.get(FakedSSOService.app_name, None)
906- if creds is not None and len(creds) > 0:
907- self.CredentialsFound(FakedSSOService.app_name, creds)
908- elif creds == {}:
909- # fake an AuthorizationDenied
910- self.AuthorizationDenied(FakedSSOService.app_name)
911- elif creds is None:
912- # fake the adding of the credentials, in reality this will bring
913- # a GUI that the user will interact with.
914- self._credentials[FakedSSOService.app_name] = FAKED_CREDENTIALS
915- self.CredentialsFound(FakedSSOService.app_name, FAKED_CREDENTIALS)
916-
917- @store_args
918- @maybe_emit_error
919- @dbus.service.method(dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE,
920- in_signature='sa{ss}', out_signature='')
921- def login(self, app_name, args):
922- """Get credentials if found else prompt GUI to login."""
923- self.register(app_name, args)
924-
925-
926-class BaseTestCase(DBusTestCase):
927- """Base test case."""
928-
929- timeout = 8
930- app_name = APP_NAME
931- error_dict = None
932-
933- @inlineCallbacks
934- def setUp(self):
935- yield super(BaseTestCase, self).setUp()
936- FakedSSOService.app_name = self.app_name
937- FakedSSOService.error_dict = self.error_dict
938-
939- self.memento = MementoHandler()
940- self.memento.setLevel(logging.DEBUG)
941- logger.addHandler(self.memento)
942-
943- self.sso_server = self.register_server(ubuntu_sso.DBUS_BUS_NAME,
944- ubuntu_sso.DBUS_CREDENTIALS_PATH,
945- FakedSSOService) # faked SSO server
946- self.args = {'window_id': '803'}
947-
948- def register_server(self, bus_name, object_path, service_class):
949- """Register a service on the session bus."""
950- name = self.bus.request_name(bus_name, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
951- self.assertNotEqual(name, dbus.bus.REQUEST_NAME_REPLY_EXISTS,
952- 'Service %s should not be running.' % bus_name)
953- mock = service_class(object_path=object_path, conn=self.bus)
954- self.addCleanup(mock.remove_from_connection)
955- self.addCleanup(self.bus.release_name, bus_name)
956-
957- return mock
958-
959- def get_proxy(self, bus_name, object_path, dbus_interface):
960- obj = self.bus.get_object(bus_name=bus_name, object_path=object_path,
961- follow_name_owner_changes=True)
962- proxy = dbus.Interface(object=obj, dbus_interface=dbus_interface)
963- return proxy
964-
965- def get_sso_proxy(self):
966- return self.get_proxy(bus_name=ubuntu_sso.DBUS_BUS_NAME,
967- object_path=ubuntu_sso.DBUS_CREDENTIALS_PATH,
968- dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE)
969-
970-
971-class CredentialsManagementTestCase(BaseTestCase):
972- """Test case for the DBus object that manages Magicicada credentials."""
973-
974- signals = ('CredentialsFound', 'CredentialsNotFound', 'CredentialsCleared',
975- 'CredentialsStored', 'CredentialsError', 'AuthorizationDenied')
976-
977- @inlineCallbacks
978- def setUp(self):
979- yield super(CredentialsManagementTestCase, self).setUp()
980- self.creds_server = self.register_server(DBUS_BUS_NAME,
981- DBUS_CREDENTIALS_PATH,
982- CredentialsManagement) # real service
983-
984- self.deferred = Deferred()
985- self.proxy = self.get_creds_proxy()
986-
987- def get_creds_proxy(self):
988- return self.get_proxy(bus_name=DBUS_BUS_NAME,
989- object_path=DBUS_CREDENTIALS_PATH,
990- dbus_interface=DBUS_CREDENTIALS_IFACE)
991-
992- def connect_signals(self, callback=None):
993- """Connect every signal accordingly to fire self.deferred.
994-
995- If 'callback' is not None, it will be used as a tuple (sig_name,
996- function) and 'sig_name' will be connected to 'function', which should
997- fire self.deferred properly.
998-
999- """
1000- success_sig_name, success_function = None, None
1001- if callback is not None:
1002- success_sig_name, success_function = callback
1003-
1004- def fail(sig_name):
1005- """Decorator to fire self.deferred."""
1006- def inner(*args, **kwargs):
1007- """Fire self.deferred."""
1008- msg = 'Received an unexpected signal (%r).' % sig_name
1009- self.deferred.errback(TypeError(msg))
1010- return inner
1011-
1012- for sig_name in self.signals:
1013- if sig_name == success_sig_name:
1014- sig = self.proxy.connect_to_signal(sig_name, success_function)
1015- else:
1016- sig = self.proxy.connect_to_signal(sig_name, fail(sig_name))
1017- self.addCleanup(sig.remove)
1018-
1019- @inlineCallbacks
1020- def add_credentials(self, creds=FAKED_CREDENTIALS):
1021- """Add some fake credentials for 'self.app_name'."""
1022- d = Deferred()
1023- sso_proxy = self.get_sso_proxy()
1024- sso_proxy.store_credentials(self.app_name, creds,
1025- reply_handler=lambda: d.callback(None),
1026- error_handler=d.errback)
1027- yield d
1028-
1029- @inlineCallbacks
1030- def do_test(self):
1031- """Perform the test itself."""
1032- yield self.deferred
1033-
1034- def test_get_sso_proxy(self):
1035- """The SSO dbus proxy is properly retrieved."""
1036- sso_proxy = CredentialsManagement().sso_proxy
1037- self.assertEqual(sso_proxy.bus_name, ubuntu_sso.DBUS_BUS_NAME)
1038- self.assertEqual(sso_proxy.object_path,
1039- ubuntu_sso.DBUS_CREDENTIALS_PATH)
1040- self.assertEqual(sso_proxy.dbus_interface,
1041- ubuntu_sso.DBUS_CREDENTIALS_IFACE)
1042-
1043-
1044-class ArgsTestCase(CredentialsManagementTestCase):
1045- """Test case to check that proper arguments are passed to SSO backend."""
1046-
1047- @inlineCallbacks
1048- def test_find_credentials(self):
1049- """The find_credentials method calls ubuntu_sso's method."""
1050- d = Deferred()
1051- self.proxy.find_credentials(reply_handler=lambda: d.callback(None),
1052- error_handler=d.errback)
1053- yield d
1054-
1055- self.assertEqual(self.sso_server._app_name, APP_NAME)
1056- self.assertEqual(self.sso_server._args, {})
1057-
1058- @inlineCallbacks
1059- def test_find_credentials_sync(self):
1060- """The find_credentials_sync method calls ubuntu_sso's method."""
1061- d = Deferred()
1062- self.proxy.find_credentials_sync(reply_handler=d.callback,
1063- error_handler=lambda *a: d.errback(a))
1064- yield d
1065-
1066- self.assertEqual(self.sso_server._app_name, APP_NAME)
1067- self.assertEqual(self.sso_server._args, {})
1068- self.assertTrue('reply_handler' in self.sso_server._kwargs)
1069- self.assertTrue('error_handler' in self.sso_server._kwargs)
1070-
1071- @inlineCallbacks
1072- def test_clear_credentials(self):
1073- """The clear_credentials method calls ubuntu_sso's method."""
1074- d = Deferred()
1075- self.proxy.clear_credentials(reply_handler=lambda: d.callback(None),
1076- error_handler=d.errback)
1077- yield d
1078-
1079- self.assertEqual(self.sso_server._app_name, APP_NAME)
1080- self.assertEqual(self.sso_server._args, {})
1081-
1082- @inlineCallbacks
1083- def test_store_credentials(self):
1084- """The store_credentials method calls ubuntu_sso's method."""
1085- d = Deferred()
1086- self.proxy.store_credentials(FAKED_CREDENTIALS,
1087- reply_handler=lambda: d.callback(None),
1088- error_handler=d.errback)
1089- yield d
1090-
1091- self.assertEqual(self.sso_server._app_name, APP_NAME)
1092- self.assertEqual(self.sso_server._args, FAKED_CREDENTIALS)
1093-
1094- @inlineCallbacks
1095- def test_register(self):
1096- """The register method calls ubuntu_sso's method."""
1097- d = Deferred()
1098- self.proxy.register(self.args, reply_handler=lambda: d.callback(None),
1099- error_handler=d.errback)
1100- yield d
1101-
1102- self.assertEqual(self.sso_server._app_name, APP_NAME)
1103- # convert to unicode for the comparison, as the message is arriving
1104- # here as bytes (bad gettext usage!)
1105- params = dict((x, y.decode("utf8") if isinstance(y, str) else y)
1106- for x, y in UI_PARAMS.items())
1107- params.update(self.args)
1108- self.assertEqual(self.sso_server._args, params)
1109-
1110- @inlineCallbacks
1111- def test_login(self):
1112- """The login method calls ubuntu_sso's method."""
1113- d = Deferred()
1114- self.proxy.login(self.args, reply_handler=lambda: d.callback(None),
1115- error_handler=d.errback)
1116- yield d
1117-
1118- self.assertEqual(self.sso_server._app_name, APP_NAME)
1119- # convert to unicode for the comparison, as the message is arriving
1120- # here as bytes (bad gettext usage!)
1121- params = dict((x, y.decode("utf8") if isinstance(y, str) else y)
1122- for x, y in UI_PARAMS.items())
1123- params.update(self.args)
1124- self.assertEqual(self.sso_server._args, params)
1125-
1126-
1127-class DictSignatureTestCase(DBusTestCase):
1128- """Test the errors with dict signatures."""
1129-
1130- def verify(self, app_name, options_dict, reply_handler, error_handler):
1131- """Verify that the options_dict is a dbus.Dictionary."""
1132- self.assertIsInstance(options_dict, dbus.Dictionary)
1133-
1134- def test_find_credentials_dict_signature(self):
1135- """Test for find_credentials."""
1136- creds_man = CredentialsManagement()
1137- self.patch(creds_man.sso_proxy, "find_credentials", self.verify)
1138- creds_man.find_credentials()
1139-
1140- def test_find_credentials_sync_dict_signature(self):
1141- """Test for find_credentials_sync."""
1142- creds_man = CredentialsManagement()
1143- self.patch(creds_man.sso_proxy, "find_credentials_sync", self.verify)
1144- creds_man.find_credentials_sync()
1145-
1146- def test_clear_credentials_dict_signature(self):
1147- """Test for clear_credentials."""
1148- creds_man = CredentialsManagement()
1149- self.patch(creds_man.sso_proxy, "clear_credentials", self.verify)
1150- creds_man.clear_credentials()
1151-
1152-
1153-class SameAppNoErrorTestCase(CredentialsManagementTestCase):
1154- """Test case when the app_name matches APP_NAME and there was no error."""
1155-
1156- @inlineCallbacks
1157- def test_find_credentials(self):
1158- """The find_credentials method calls ubuntu_sso's method."""
1159- d = Deferred()
1160- yield self.add_credentials()
1161-
1162- def verify(credentials):
1163- """Do the check."""
1164- self.assertEqual(credentials, FAKED_CREDENTIALS)
1165- self.deferred.callback(None)
1166-
1167- self.connect_signals(callback=('CredentialsFound', verify))
1168-
1169- self.proxy.find_credentials(reply_handler=lambda: d.callback(None),
1170- error_handler=d.errback)
1171- yield d
1172- yield self.do_test()
1173-
1174- @inlineCallbacks
1175- def test_find_credentials_sync(self):
1176- """The find_credentials_sync method calls ubuntu_sso's method."""
1177- yield self.add_credentials()
1178-
1179- def verify(credentials):
1180- """Do the check."""
1181- self.assertEqual(credentials, FAKED_CREDENTIALS)
1182- self.deferred.callback(None)
1183-
1184- error_handler = lambda a: self.deferred.errback(a)
1185- self.proxy.find_credentials_sync(reply_handler=verify,
1186- error_handler=error_handler)
1187- yield self.do_test()
1188-
1189- @inlineCallbacks
1190- def test_find_credentials_without_credentials(self):
1191- """The find_credentials method calls ubuntu_sso's method."""
1192- d = Deferred()
1193-
1194- def verify():
1195- """Do the check."""
1196- self.deferred.callback(None)
1197-
1198- self.connect_signals(callback=('CredentialsNotFound', verify))
1199-
1200- self.proxy.find_credentials(reply_handler=lambda: d.callback(None),
1201- error_handler=d.errback)
1202- yield d
1203- yield self.do_test()
1204-
1205- @inlineCallbacks
1206- def test_clear_credentials(self):
1207- """The clear_credentials method calls ubuntu_sso's method."""
1208- d = Deferred()
1209- yield self.add_credentials()
1210-
1211- def verify():
1212- """Do the check."""
1213- self.deferred.callback(None)
1214-
1215- self.connect_signals(callback=('CredentialsCleared', verify))
1216-
1217- self.proxy.clear_credentials(reply_handler=lambda: d.callback(None),
1218- error_handler=d.errback)
1219- yield d
1220- yield self.do_test()
1221-
1222- @inlineCallbacks
1223- def test_clear_credentials_without_credentials(self):
1224- """The clear_credentials method calls ubuntu_sso's method."""
1225- d = Deferred()
1226-
1227- def verify():
1228- """Do the check."""
1229- self.deferred.callback(None)
1230-
1231- self.connect_signals(callback=('CredentialsCleared', verify))
1232-
1233- self.proxy.clear_credentials(reply_handler=lambda: d.callback(None),
1234- error_handler=d.errback)
1235- yield d
1236- yield self.do_test()
1237-
1238- @inlineCallbacks
1239- def test_store_credentials(self):
1240- """The store_credentials method calls ubuntu_sso's method."""
1241- d = Deferred()
1242-
1243- def verify():
1244- """Do the check."""
1245- self.deferred.callback(None)
1246-
1247- self.connect_signals(callback=('CredentialsStored', verify))
1248-
1249- self.proxy.store_credentials(FAKED_CREDENTIALS,
1250- reply_handler=lambda: d.callback(None),
1251- error_handler=d.errback)
1252- yield d
1253- yield self.do_test()
1254-
1255- @inlineCallbacks
1256- def test_register_with_credentials(self):
1257- """The register method calls ubuntu_sso's method."""
1258- d = Deferred()
1259- yield self.add_credentials()
1260-
1261- def verify(credentials):
1262- """Do the check."""
1263- self.assertEqual(credentials, FAKED_CREDENTIALS)
1264- self.deferred.callback(None)
1265-
1266- self.connect_signals(callback=('CredentialsFound', verify))
1267-
1268- self.proxy.register(self.args, reply_handler=lambda: d.callback(None),
1269- error_handler=d.errback)
1270- yield d
1271- yield self.do_test()
1272-
1273- @inlineCallbacks
1274- def test_register_without_credentials(self):
1275- """The register method calls ubuntu_sso's method."""
1276- d = Deferred()
1277-
1278- def verify(credentials):
1279- """Do the check."""
1280- self.assertEqual(credentials, FAKED_CREDENTIALS)
1281- self.deferred.callback(None)
1282-
1283- self.connect_signals(callback=('CredentialsFound', verify))
1284-
1285- self.proxy.register(self.args, reply_handler=lambda: d.callback(None),
1286- error_handler=d.errback)
1287- yield d
1288- yield self.do_test()
1289-
1290- @inlineCallbacks
1291- def test_register_authorization_denied(self):
1292- """The register method calls ubuntu_sso's method."""
1293- d = Deferred()
1294- yield self.add_credentials(creds={})
1295-
1296- def verify():
1297- """Do the check."""
1298- self.deferred.callback(None)
1299-
1300- self.connect_signals(callback=('AuthorizationDenied', verify))
1301-
1302- self.proxy.register(self.args, reply_handler=lambda: d.callback(None),
1303- error_handler=d.errback)
1304- yield d
1305- yield self.do_test()
1306-
1307- @inlineCallbacks
1308- def test_login_with_credentials(self):
1309- """The login method calls ubuntu_sso's method."""
1310- d = Deferred()
1311- yield self.add_credentials()
1312-
1313- def verify(credentials):
1314- """Do the check."""
1315- self.assertEqual(credentials, FAKED_CREDENTIALS)
1316- self.deferred.callback(None)
1317-
1318- self.connect_signals(callback=('CredentialsFound', verify))
1319-
1320- self.proxy.login(self.args, reply_handler=lambda: d.callback(None),
1321- error_handler=d.errback)
1322- yield d
1323- yield self.do_test()
1324-
1325- @inlineCallbacks
1326- def test_login_without_credentials(self):
1327- """The login method calls ubuntu_sso's method."""
1328- d = Deferred()
1329-
1330- def verify(credentials):
1331- """Do the check."""
1332- self.assertEqual(credentials, FAKED_CREDENTIALS)
1333- self.deferred.callback(None)
1334-
1335- self.connect_signals(callback=('CredentialsFound', verify))
1336-
1337- self.proxy.login(self.args, reply_handler=lambda: d.callback(None),
1338- error_handler=d.errback)
1339- yield d
1340- yield self.do_test()
1341-
1342- @inlineCallbacks
1343- def test_login_authorization_denied(self):
1344- """The login method calls ubuntu_sso's method."""
1345- d = Deferred()
1346- yield self.add_credentials(creds={})
1347-
1348- def verify():
1349- """Do the check."""
1350- self.deferred.callback(None)
1351-
1352- self.connect_signals(callback=('AuthorizationDenied', verify))
1353-
1354- self.proxy.login(self.args, reply_handler=lambda: d.callback(None),
1355- error_handler=d.errback)
1356- yield d
1357- yield self.do_test()
1358-
1359-
1360-class SameAppWithErrorTestCase(SameAppNoErrorTestCase):
1361- """Test case when the app_name matches APP_NAME and there was an error."""
1362-
1363- error_dict = {'error_type': 'Test'}
1364-
1365- def connect_signals(self, callback=None):
1366- """CredentialsError is the success signals in this suite."""
1367-
1368- def verify(error_dict):
1369- """Do the check."""
1370- self.assertEqual(error_dict, self.error_dict)
1371- self.deferred.callback(error_dict)
1372-
1373- args = ('CredentialsError', verify)
1374- super(SameAppWithErrorTestCase, self).connect_signals(callback=args)
1375-
1376- @skipTest('Failing on Ubuntu 13.04 - bug #1085204')
1377- @inlineCallbacks
1378- def test_find_credentials_sync(self):
1379- """The find_credentials_sync method calls ubuntu_sso's method."""
1380-
1381- def verify(error):
1382- """Do the check."""
1383- try:
1384- self.assertEqual(error.args[0], str(self.error_dict))
1385- except Exception, e:
1386- self.deferred.errback(e)
1387- else:
1388- self.deferred.callback(None)
1389-
1390- self.proxy.find_credentials_sync(reply_handler=self.deferred.errback,
1391- error_handler=verify)
1392- yield self.do_test()
1393-
1394-
1395-class OtherAppNoErrorTestCase(SameAppNoErrorTestCase):
1396- """Test case when the app_name is not APP_NAME and there was no error."""
1397-
1398- app_name = APP_NAME * 2
1399-
1400- def connect_signals(self, callback=None):
1401- """No signal should be received in this suite."""
1402- # ignore all success connection, self.deferred should always errback
1403- super(OtherAppNoErrorTestCase, self).connect_signals(callback=None)
1404-
1405- @inlineCallbacks
1406- def do_test(self):
1407- """Perform the test itself."""
1408- if not self.deferred.called:
1409- msg = 'does not match %r, exiting.' % APP_NAME
1410- if self.memento.check_info(self.app_name, msg):
1411- self.deferred.callback(None)
1412- else:
1413- self.deferred.errback('Log should be present.')
1414-
1415- yield self.deferred
1416-
1417-
1418-class OtherAppWithErrorTestCase(OtherAppNoErrorTestCase):
1419- """Test case when the app_name is not APP_NAME and there was an error."""
1420-
1421- app_name = APP_NAME * 2
1422- error_dict = {'error_type': 'Test'}
1423-
1424- def test_find_credentials_sync(self):
1425- """This test has no sense for a synchronous method."""
1426-
1427-
1428-class RefCountingTestCase(BaseTestCase):
1429- """Tests for the CredentialsManagement ref counting."""
1430-
1431- @inlineCallbacks
1432- def setUp(self):
1433- yield super(RefCountingTestCase, self).setUp()
1434- self._called = False
1435- self.client = CredentialsManagement()
1436-
1437- def _set_called(self, *args, **kwargs):
1438- """Keep track of method calls."""
1439- self._called = (args, kwargs)
1440-
1441- def test_ref_counting(self):
1442- """Ref counting is in place."""
1443- self.assertEqual(self.client.ref_count, 0)
1444-
1445- def test_find_credentials(self):
1446- """Keep proper track of on going requests."""
1447- d = Deferred()
1448-
1449- def verify(*args):
1450- """Make the check."""
1451- self.assertEqual(self.client.ref_count, 1)
1452- d.callback(True)
1453-
1454- self.patch(self.client, 'CredentialsNotFound', verify)
1455- self.client.find_credentials()
1456-
1457- return d
1458-
1459- def test_find_credentials_sync(self):
1460- """Keep proper track of on going requests."""
1461- d = Deferred()
1462-
1463- def verify(*args):
1464- """Make the check."""
1465- self.assertEqual(self.client.ref_count, 1)
1466- d.callback(True)
1467-
1468- self.client.find_credentials_sync(reply_handler=verify,
1469- error_handler=d.errback)
1470- return d
1471-
1472- def test_find_credentials_sync_error(self):
1473- """Keep proper track of on going requests."""
1474- d = Deferred()
1475-
1476- def verify(*args):
1477- """Make the check."""
1478- self.assertEqual(self.client.ref_count, 1)
1479- d.callback(True)
1480-
1481- self.patch(FakedSSOService, 'error_dict', 'foo')
1482- self.client.find_credentials_sync(reply_handler=d.errback,
1483- error_handler=verify)
1484-
1485- return d
1486-
1487- def test_clear_credentials(self):
1488- """Keep proper track of on going requests."""
1489- d = Deferred()
1490-
1491- def verify(*args):
1492- """Make the check."""
1493- self.assertEqual(self.client.ref_count, 1)
1494- d.callback(True)
1495-
1496- self.patch(self.client, 'CredentialsCleared', verify)
1497- self.client.clear_credentials()
1498-
1499- return d
1500-
1501- def test_store_credentials(self):
1502- """Keep proper track of on going requests."""
1503- d = Deferred()
1504-
1505- def verify(*args):
1506- """Make the check."""
1507- self.assertEqual(self.client.ref_count, 1)
1508- d.callback(True)
1509-
1510- self.patch(self.client, 'CredentialsStored', verify)
1511- self.client.store_credentials(self.args)
1512-
1513- return d
1514-
1515- def test_register(self):
1516- """Keep proper track of on going requests."""
1517- d = Deferred()
1518-
1519- def verify(*args):
1520- """Make the check."""
1521- self.assertEqual(self.client.ref_count, 1)
1522- d.callback(True)
1523-
1524- self.patch(self.client, 'CredentialsFound', verify)
1525- self.client.register(self.args)
1526-
1527- return d
1528-
1529- def test_login(self):
1530- """Keep proper track of on going requests."""
1531- d = Deferred()
1532-
1533- def verify(*args):
1534- """Make the check."""
1535- self.assertEqual(self.client.ref_count, 1)
1536- d.callback(True)
1537-
1538- self.patch(self.client, 'CredentialsFound', verify)
1539- self.client.login(self.args)
1540-
1541- return d
1542-
1543- def test_several_requests(self):
1544- """Requests can be nested."""
1545- d = Deferred()
1546-
1547- self.ref_count = 0
1548-
1549- def parallel_counter(*args):
1550- """Make the check."""
1551- self.ref_count += 1
1552- if self.ref_count == 5:
1553- self.assertEqual(self.client.ref_count, self.ref_count)
1554- d.callback(True)
1555-
1556- self.patch(self.client, 'CredentialsFound', parallel_counter)
1557-
1558- self.client.login(self.args)
1559- self.client.register(self.args)
1560- self.client.login(self.args)
1561- self.client.register(self.args)
1562- self.client.register(self.args)
1563-
1564- return d
1565-
1566- def test_credentials_found(self):
1567- """Ref counter is decreased when a signal is sent."""
1568- self.client.ref_count = 3
1569- self.client.CredentialsFound(FAKED_CREDENTIALS)
1570-
1571- self.assertEqual(self.client.ref_count, 2)
1572-
1573- def test_credentials_not_found(self):
1574- """Ref counter is decreased when a signal is sent."""
1575- self.client.ref_count = 3
1576- self.client.CredentialsNotFound()
1577-
1578- self.assertEqual(self.client.ref_count, 2)
1579-
1580- def test_credentials_cleared(self):
1581- """Ref counter is decreased when a signal is sent."""
1582- self.client.ref_count = 3
1583- self.client.CredentialsCleared()
1584-
1585- self.assertEqual(self.client.ref_count, 2)
1586-
1587- def test_credentials_stored(self):
1588- """Ref counter is decreased when a signal is sent."""
1589- self.client.ref_count = 3
1590- self.client.CredentialsStored()
1591-
1592- self.assertEqual(self.client.ref_count, 2)
1593-
1594- def test_credentials_error(self):
1595- """Ref counter is decreased when a signal is sent."""
1596- self.client.ref_count = 3
1597- self.client.CredentialsError({'error_type': 'test'})
1598-
1599- self.assertEqual(self.client.ref_count, 2)
1600-
1601- def test_authorization_denied(self):
1602- """Ref counter is decreased when a signal is sent."""
1603- self.client.ref_count = 3
1604- self.client.AuthorizationDenied()
1605-
1606- self.assertEqual(self.client.ref_count, 2)
1607-
1608- def test_credentials_found_when_ref_count_is_not_positive(self):
1609- """Ref counter is decreased when a signal is sent."""
1610- self.client._ref_count = -3
1611- self.client.CredentialsFound(FAKED_CREDENTIALS)
1612-
1613- self.assertEqual(self.client.ref_count, 0)
1614- msg = 'Attempting to decrease ref_count to a negative value (-4).'
1615- self.assertTrue(self.memento.check_warning(msg))
1616-
1617- def test_credentials_not_found_when_ref_count_is_not_positive(self):
1618- """Ref counter is decreased when a signal is sent."""
1619- self.client._ref_count = -3
1620- self.client.CredentialsNotFound()
1621-
1622- self.assertEqual(self.client.ref_count, 0)
1623- msg = 'Attempting to decrease ref_count to a negative value (-4).'
1624- self.assertTrue(self.memento.check_warning(msg))
1625-
1626- def test_credentials_cleared_when_ref_count_is_not_positive(self):
1627- """Ref counter is decreased when a signal is sent."""
1628- self.client._ref_count = -3
1629- self.client.CredentialsCleared()
1630-
1631- self.assertEqual(self.client.ref_count, 0)
1632- msg = 'Attempting to decrease ref_count to a negative value (-4).'
1633- self.assertTrue(self.memento.check_warning(msg))
1634-
1635- def test_credentials_stored_when_ref_count_is_not_positive(self):
1636- """Ref counter is decreased when a signal is sent."""
1637- self.client._ref_count = -3
1638- self.client.CredentialsStored()
1639-
1640- self.assertEqual(self.client.ref_count, 0)
1641- msg = 'Attempting to decrease ref_count to a negative value (-4).'
1642- self.assertTrue(self.memento.check_warning(msg))
1643-
1644- def test_credentials_error_when_ref_count_is_not_positive(self):
1645- """Ref counter is decreased when a signal is sent."""
1646- self.client._ref_count = -3
1647- self.client.CredentialsError({'error_type': 'test'})
1648-
1649- self.assertEqual(self.client.ref_count, 0)
1650- msg = 'Attempting to decrease ref_count to a negative value (-4).'
1651- self.assertTrue(self.memento.check_warning(msg))
1652-
1653- def test_autorization_denied_when_ref_count_is_not_positive(self):
1654- """Ref counter is decreased when a signal is sent."""
1655- self.client._ref_count = -3
1656- self.client.AuthorizationDenied()
1657-
1658- self.assertEqual(self.client.ref_count, 0)
1659- msg = 'Attempting to decrease ref_count to a negative value (-4).'
1660- self.assertTrue(self.memento.check_warning(msg))
1661-
1662- def test_on_zero_ref_count_shutdown(self):
1663- """When ref count reaches 0, queue shutdown op."""
1664- self.client.timeout_func = self._set_called
1665- self.client.login(self.args)
1666- self.client.CredentialsFound(FAKED_CREDENTIALS)
1667-
1668- self.assertEqual(self._called,
1669- ((TIMEOUT_INTERVAL, self.client.shutdown), {}))
1670-
1671- def test_on_non_zero_ref_count_do_not_shutdown(self):
1672- """If ref count is not 0, do not queue shutdown op."""
1673- self.client.timeout_func = self._set_called
1674- self.client.login(self.args)
1675-
1676- self.assertEqual(self._called, False)
1677-
1678- def test_on_non_zero_ref_count_after_zero_do_not_shutdown(self):
1679- """If the shutdown was queued, do not quit if counter is not zero."""
1680-
1681- def fake_timeout_func(interval, func):
1682- """Start a new request when the timer is started."""
1683- self.client._ref_count = 1
1684- assert self.client.ref_count > 0
1685- func()
1686-
1687- self.client.timeout_func = fake_timeout_func
1688- self.client.shutdown_func = self._set_called
1689-
1690- self.client.ref_count = 0 # trigger timer and possible shutdown
1691-
1692- self.assertEqual(self._called, False, 'shutdown_func was not called')
1693-
1694- def test_zero_ref_count_after_zero_do_shutdown(self):
1695- """If the shutdown was queued, do quit if counter is zero."""
1696-
1697- def fake_timeout_func(interval, func):
1698- """Start a new request when the timer is started."""
1699- assert self.client.ref_count == 0
1700- func()
1701-
1702- self.client.timeout_func = fake_timeout_func
1703- self.client.shutdown_func = self._set_called
1704-
1705- self.client.ref_count = 0 # trigger timer and possible shutdown
1706-
1707- self.assertEqual(self._called, ((), {}), 'shutdown_func was called')
1708-
1709-
1710-class CredentialsTestCase(BaseTestCase):
1711- """Test suite for the Credentials class."""
1712-
1713- @inlineCallbacks
1714- def setUp(self):
1715- yield super(CredentialsTestCase, self).setUp()
1716- FakedSSOService.error_dict = None
1717- self.creds_server = self.register_server(DBUS_BUS_NAME,
1718- DBUS_CREDENTIALS_PATH,
1719- CredentialsManagement) # real service
1720- self.client = CredentialsManagementTool()
1721-
1722- @inlineCallbacks
1723- def test_find_credentials_no_credentials(self):
1724- """Find credentials when credentials does not exist."""
1725- result = yield self.client.find_credentials()
1726-
1727- self.assertEqual(result, {})
1728-
1729- @inlineCallbacks
1730- def test_find_credentials_with_credentials(self):
1731- """Find credentials when credentials exists."""
1732- yield self.client.store_credentials(FAKED_CREDENTIALS)
1733-
1734- result = yield self.client.find_credentials()
1735-
1736- self.assertEqual(result, FAKED_CREDENTIALS)
1737-
1738- @inlineCallbacks
1739- def test_find_credentials_error(self):
1740- """Find credentials and error."""
1741- FakedSSOService.error_dict = {'failure': 'really'}
1742-
1743- e = yield self.assertFailure(self.client.find_credentials(),
1744- CredentialsError)
1745- self.assertEqual(e[0], FakedSSOService.error_dict)
1746-
1747- @inlineCallbacks
1748- def test_clear_credentials(self):
1749- """Clear credentials."""
1750- yield self.client.store_credentials({'test': 'me'})
1751- yield self.client.clear_credentials()
1752-
1753- result = yield self.client.find_credentials()
1754-
1755- self.assertEqual(result, {})
1756-
1757- @inlineCallbacks
1758- def test_clear_credentials_error(self):
1759- """Clear credentials and error."""
1760- FakedSSOService.error_dict = {'failure': 'really'}
1761-
1762- e = yield self.assertFailure(self.client.clear_credentials(),
1763- CredentialsError)
1764- self.assertEqual(e[0], FakedSSOService.error_dict)
1765-
1766- @inlineCallbacks
1767- def test_store_credentials(self):
1768- """Store credentials."""
1769- token = {'test': 'me'}
1770- yield self.client.store_credentials(token)
1771-
1772- result = yield self.client.find_credentials()
1773-
1774- self.assertEqual(result, token)
1775-
1776- @inlineCallbacks
1777- def test_store_credentials_error(self):
1778- """Store credentials and error."""
1779- FakedSSOService.error_dict = {'failure': 'really'}
1780-
1781- e = yield self.assertFailure(self.client.store_credentials({'0': '1'}),
1782- CredentialsError)
1783- self.assertEqual(e[0], FakedSSOService.error_dict)
1784-
1785- @inlineCallbacks
1786- def test_register(self):
1787- """Register."""
1788- result = yield self.client.register()
1789-
1790- self.assertEqual(result, FAKED_CREDENTIALS)
1791-
1792- @inlineCallbacks
1793- def test_register_auth_denied(self):
1794- """Register and auth_denied."""
1795- yield self.client.store_credentials({}) # trigger AuthorizationDenied
1796- result = yield self.client.register()
1797-
1798- self.assertEqual(result, None)
1799-
1800- @inlineCallbacks
1801- def test_register_error(self):
1802- """Register and error."""
1803- FakedSSOService.error_dict = {'failure': 'really'}
1804-
1805- e = yield self.assertFailure(self.client.register(),
1806- CredentialsError)
1807- self.assertEqual(e[0], FakedSSOService.error_dict)
1808-
1809- @inlineCallbacks
1810- def test_login(self):
1811- """Login."""
1812- result = yield self.client.login()
1813-
1814- self.assertEqual(result, FAKED_CREDENTIALS)
1815-
1816- @inlineCallbacks
1817- def test_login_auth_denied(self):
1818- """Login and auth denied."""
1819- yield self.client.store_credentials({}) # trigger AuthorizationDenied
1820- result = yield self.client.login()
1821-
1822- self.assertEqual(result, None)
1823-
1824- @inlineCallbacks
1825- def test_login_error(self):
1826- """Login and error."""
1827- FakedSSOService.error_dict = {'failure': 'really'}
1828-
1829- e = yield self.assertFailure(self.client.login(),
1830- CredentialsError)
1831- self.assertEqual(e[0], FakedSSOService.error_dict)
1832
1833=== removed file 'tests/platform/test_credentials.py'
1834--- tests/platform/test_credentials.py 2015-09-29 21:05:26 +0000
1835+++ tests/platform/test_credentials.py 1970-01-01 00:00:00 +0000
1836@@ -1,502 +0,0 @@
1837-# -*- coding: utf-8 -*-
1838-#
1839-# Authors: Manuel de la Pena <manuel@canonical.com>
1840-# Alejandro J. Cura <alecu@canonical.com>
1841-#
1842-# Copyright 2011-2012 Canonical Ltd.
1843-#
1844-# This program is free software: you can redistribute it and/or modify it
1845-# under the terms of the GNU General Public License version 3, as published
1846-# by the Free Software Foundation.
1847-#
1848-# This program is distributed in the hope that it will be useful, but
1849-# WITHOUT ANY WARRANTY; without even the implied warranties of
1850-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1851-# PURPOSE. See the GNU General Public License for more details.
1852-#
1853-# You should have received a copy of the GNU General Public License along
1854-# with this program. If not, see <http://www.gnu.org/licenses/>.
1855-#
1856-# In addition, as a special exception, the copyright holders give
1857-# permission to link the code of portions of this program with the
1858-# OpenSSL library under certain conditions as described in each
1859-# individual source file, and distribute linked combinations
1860-# including the two.
1861-# You must obey the GNU General Public License in all respects
1862-# for all of the code used other than OpenSSL. If you modify
1863-# file(s) with this exception, you may extend this exception to your
1864-# version of the file(s), but you are not obligated to do so. If you
1865-# do not wish to do so, delete this exception statement from your
1866-# version. If you delete this exception statement from all source
1867-# files in the program, then also delete it here.
1868-"""Platform independent tests for the credentials management."""
1869-
1870-import platform
1871-import urllib
1872-import urlparse
1873-
1874-from collections import defaultdict
1875-from functools import wraps
1876-
1877-from twisted.internet import defer
1878-from twisted.trial.unittest import TestCase
1879-
1880-from contrib.testing.testcase import FAKED_CREDENTIALS
1881-from ubuntuone import clientdefs
1882-from ubuntuone.platform.credentials import (
1883- BASE_PING_URL,
1884- CredentialsError,
1885- CredentialsManagementTool,
1886- NO_OP,
1887- PING_URL,
1888- PING_URL_KEY,
1889- platform_data,
1890- POLICY_URL,
1891- POLICY_URL_KEY,
1892- TC_URL,
1893- TC_URL_KEY,
1894- UI_EXECUTABLE_KEY,
1895- UI_EXECUTABLE_QT,
1896- UI_PARAMS,
1897-)
1898-
1899-
1900-class FakedSignal(object):
1901- """A faked signal."""
1902-
1903- def __init__(self, name, callback):
1904- self.name = name
1905- self.callback = callback
1906- self.removed = False
1907- self.remove = lambda: setattr(self, 'removed', True)
1908-
1909-
1910-class FakedProxy(object):
1911- """Fake a CredentialsManagement proxy."""
1912-
1913- error_dict = None
1914- error_handler = None
1915-
1916- def __init__(self, *args, **kwargs):
1917- self._credentials = None
1918- self._args = None
1919- self._kwargs = None
1920- self._signals = []
1921- self._receivers = defaultdict(list)
1922- self._called = defaultdict(list)
1923-
1924- def connect_to_signal(self, signal_name, callback):
1925- """Keep track of connected signals."""
1926- self._receivers[signal_name].append(callback)
1927- result = FakedSignal(signal_name, callback)
1928- self._signals.append(result)
1929- return result
1930-
1931- def maybe_emit_error(f):
1932- """Decorator to fake a CredentialsError signal."""
1933-
1934- @wraps(f)
1935- def inner(self, *args, **kwargs):
1936- """Fake a CredentialsError signal."""
1937- if self.error_dict is None and self.error_handler is None:
1938- f(self, *args, **kwargs)
1939-
1940- reply_handler = kwargs.pop('reply_handler', NO_OP)
1941- error_handler = kwargs.pop('error_handler', NO_OP)
1942- if self.error_handler is not None:
1943- error_handler(self.error_handler)
1944- else:
1945- reply_handler()
1946-
1947- if self.error_dict is not None:
1948- self.CredentialsError(self.error_dict)
1949-
1950- return inner
1951-
1952- def record_call(f):
1953- """Decorator to record calls to 'f'."""
1954-
1955- @wraps(f)
1956- def inner(self, *a, **kw):
1957- """Record the call to 'f' and call it."""
1958- self._called[f.__name__].append((a, kw))
1959- return f(self, *a, **kw)
1960-
1961- return inner
1962-
1963- def emit_signal(f):
1964- """Decorator to emit a signal."""
1965-
1966- @wraps(f)
1967- def inner(self, *args, **kwargs):
1968- """Emit the signal."""
1969- for cb in self._receivers[f.__name__]:
1970- cb(*args, **kwargs)
1971-
1972- return inner
1973-
1974- @emit_signal
1975- def AuthorizationDenied(self):
1976- """Signal thrown when the user denies the authorization."""
1977-
1978- @emit_signal
1979- def CredentialsFound(self, credentials):
1980- """Signal thrown when the credentials are found."""
1981-
1982- @emit_signal
1983- def CredentialsNotFound(self):
1984- """Signal thrown when the credentials are not found."""
1985-
1986- @emit_signal
1987- def CredentialsCleared(self):
1988- """Signal thrown when the credentials were cleared."""
1989-
1990- @emit_signal
1991- def CredentialsStored(self):
1992- """Signal thrown when the credentials were stored."""
1993-
1994- @emit_signal
1995- def CredentialsError(self, error_dict):
1996- """Signal thrown when there is a problem getting the credentials."""
1997-
1998- @record_call
1999- @maybe_emit_error
2000- def find_credentials(self, reply_handler=NO_OP, error_handler=NO_OP):
2001- """Look for the credentials for an application."""
2002- if self._credentials is not None:
2003- self.CredentialsFound(self._credentials)
2004- else:
2005- self.CredentialsNotFound()
2006-
2007- @record_call
2008- @maybe_emit_error
2009- def clear_credentials(self, reply_handler=NO_OP, error_handler=NO_OP):
2010- """Clear the credentials for an application."""
2011- self._credentials = None
2012- self.CredentialsCleared()
2013-
2014- @record_call
2015- @maybe_emit_error
2016- def store_credentials(self, credentials,
2017- reply_handler=NO_OP, error_handler=NO_OP):
2018- """Store the token for an application."""
2019- self._credentials = credentials
2020- self.CredentialsStored()
2021-
2022- @record_call
2023- @maybe_emit_error
2024- def register(self, dict_arg,
2025- reply_handler=NO_OP, error_handler=NO_OP):
2026- """Get credentials if found else prompt GUI to register."""
2027- creds = self._credentials
2028- if creds is not None and len(creds) > 0:
2029- self.CredentialsFound(creds)
2030- elif creds == {}:
2031- # fake an AuthorizationDenied
2032- self.AuthorizationDenied()
2033- elif creds is None:
2034- # fake the adding of the credentials
2035- self._credentials = FAKED_CREDENTIALS
2036- self.CredentialsFound(FAKED_CREDENTIALS)
2037-
2038- @record_call
2039- @maybe_emit_error
2040- def login(self, dict_arg,
2041- reply_handler=NO_OP, error_handler=NO_OP):
2042- """Get credentials if found else prompt GUI to login."""
2043- self.register(dict_arg, reply_handler, error_handler)
2044-
2045- @record_call
2046- @maybe_emit_error
2047- def login_email_password(self, dict_arg,
2048- reply_handler=NO_OP, error_handler=NO_OP):
2049- """Fake login_email_password."""
2050- self.register(dict_arg, reply_handler, error_handler)
2051-
2052-
2053-class FakedPlatformSource(object):
2054- """Faked the platform source."""
2055-
2056- def get_creds_proxy(self):
2057- """Return a new faked proxy every time, so we can test proxy caching."""
2058- return FakedProxy()
2059-
2060-
2061-class CredentialsManagementToolTestCase(TestCase):
2062- """Test case for the object that manages Magicicada credentials."""
2063-
2064- timeout = 3
2065- error_dict = None
2066- error_handler = None
2067-
2068- @defer.inlineCallbacks
2069- def setUp(self):
2070- yield super(CredentialsManagementToolTestCase, self).setUp()
2071- self.cred_tool = CredentialsManagementTool()
2072- self.patch(self.cred_tool, 'get_platform_source', FakedPlatformSource)
2073- self.proxy = yield self.cred_tool.get_creds_proxy()
2074- self.proxy.error_dict = self.error_dict
2075- self.proxy.error_handler = self.error_handler
2076-
2077- self.window_id_arg = {'window_id': '803'}
2078- self.email_password_arg = {'email': 'foo@bar.com', 'password': 'yadda'}
2079-
2080- @defer.inlineCallbacks
2081- def test_proxy_is_reused(self):
2082- """The inner proxy is re-used."""
2083- proxy1 = yield self.cred_tool.get_creds_proxy()
2084- proxy2 = yield self.cred_tool.get_creds_proxy()
2085- self.assertTrue(proxy1 is proxy2)
2086-
2087- def test_ui_params(self):
2088- """The UI_PARAMS dict is correct."""
2089- expected = {
2090- PING_URL_KEY: PING_URL,
2091- POLICY_URL_KEY: POLICY_URL,
2092- TC_URL_KEY: TC_URL,
2093- UI_EXECUTABLE_KEY: UI_EXECUTABLE_QT,
2094- }
2095- self.assertEqual(expected, UI_PARAMS)
2096-
2097-
2098-class ArgsTestCase(CredentialsManagementToolTestCase):
2099- """Test case to check that proper arguments are passed to SSO backend."""
2100-
2101- @defer.inlineCallbacks
2102- def assert_method_called(self, method_name, *args, **kwargs):
2103- """Test that 'method_name' was called once with 'args' and 'kwargs."""
2104- self.assertIn(method_name, self.proxy._called)
2105-
2106- calls = self.proxy._called[method_name]
2107- msg = '%s must be called only once (got %s instead).'
2108- self.assertEqual(len(calls), 1, msg % (method_name, len(calls)))
2109-
2110- actual_args, actual_kwargs = calls[0]
2111- self.assertEqual(args, actual_args)
2112-
2113- reply_handler = actual_kwargs.pop('reply_handler', None)
2114- self.assertFalse(reply_handler is None, 'Must provide a reply_handler')
2115- d = defer.Deferred()
2116- reply_handler(deferred=d)
2117- yield d
2118- self.assertTrue(d.called)
2119-
2120- error_handler = actual_kwargs.pop('error_handler', None)
2121- self.assertFalse(error_handler is None, 'Must provide a error_handler')
2122- d = defer.Deferred()
2123- error_handler(error='foo', deferred=d)
2124- e = yield self.assertFailure(d, CredentialsError)
2125- self.assertEqual(e.message, 'foo')
2126-
2127- self.assertEqual(kwargs, actual_kwargs)
2128-
2129- @defer.inlineCallbacks
2130- def test_find_credentials(self):
2131- """The find_credentials method calls proper method."""
2132- yield self.cred_tool.find_credentials()
2133-
2134- self.assert_method_called('find_credentials')
2135-
2136- @defer.inlineCallbacks
2137- def test_clear_credentials(self):
2138- """The clear_credentials method calls proper method."""
2139- yield self.cred_tool.clear_credentials()
2140-
2141- self.assert_method_called('clear_credentials')
2142-
2143- @defer.inlineCallbacks
2144- def test_store_credentials(self):
2145- """The store_credentials method calls proper method."""
2146- yield self.cred_tool.store_credentials(FAKED_CREDENTIALS)
2147-
2148- self.assert_method_called('store_credentials', FAKED_CREDENTIALS)
2149-
2150- @defer.inlineCallbacks
2151- def test_register(self):
2152- """The register method calls proper method."""
2153- yield self.cred_tool.register(**self.window_id_arg)
2154-
2155- self.assert_method_called('register', self.window_id_arg)
2156-
2157- @defer.inlineCallbacks
2158- def test_login(self):
2159- """The login method calls proper method."""
2160- yield self.cred_tool.login(**self.window_id_arg)
2161-
2162- self.assert_method_called('login', self.window_id_arg)
2163-
2164- @defer.inlineCallbacks
2165- def test_login_email_password(self):
2166- """The login method calls proper method."""
2167- yield self.cred_tool.login_email_password(**self.email_password_arg)
2168-
2169- self.assert_method_called('login_email_password',
2170- self.email_password_arg)
2171-
2172-
2173-class NoErrorWithCredsTestCase(CredentialsManagementToolTestCase):
2174- """Test case when there was no error, and credentials are present."""
2175-
2176- token = {'test': 'me'}
2177-
2178- @defer.inlineCallbacks
2179- def do_test(self, method, expected=None, **kwargs):
2180- """Perform the test itself."""
2181- if self.token is not None:
2182- self.proxy.store_credentials(self.token)
2183- else:
2184- yield self.proxy.clear_credentials()
2185- actual = yield method(**kwargs)
2186- self.assertEqual(expected, actual)
2187-
2188- @defer.inlineCallbacks
2189- def test_find_credentials(self):
2190- """The find_credentials method calls proper method."""
2191- yield self.do_test(self.cred_tool.find_credentials, self.token)
2192-
2193- @defer.inlineCallbacks
2194- def test_clear_credentials(self):
2195- """The clear_credentials method calls proper method."""
2196- yield self.do_test(self.cred_tool.clear_credentials, None)
2197-
2198- @defer.inlineCallbacks
2199- def test_store_credentials(self):
2200- """The store_credentials method calls proper method."""
2201- yield self.do_test(self.cred_tool.store_credentials, None,
2202- token=FAKED_CREDENTIALS)
2203-
2204- @defer.inlineCallbacks
2205- def test_register(self):
2206- """The register method calls proper method."""
2207- yield self.do_test(self.cred_tool.register, self.token,
2208- **self.window_id_arg)
2209-
2210- @defer.inlineCallbacks
2211- def test_login(self):
2212- """The login method calls proper method."""
2213- yield self.do_test(self.cred_tool.login, self.token,
2214- **self.window_id_arg)
2215-
2216- @defer.inlineCallbacks
2217- def test_login_email_password(self):
2218- """The login_email_password method calls proper method."""
2219- yield self.do_test(self.cred_tool.login_email_password, self.token,
2220- **self.email_password_arg)
2221-
2222-
2223-class NoErrorNoCredsTestCase(NoErrorWithCredsTestCase):
2224- """Test case when there was no error, and credentials are not present."""
2225-
2226- token = None
2227-
2228- @defer.inlineCallbacks
2229- def test_find_credentials(self):
2230- """The find_credentials method calls proper method."""
2231- yield self.do_test(self.cred_tool.find_credentials, {})
2232-
2233- @defer.inlineCallbacks
2234- def test_register(self):
2235- """The register method calls proper method."""
2236- yield self.do_test(self.cred_tool.register, FAKED_CREDENTIALS,
2237- **self.window_id_arg)
2238-
2239- @defer.inlineCallbacks
2240- def test_register_authorization_denied(self):
2241- """The register method calls proper method."""
2242- self.token = {}
2243- yield self.do_test(self.cred_tool.register, None,
2244- **self.window_id_arg)
2245-
2246- @defer.inlineCallbacks
2247- def test_login(self):
2248- """The login method calls proper method."""
2249- yield self.do_test(self.cred_tool.login, FAKED_CREDENTIALS,
2250- **self.window_id_arg)
2251-
2252- @defer.inlineCallbacks
2253- def test_login_authorization_denied(self):
2254- """The login method calls proper method."""
2255- self.token = {}
2256- yield self.do_test(self.cred_tool.login, None, **self.window_id_arg)
2257-
2258- @defer.inlineCallbacks
2259- def test_login_email_password(self):
2260- """The login_email_password method calls proper method."""
2261- yield self.do_test(self.cred_tool.login_email_password,
2262- FAKED_CREDENTIALS, **self.email_password_arg)
2263-
2264-
2265-class WithCredentialsErrorTestCase(NoErrorNoCredsTestCase):
2266- """Test case when there was a CredentialsError sent from the proxy."""
2267-
2268- error_dict = expected = {'error_type': 'Test'}
2269-
2270- @defer.inlineCallbacks
2271- def do_test(self, method, expected=None, **kwargs):
2272- """Perform the test itself."""
2273- exc = yield self.assertFailure(method(**kwargs), Exception)
2274- self.assertIsInstance(exc, CredentialsError)
2275- self.assertEqual(self.expected, exc.args[0])
2276-
2277-
2278-class WithErrorHandlerCalledTestCase(WithCredentialsErrorTestCase):
2279- """Test case when the error handler was called."""
2280-
2281- error_dict = None
2282- error_handler = expected = {'error_type': 'Another Test'}
2283-
2284-
2285-class SignalsRemovedTestCase(NoErrorWithCredsTestCase):
2286-
2287- @defer.inlineCallbacks
2288- def do_test(self, method, expected=None, **kwargs):
2289- """Perform the test itself."""
2290- yield method(**kwargs)
2291- for signal in self.proxy._signals:
2292- self.assertTrue(signal.removed)
2293-
2294-
2295-class PingURLPlatformDetails(TestCase):
2296-
2297- def test_ping_url(self):
2298- """The PING_URL is BASE_PING_URL plus urlencoded platform data."""
2299- result = urlparse.urlparse(PING_URL)
2300-
2301- expected_base = urlparse.urljoin(result.scheme + '://' + result.netloc,
2302- result.path)
2303- self.assertEqual(expected_base, BASE_PING_URL)
2304-
2305- expected_query = dict(urlparse.parse_qsl(result.query))
2306- expected_query = urllib.urlencode(expected_query)
2307- self.assertEqual(expected_query, platform_data())
2308-
2309- def test_ping_url_is_unicode(self):
2310- """The PING_URL is unicode."""
2311- self.assertIsInstance(PING_URL, unicode)
2312-
2313- def test_platform_data(self):
2314- """The platform data is correct."""
2315- expected = {"platform": platform.system(),
2316- "platform_version": platform.release(),
2317- "platform_arch": platform.machine(),
2318- "client_version": clientdefs.VERSION}
2319- self.assertEqual(urllib.urlencode(expected), platform_data())
2320-
2321- def test_platform_data_non_ascii(self):
2322- """The platform data is correct for non ascii values."""
2323- system = u'Ñandú'
2324- release = u'ñoño'
2325- machine = u'rápida'
2326- version = u'1.2.3-ubuntu♥'
2327-
2328- self.patch(platform, 'system', lambda: system.encode('utf8'))
2329- self.patch(platform, 'release', lambda: release.encode('utf8'))
2330- self.patch(platform, 'machine', lambda: machine.encode('utf8'))
2331- self.patch(clientdefs, 'VERSION', version.encode('utf8'))
2332-
2333- expected = {"platform": system.encode('utf8'),
2334- "platform_version": release.encode('utf8'),
2335- "platform_arch": machine.encode('utf8'),
2336- "client_version": version.encode('utf8')}
2337- expected = urllib.urlencode(expected)
2338- self.assertEqual(expected, platform_data())
2339
2340=== modified file 'ubuntuone/__init__.py'
2341--- ubuntuone/__init__.py 2012-04-09 20:07:05 +0000
2342+++ ubuntuone/__init__.py 2016-05-30 13:04:07 +0000
2343@@ -1,4 +1,7 @@
2344+# -*- coding: utf-8 -*-
2345+#
2346 # Copyright 2009-2012 Canonical Ltd.
2347+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
2348 #
2349 # This program is free software: you can redistribute it and/or modify it
2350 # under the terms of the GNU General Public License version 3, as published
2351@@ -24,5 +27,7 @@
2352 # do not wish to do so, delete this exception statement from your
2353 # version. If you delete this exception statement from all source
2354 # files in the program, then also delete it here.
2355-"""ubuntuone package"""
2356+
2357+"""Main package."""
2358+
2359 __import__('pkg_resources').declare_namespace(__name__)
2360
2361=== modified file 'ubuntuone/clientdefs.py.in'
2362--- ubuntuone/clientdefs.py.in 2015-09-29 21:05:26 +0000
2363+++ ubuntuone/clientdefs.py.in 2016-05-30 13:04:07 +0000
2364@@ -31,10 +31,28 @@
2365 """Configure-time definitions for the client."""
2366
2367 import gettext
2368+import os
2369+import platform
2370+import urllib
2371
2372 Q_ = lambda string: gettext.dgettext(GETTEXT_PACKAGE, string)
2373
2374+NAME = 'Magicicada'
2375+
2376 VERSION = "@VERSION@"
2377 LOCALEDIR = "@localedir@"
2378 LIBEXECDIR = "@libexecdir@"
2379 GETTEXT_PACKAGE = "@GETTEXT_PACKAGE@"
2380+PROJECT_NAME = "@GETTEXT_PACKAGE@"
2381+PROJECT_DIR = os.path.join('@prefix@', 'share', PROJECT_NAME)
2382+BIN_DIR = os.path.join('@prefix@', 'lib', PROJECT_NAME)
2383+
2384+
2385+def platform_data():
2386+ result = {'platform': platform.system(),
2387+ 'platform_version': platform.release(),
2388+ 'platform_arch': platform.machine(),
2389+ 'client_version': VERSION}
2390+ # urlencode will not encode unicode, only bytes
2391+ result = urllib.urlencode(result)
2392+ return result
2393
2394=== added file 'ubuntuone/keyring.py'
2395--- ubuntuone/keyring.py 1970-01-01 00:00:00 +0000
2396+++ ubuntuone/keyring.py 2016-05-30 13:04:07 +0000
2397@@ -0,0 +1,145 @@
2398+# -*- coding: utf-8 -*-
2399+#
2400+# Copyright 2011-2012 Canonical Ltd.
2401+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
2402+#
2403+# This program is free software: you can redistribute it and/or modify it
2404+# under the terms of the GNU General Public License version 3, as published
2405+# by the Free Software Foundation.
2406+#
2407+# This program is distributed in the hope that it will be useful, but
2408+# WITHOUT ANY WARRANTY; without even the implied warranties of
2409+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2410+# PURPOSE. See the GNU General Public License for more details.
2411+#
2412+# You should have received a copy of the GNU General Public License along
2413+# with this program. If not, see <http://www.gnu.org/licenses/>.
2414+#
2415+# In addition, as a special exception, the copyright holders give
2416+# permission to link the code of portions of this program with the
2417+# OpenSSL library under certain conditions as described in each
2418+# individual source file, and distribute linked combinations
2419+# including the two.
2420+# You must obey the GNU General Public License in all respects
2421+# for all of the code used other than OpenSSL. If you modify
2422+# file(s) with this exception, you may extend this exception to your
2423+# version of the file(s), but you are not obligated to do so. If you
2424+# do not wish to do so, delete this exception statement from your
2425+# version. If you delete this exception statement from all source
2426+# files in the program, then also delete it here.
2427+
2428+"""Handle keys in the local kerying."""
2429+
2430+from __future__ import unicode_literals
2431+
2432+import logging
2433+import socket
2434+import sys
2435+
2436+try:
2437+ from urllib.parse import parse_qsl, quote, urlencode
2438+except ImportError:
2439+ from urllib import quote, urlencode
2440+ from urlparse import parse_qsl
2441+
2442+from twisted.internet.defer import inlineCallbacks, returnValue
2443+
2444+from ubuntuone.clientdefs import NAME
2445+from ubuntuone.utils import compat
2446+from ubuntuone.utils.txsecrets import SecretService
2447+
2448+
2449+logger = logging.getLogger(__name__)
2450+
2451+TOKEN_SEPARATOR = ' @ '
2452+SEPARATOR_REPLACEMENT = ' AT '
2453+
2454+
2455+def gethostname():
2456+ """Get the hostname, return the name as unicode."""
2457+ sys_encoding = sys.getfilesystemencoding()
2458+ hostname = socket.gethostname()
2459+ if isinstance(hostname, compat.binary_type):
2460+ return hostname.decode(sys_encoding)
2461+ return hostname
2462+
2463+
2464+def get_token_name(app_name):
2465+ """Build the token name.. Return an unicode."""
2466+ computer_name = gethostname()
2467+ computer_name = computer_name.replace(TOKEN_SEPARATOR,
2468+ SEPARATOR_REPLACEMENT)
2469+
2470+ assert isinstance(computer_name, compat.text_type)
2471+ assert isinstance(computer_name, compat.text_type)
2472+
2473+ return TOKEN_SEPARATOR.join((app_name, computer_name))
2474+
2475+
2476+class Keyring(object):
2477+ """A Keyring for a given application name."""
2478+
2479+ def __init__(self):
2480+ """Initialize this instance."""
2481+ self.service = SecretService()
2482+
2483+ def _get_keyring_attr(self, app_name):
2484+ """Build the keyring attributes for this credentials."""
2485+ attr = {"key-type": "%s credentials" % NAME,
2486+ "token-name": get_token_name(app_name)}
2487+ return attr
2488+
2489+ @inlineCallbacks
2490+ def _find_keyring_item(self, app_name, attr=None):
2491+ """Return the keyring item or None if not found."""
2492+ if attr is None:
2493+ attr = self._get_keyring_attr(app_name)
2494+ logger.debug("Finding all items for app_name %r.", app_name)
2495+ items = yield self.service.search_items(attr)
2496+ if len(items) == 0:
2497+ # if no items found, return None
2498+ logger.debug("No items found!")
2499+ returnValue(None)
2500+
2501+ logger.debug("Returning first item found.")
2502+ returnValue(items[0])
2503+
2504+ @inlineCallbacks
2505+ def set_credentials(self, app_name, cred):
2506+ """Set the credentials."""
2507+ # Creates the secret from the credentials
2508+ secret = urlencode(cred)
2509+
2510+ attr = self._get_keyring_attr(app_name)
2511+ # Add our credentials to the keyring
2512+ yield self.service.open_session()
2513+ collection = yield self.service.get_default_collection()
2514+ yield collection.create_item(app_name, attr, secret, True)
2515+
2516+ @inlineCallbacks
2517+ def get_credentials(self, app_name):
2518+ """A deferred with the secret in a dictionary."""
2519+ # If we have no attributes, return None
2520+ logger.debug("Getting credentials for %r.", app_name)
2521+ yield self.service.open_session()
2522+ item = yield self._find_keyring_item(app_name)
2523+ if item is not None:
2524+ logger.debug("Parsing secret.")
2525+ secret = yield item.get_value()
2526+ returnValue(dict(parse_qsl(secret)))
2527+
2528+ # nothing was found
2529+ returnValue(None)
2530+
2531+ @inlineCallbacks
2532+ def delete_credentials(self, app_name):
2533+ """Delete a set of credentials from the keyring."""
2534+ attr = self._get_keyring_attr(app_name)
2535+ # Add our credentials to the keyring
2536+ yield self.service.open_session()
2537+ collection = yield self.service.get_default_collection()
2538+ yield collection.create_item(app_name, attr, "secret!", True)
2539+
2540+ item = yield self._find_keyring_item(app_name)
2541+ if item is not None:
2542+ yield item.delete()
2543
2544=== added directory 'ubuntuone/networkstate'
2545=== added file 'ubuntuone/networkstate/__init__.py'
2546--- ubuntuone/networkstate/__init__.py 1970-01-01 00:00:00 +0000
2547+++ ubuntuone/networkstate/__init__.py 2016-05-30 13:04:07 +0000
2548@@ -0,0 +1,55 @@
2549+# -*- coding: utf-8 -*-
2550+#
2551+# Copyright 2011-2012 Canonical Ltd.
2552+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
2553+#
2554+# This program is free software: you can redistribute it and/or modify it
2555+# under the terms of the GNU General Public License version 3, as published
2556+# by the Free Software Foundation.
2557+#
2558+# This program is distributed in the hope that it will be useful, but
2559+# WITHOUT ANY WARRANTY; without even the implied warranties of
2560+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2561+# PURPOSE. See the GNU General Public License for more details.
2562+#
2563+# You should have received a copy of the GNU General Public License along
2564+# with this program. If not, see <http://www.gnu.org/licenses/>.
2565+#
2566+# In addition, as a special exception, the copyright holders give
2567+# permission to link the code of portions of this program with the
2568+# OpenSSL library under certain conditions as described in each
2569+# individual source file, and distribute linked combinations
2570+# including the two.
2571+# You must obey the GNU General Public License in all respects
2572+# for all of the code used other than OpenSSL. If you modify
2573+# file(s) with this exception, you may extend this exception to your
2574+# version of the file(s), but you are not obligated to do so. If you
2575+# do not wish to do so, delete this exception statement from your
2576+# version. If you delete this exception statement from all source
2577+# files in the program, then also delete it here.
2578+
2579+"""Platform specific network status."""
2580+
2581+import sys
2582+
2583+
2584+NetworkManagerState = None
2585+
2586+
2587+class NetworkFailException(Exception):
2588+
2589+ """Exception for when the network detect process fails."""
2590+
2591+
2592+if sys.platform == 'win32':
2593+ from ubuntuone.networkstate import windows
2594+ networksource = windows
2595+elif sys.platform == 'darwin':
2596+ from ubuntuone.networkstate import darwin
2597+ networksource = darwin
2598+else:
2599+ from ubuntuone.networkstate import linux
2600+ networksource = linux
2601+
2602+NetworkManagerState = networksource.NetworkManagerState
2603+is_machine_connected = networksource.is_machine_connected
2604
2605=== added file 'ubuntuone/networkstate/darwin.py'
2606--- ubuntuone/networkstate/darwin.py 1970-01-01 00:00:00 +0000
2607+++ ubuntuone/networkstate/darwin.py 2016-05-30 13:04:07 +0000
2608@@ -0,0 +1,305 @@
2609+# -*- coding: utf-8 -*-
2610+#
2611+# Copyright 2012 Canonical Ltd.
2612+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
2613+#
2614+# This program is free software: you can redistribute it and/or modify it
2615+# under the terms of the GNU General Public License version 3, as published
2616+# by the Free Software Foundation.
2617+#
2618+# This program is distributed in the hope that it will be useful, but
2619+# WITHOUT ANY WARRANTY; without even the implied warranties of
2620+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2621+# PURPOSE. See the GNU General Public License for more details.
2622+#
2623+# You should have received a copy of the GNU General Public License along
2624+# with this program. If not, see <http://www.gnu.org/licenses/>.
2625+
2626+"""Network state detection on OS X.
2627+
2628+is_machine_connected(): (deferred) returns connected state as bool
2629+NetworkManagerState: class with listening thread, calls back with state changes
2630+
2631+"""
2632+
2633+import logging
2634+
2635+from threading import Thread
2636+
2637+from twisted.internet import defer
2638+
2639+from ubuntuone.networkstate import NetworkFailException
2640+from ubuntuone.networkstate.networkstates import (ONLINE, OFFLINE, UNKNOWN)
2641+
2642+logger = logging.getLogger(__name__)
2643+
2644+HOSTNAME_TO_CHECK = 'login.ubuntu.com'
2645+
2646+from ctypes import (
2647+ CDLL,
2648+ POINTER,
2649+ CFUNCTYPE,
2650+ Structure,
2651+ pointer,
2652+ c_bool,
2653+ c_long,
2654+ c_void_p,
2655+ c_uint32)
2656+
2657+from ctypes.util import find_library
2658+
2659+
2660+# Functions and constants below are from
2661+# /System/Library/CoreFoundation.framework/
2662+CoreFoundationPath = find_library("CoreFoundation")
2663+CoreFoundation = CDLL(CoreFoundationPath)
2664+
2665+# CFRunLoopRef CFRunLoopGetCurrent()
2666+CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
2667+CFRunLoopGetCurrent.restype = c_void_p
2668+CFRunLoopGetCurrent.argtypes = []
2669+
2670+# void CFRelease(CFTypeRef)
2671+CFRelease = CoreFoundation.CFRelease
2672+CFRelease.restype = None
2673+CFRelease.argtypes = [c_void_p]
2674+
2675+# void CFRunLoopRun()
2676+CFRunLoopRun = CoreFoundation.CFRunLoopRun
2677+
2678+# const CFStringRef kCFRunLoopDefaultMode
2679+kCFRunLoopDefaultMode = c_void_p.in_dll(CoreFoundation,
2680+ "kCFRunLoopDefaultMode")
2681+
2682+
2683+# Functions and constants below are from
2684+# /System/Library/SystemConfiguration.framework/
2685+SystemConfigurationPath = find_library("SystemConfiguration")
2686+
2687+# SystemConfiguration abbreviated as "SC" below:
2688+SC = CDLL(SystemConfigurationPath)
2689+
2690+# "SCNetworkReachability" functions abbreviated to "SCNR*" here.
2691+
2692+# SCNetworkReachabilityRef
2693+# SCNetworkReachabilityCreateWithName(CFAllocatorRef, const char *)
2694+SCNRCreateWithName = SC.SCNetworkReachabilityCreateWithName
2695+SCNRCreateWithName.restype = c_void_p
2696+
2697+# Boolean SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef,
2698+# SCNetworkReachabilityFlags)
2699+SCNRGetFlags = SC.SCNetworkReachabilityGetFlags
2700+SCNRGetFlags.restype = c_bool
2701+SCNRGetFlags.argtypes = [c_void_p,
2702+ POINTER(c_uint32)]
2703+
2704+SCNRScheduleWithRunLoop = SC.SCNetworkReachabilityScheduleWithRunLoop
2705+SCNRScheduleWithRunLoop.restype = c_bool
2706+SCNRScheduleWithRunLoop.argtypes = [c_void_p,
2707+ c_void_p,
2708+ c_void_p]
2709+
2710+# ctypes callback type to match SCNetworkReachabilityCallback
2711+# void (*SCNetworkReachabilityCallback) (SCNetworkReachabilityRef,
2712+# SCNetworkReachabilityFlags,
2713+# void *)
2714+SCNRCallbackType = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p)
2715+# NOTE: need to keep this reference alive as long as a callback might occur.
2716+
2717+# Boolean SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef,
2718+# SCNetworkReachabilityCallback,
2719+# SCNetworkReachabilityContext)
2720+SCNRSetCallback = SC.SCNetworkReachabilitySetCallback
2721+SCNRSetCallback.restype = c_bool
2722+SCNRSetCallback.argtypes = [c_void_p,
2723+ SCNRCallbackType,
2724+ c_void_p]
2725+
2726+
2727+def check_connected_state():
2728+ """Calls Synchronous SCNR API, returns bool."""
2729+ target = SCNRCreateWithName(None, HOSTNAME_TO_CHECK)
2730+ if target is None:
2731+ logger.error("Error creating network reachability reference.")
2732+ raise NetworkFailException()
2733+
2734+ flags = c_uint32(0)
2735+ ok = SCNRGetFlags(target, pointer(flags))
2736+ CFRelease(target)
2737+
2738+ if not ok:
2739+ logger.error("Error getting reachability status of '%s'" %
2740+ HOSTNAME_TO_CHECK)
2741+ raise NetworkFailException()
2742+
2743+ return flags_say_reachable(flags.value)
2744+
2745+
2746+def flags_say_reachable(flags):
2747+ """Check flags returned from SCNetworkReachability API. Returns bool.
2748+
2749+ Requires some logic:
2750+ reachable_flag isn't enough on its own.
2751+
2752+ A down wifi will return flags = 7, or reachable_flag and
2753+ connection_required_flag, meaning that the host *would be*
2754+ reachable, but you need a connection first. (And then you'd
2755+ presumably be best off checking again.)
2756+ """
2757+ # values from SCNetworkReachability.h
2758+ reachable_flag = 1 << 1
2759+ connection_required_flag = 1 << 2
2760+
2761+ if flags & connection_required_flag:
2762+ return False
2763+ elif flags & reachable_flag:
2764+ return True
2765+ else:
2766+ return False
2767+
2768+
2769+class SCNRContext(Structure):
2770+
2771+ """A struct to send as SCNetworkReachabilityContext to SCNRSetCallback.
2772+
2773+ We don't use the fields currently.
2774+ """
2775+
2776+ _fields_ = [("version", c_long),
2777+ ("info", c_void_p),
2778+ ("retain", c_void_p), # func ptr
2779+ ("release", c_void_p), # func ptr
2780+ ("copyDescription", c_void_p)] # func ptr
2781+
2782+
2783+class NetworkManagerState(object):
2784+
2785+ """Probe Network State and receive callbacks on changes.
2786+
2787+ This class uses both synchronous and async API from the
2788+ SystemConfiguration framework.
2789+
2790+ To use: Initialize with a callback function, then call
2791+ find_online_state. The callback will be called once immediately
2792+ with the current state and then only on state changes.
2793+
2794+ Any exceptions in checking state will result in the callback being
2795+ called with UNKNOWN. At this point the listening thread is no
2796+ longer runing, and a new NetworkManagerState should be created.
2797+
2798+ NOTE: the callback will be called from the separate listening
2799+ thread, except for the first call.
2800+ """
2801+
2802+ def __init__(self, result_cb):
2803+ """Initialize and save result callback function.
2804+
2805+ result_cb should take one argument, a networkstate object.
2806+
2807+ The callback will be called with one of ONLINE, OFFLINE, or
2808+ UNKNOWN, as defined in networkstates.py.
2809+ """
2810+ self.result_cb = result_cb
2811+ self.listener_thread = None
2812+
2813+ def _state_changed(self, flags):
2814+ """Testable callback called by reachability_state_changed_cb.
2815+
2816+ Used because reachability_state_changed_cb has to have a
2817+ particular method signature.
2818+
2819+ Clients should not call this method.
2820+ """
2821+ if flags_say_reachable(flags):
2822+ self.result_cb(ONLINE)
2823+ else:
2824+ self.result_cb(OFFLINE)
2825+
2826+ def _listen_on_separate_thread(self):
2827+ """In separate thread, setup callback and listen for changes.
2828+
2829+ On error, calls result_cb(UNKNOWN) and returns.
2830+ """
2831+
2832+ def reachability_state_changed_cb(targetref, flags, info):
2833+ """Callback for SCNetworkReachability API
2834+
2835+ This callback is passed to the SCNetworkReachability API,
2836+ so its method signature has to be exactly this. Therefore,
2837+ we declare it here and just call _state_changed with
2838+ flags."""
2839+ self._state_changed(flags)
2840+
2841+ c_callback = SCNRCallbackType(reachability_state_changed_cb)
2842+ context = SCNRContext(0, None, None, None, None)
2843+
2844+ target = SCNRCreateWithName(None, HOSTNAME_TO_CHECK)
2845+ if target is None:
2846+ logger.error("Error creating SCNetworkReachability target")
2847+ self.result_cb(UNKNOWN)
2848+ return
2849+
2850+ ok = SCNRSetCallback(target, c_callback, pointer(context))
2851+ if not ok:
2852+ logger.error("error setting SCNetworkReachability callback")
2853+ CFRelease(target)
2854+ self.result_cb(UNKNOWN)
2855+ return
2856+
2857+ ok = SCNRScheduleWithRunLoop(target,
2858+ CFRunLoopGetCurrent(),
2859+ kCFRunLoopDefaultMode)
2860+ if not ok:
2861+ logger.error("error scheduling on runloop: SCNetworkReachability")
2862+ CFRelease(target)
2863+ self.result_cb(UNKNOWN)
2864+ return
2865+
2866+ CFRunLoopRun()
2867+
2868+ CFRelease(target) # won't happen
2869+
2870+ def _start_listening_thread(self):
2871+ """Start the separate listener thread.
2872+
2873+ Currently will not start one more than once.
2874+ Should be OK because we don't expect errors the listen method.
2875+
2876+ To add more error handling support, you could either add a
2877+ call to join and re-start, or client could just create a new
2878+ NetworkManagerState.
2879+ """
2880+
2881+ if self.listener_thread is None:
2882+ self.listener_thread = Thread(
2883+ target=self._listen_on_separate_thread,
2884+ name="Network Connection Monitor")
2885+ self.listener_thread.daemon = True
2886+ self.listener_thread.start()
2887+
2888+ def find_online_state(self):
2889+ """Calls callback with current state. Starts listening thread."""
2890+ try:
2891+ if check_connected_state():
2892+ self.result_cb(ONLINE)
2893+ else:
2894+ self.result_cb(OFFLINE)
2895+
2896+ except Exception:
2897+ logger.exception("Getting state from SCNetworkReachability")
2898+ self.result_cb(UNKNOWN)
2899+ return # don't start thread on error
2900+
2901+ self._start_listening_thread()
2902+
2903+
2904+def is_machine_connected():
2905+ """Return a deferred that when fired, returns online state as a bool.
2906+
2907+ Raises NetworkFailException for errors.
2908+ """
2909+ try:
2910+ return defer.succeed(check_connected_state())
2911+ except Exception as e:
2912+ logger.exception("Exception calling check_connected_state:")
2913+ return defer.fail(NetworkFailException(e))
2914
2915=== added file 'ubuntuone/networkstate/linux.py'
2916--- ubuntuone/networkstate/linux.py 1970-01-01 00:00:00 +0000
2917+++ ubuntuone/networkstate/linux.py 2016-05-30 13:04:07 +0000
2918@@ -0,0 +1,130 @@
2919+# -*- coding: utf-8 -*-
2920+#
2921+# Copyright 2010-2012 Canonical Ltd.
2922+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
2923+#
2924+# This program is free software: you can redistribute it and/or modify it
2925+# under the terms of the GNU General Public License version 3, as published
2926+# by the Free Software Foundation.
2927+#
2928+# This program is distributed in the hope that it will be useful, but
2929+# WITHOUT ANY WARRANTY; without even the implied warranties of
2930+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2931+# PURPOSE. See the GNU General Public License for more details.
2932+#
2933+# You should have received a copy of the GNU General Public License along
2934+# with this program. If not, see <http://www.gnu.org/licenses/>.
2935+#
2936+# In addition, as a special exception, the copyright holders give
2937+# permission to link the code of portions of this program with the
2938+# OpenSSL library under certain conditions as described in each
2939+# individual source file, and distribute linked combinations
2940+# including the two.
2941+# You must obey the GNU General Public License in all respects
2942+# for all of the code used other than OpenSSL. If you modify
2943+# file(s) with this exception, you may extend this exception to your
2944+# version of the file(s), but you are not obligated to do so. If you
2945+# do not wish to do so, delete this exception statement from your
2946+# version. If you delete this exception statement from all source
2947+# files in the program, then also delete it here.
2948+
2949+"""Implementation of network state detection."""
2950+
2951+import dbus
2952+import logging
2953+
2954+from twisted.internet import defer
2955+
2956+from ubuntuone.networkstate import NetworkFailException
2957+from ubuntuone.networkstate.networkstates import (
2958+ ONLINE, OFFLINE,
2959+ NM_STATE_CONNECTING_LIST,
2960+ NM_STATE_CONNECTED_LIST,
2961+ NM_STATE_DISCONNECTED_LIST,
2962+)
2963+
2964+
2965+logger = logging.getLogger(__name__)
2966+
2967+NM_DBUS_INTERFACE = "org.freedesktop.NetworkManager"
2968+NM_DBUS_OBJECTPATH = "/org/freedesktop/NetworkManager"
2969+
2970+
2971+class NetworkManagerState(object):
2972+ """Checks the state of NetworkManager thru DBus."""
2973+
2974+ def __init__(self, result_cb, dbus_module=dbus):
2975+ """Initialize this instance with a result and error callbacks."""
2976+ self.result_cb = result_cb
2977+ self.dbus = dbus_module
2978+ self.state_signal = None
2979+
2980+ def call_result_cb(self, state):
2981+ """Return the state thru the result callback."""
2982+ self.result_cb(state)
2983+
2984+ def got_state(self, state):
2985+ """Called by DBus when the state is retrieved from NM."""
2986+ if state in NM_STATE_CONNECTED_LIST:
2987+ self.call_result_cb(ONLINE)
2988+ elif state in NM_STATE_CONNECTING_LIST:
2989+ logger.debug("Currently connecting, waiting for signal")
2990+ else:
2991+ self.call_result_cb(OFFLINE)
2992+
2993+ def got_error(self, error):
2994+ """Called by DBus when the state is retrieved from NM."""
2995+ # Assuming since Network Manager is not running,
2996+ # the user has connected in some other way
2997+ logger.error("Error contacting NetworkManager: %s" %
2998+ str(error))
2999+ self.call_result_cb(ONLINE)
3000+
3001+ def state_changed(self, state):
3002+ """Called when a signal is emmited by Network Manager."""
3003+ if int(state) in NM_STATE_CONNECTED_LIST:
3004+ self.call_result_cb(ONLINE)
3005+ elif int(state) in NM_STATE_DISCONNECTED_LIST:
3006+ self.call_result_cb(OFFLINE)
3007+ else:
3008+ logger.debug("Not yet connected: continuing to wait")
3009+
3010+ def find_online_state(self):
3011+ """Get the network state and return it thru the set callback."""
3012+ try:
3013+ sysbus = self.dbus.SystemBus()
3014+ nm_proxy = sysbus.get_object(NM_DBUS_INTERFACE,
3015+ NM_DBUS_OBJECTPATH,
3016+ follow_name_owner_changes=True)
3017+ nm_if = self.dbus.Interface(nm_proxy, NM_DBUS_INTERFACE)
3018+ self.state_signal = nm_if.connect_to_signal(
3019+ signal_name="StateChanged",
3020+ handler_function=self.state_changed,
3021+ dbus_interface=NM_DBUS_INTERFACE)
3022+ nm_proxy.Get(NM_DBUS_INTERFACE, "State",
3023+ reply_handler=self.got_state,
3024+ error_handler=self.got_error)
3025+ except Exception as e:
3026+ self.got_error(e)
3027+
3028+
3029+def is_machine_connected():
3030+ """Return a deferred that when fired, returns if the machine is online."""
3031+ d = defer.Deferred()
3032+
3033+ def got_state(state):
3034+ """The state was retrieved from the Network Manager."""
3035+ if type(state) is not type(ONLINE):
3036+ logger.exception("bad callback argument in is_machine_connected")
3037+ raise NetworkFailException()
3038+ result = (state == ONLINE)
3039+ d.callback(result)
3040+
3041+ try:
3042+ network = NetworkManagerState(got_state)
3043+ network.find_online_state()
3044+ except Exception as e:
3045+ logger.exception('is_machine_connected failed with:')
3046+ d.errback(NetworkFailException(e))
3047+
3048+ return d
3049
3050=== added file 'ubuntuone/networkstate/networkstates.py'
3051--- ubuntuone/networkstate/networkstates.py 1970-01-01 00:00:00 +0000
3052+++ ubuntuone/networkstate/networkstates.py 2016-05-30 13:04:07 +0000
3053@@ -0,0 +1,59 @@
3054+# -*- coding: utf-8 -*-
3055+#
3056+# Copyright 2012 Canonical Ltd.
3057+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
3058+#
3059+# This program is free software: you can redistribute it and/or modify it
3060+# under the terms of the GNU General Public License version 3, as published
3061+# by the Free Software Foundation.
3062+#
3063+# This program is distributed in the hope that it will be useful, but
3064+# WITHOUT ANY WARRANTY; without even the implied warranties of
3065+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3066+# PURPOSE. See the GNU General Public License for more details.
3067+#
3068+# You should have received a copy of the GNU General Public License along
3069+# with this program. If not, see <http://www.gnu.org/licenses/>.
3070+
3071+"""Network states."""
3072+
3073+
3074+class NetworkState(object):
3075+ """ A simple class to add a label and make debugging easier. """
3076+
3077+ def __init__(self, label="unlabeled"):
3078+ self.label = label
3079+
3080+ def __repr__(self):
3081+ return "Network state (%s)" % self.label
3082+
3083+# Values returned by the callback
3084+(ONLINE, OFFLINE, UNKNOWN) = (NetworkState("online"),
3085+ NetworkState("offline"),
3086+ NetworkState("unknown"))
3087+
3088+# Internal NetworkManager State constants
3089+NM_STATE_UNKNOWN = 0
3090+NM_STATE_UNKNOWN_LIST = [NM_STATE_UNKNOWN]
3091+NM_STATE_ASLEEP_OLD = 1
3092+NM_STATE_ASLEEP = 10
3093+NM_STATE_ASLEEP_LIST = [NM_STATE_ASLEEP_OLD,
3094+ NM_STATE_ASLEEP]
3095+NM_STATE_CONNECTING_OLD = 2
3096+NM_STATE_CONNECTING = 40
3097+NM_STATE_CONNECTING_LIST = [NM_STATE_CONNECTING_OLD,
3098+ NM_STATE_CONNECTING]
3099+NM_STATE_CONNECTED_OLD = 3
3100+NM_STATE_CONNECTED_LOCAL = 50
3101+NM_STATE_CONNECTED_SITE = 60
3102+NM_STATE_CONNECTED_GLOBAL = 70
3103+# Specifically don't include local and site, as they won't let us get to server
3104+NM_STATE_CONNECTED_LIST = [NM_STATE_CONNECTED_OLD,
3105+ NM_STATE_CONNECTED_GLOBAL]
3106+NM_STATE_DISCONNECTED_OLD = 4
3107+NM_STATE_DISCONNECTED = 20
3108+# For us, local and site connections are the same as diconnected
3109+NM_STATE_DISCONNECTED_LIST = [NM_STATE_DISCONNECTED_OLD,
3110+ NM_STATE_DISCONNECTED,
3111+ NM_STATE_CONNECTED_LOCAL,
3112+ NM_STATE_CONNECTED_SITE]
3113
3114=== added directory 'ubuntuone/networkstate/tests'
3115=== added file 'ubuntuone/networkstate/tests/__init__.py'
3116--- ubuntuone/networkstate/tests/__init__.py 1970-01-01 00:00:00 +0000
3117+++ ubuntuone/networkstate/tests/__init__.py 2016-05-30 13:04:07 +0000
3118@@ -0,0 +1,31 @@
3119+# -*- coding: utf-8 -*-
3120+#
3121+# Copyright 2011-2012 Canonical Ltd.
3122+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
3123+#
3124+# This program is free software: you can redistribute it and/or modify it
3125+# under the terms of the GNU General Public License version 3, as published
3126+# by the Free Software Foundation.
3127+#
3128+# This program is distributed in the hope that it will be useful, but
3129+# WITHOUT ANY WARRANTY; without even the implied warranties of
3130+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3131+# PURPOSE. See the GNU General Public License for more details.
3132+#
3133+# You should have received a copy of the GNU General Public License along
3134+# with this program. If not, see <http://www.gnu.org/licenses/>.
3135+#
3136+# In addition, as a special exception, the copyright holders give
3137+# permission to link the code of portions of this program with the
3138+# OpenSSL library under certain conditions as described in each
3139+# individual source file, and distribute linked combinations
3140+# including the two.
3141+# You must obey the GNU General Public License in all respects
3142+# for all of the code used other than OpenSSL. If you modify
3143+# file(s) with this exception, you may extend this exception to your
3144+# version of the file(s), but you are not obligated to do so. If you
3145+# do not wish to do so, delete this exception statement from your
3146+# version. If you delete this exception statement from all source
3147+# files in the program, then also delete it here.
3148+
3149+"""Test the different networkstatus implementations."""
3150
3151=== added file 'ubuntuone/networkstate/tests/test_darwin.py'
3152--- ubuntuone/networkstate/tests/test_darwin.py 1970-01-01 00:00:00 +0000
3153+++ ubuntuone/networkstate/tests/test_darwin.py 2016-05-30 13:04:07 +0000
3154@@ -0,0 +1,186 @@
3155+# -*- coding: utf-8 -*-
3156+#
3157+# Copyright 2010-2012 Canonical Ltd.
3158+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
3159+#
3160+# This program is free software: you can redistribute it and/or modify it
3161+# under the terms of the GNU General Public License version 3, as published
3162+# by the Free Software Foundation.
3163+#
3164+# This program is distributed in the hope that it will be useful, but
3165+# WITHOUT ANY WARRANTY; without even the implied warranties of
3166+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3167+# PURPOSE. See the GNU General Public License for more details.
3168+#
3169+# You should have received a copy of the GNU General Public License along
3170+# with this program. If not, see <http://www.gnu.org/licenses/>.
3171+#
3172+# In addition, as a special exception, the copyright holders give
3173+# permission to link the code of portions of this program with the
3174+# OpenSSL library under certain conditions as described in each
3175+# individual source file, and distribute linked combinations
3176+# including the two.
3177+# You must obey the GNU General Public License in all respects
3178+# for all of the code used other than OpenSSL. If you modify
3179+# file(s) with this exception, you may extend this exception to your
3180+# version of the file(s), but you are not obligated to do so. If you
3181+# do not wish to do so, delete this exception statement from your
3182+# version. If you delete this exception statement from all source
3183+# files in the program, then also delete it here.
3184+
3185+"""Tests for the network state detection code."""
3186+
3187+from twisted.internet.defer import inlineCallbacks
3188+
3189+from ubuntuone.networkstate import (
3190+ darwin,
3191+ NetworkFailException,
3192+)
3193+from ubuntuone.networkstate.darwin import (
3194+ NetworkManagerState,
3195+ flags_say_reachable,
3196+ is_machine_connected,
3197+)
3198+from ubuntuone.networkstate.networkstates import (
3199+ ONLINE, OFFLINE, UNKNOWN,
3200+)
3201+from ubuntuone.tests import TestCase
3202+
3203+
3204+REACHABLE_FLAG = 1 << 1
3205+CONNECTION_REQUIRED_FLAG = 1 << 2
3206+
3207+
3208+class TestSCNRFailingInDirect(TestCase):
3209+
3210+ """Test that we handle a problem getting status in a direct call
3211+ to check_connected_state (used by is_machine_connected) by
3212+ raising an exception.
3213+ """
3214+
3215+ def test_cant_create_target(self):
3216+ """SCNRCreateWithName returning None should cause an exception."""
3217+ self.patch(darwin, "SCNRCreateWithName",
3218+ lambda _1, _2: None)
3219+ self.assertRaises(NetworkFailException,
3220+ darwin.check_connected_state)
3221+
3222+ def test_cant_get_flags(self):
3223+ """SCNRGetFlags returning False should cause an exception."""
3224+ self.patch(darwin, "SCNRGetFlags",
3225+ lambda _1, _2: False)
3226+ self.assertRaises(NetworkFailException,
3227+ darwin.check_connected_state)
3228+
3229+
3230+class TestFailingSCNRInCallbacks(TestCase):
3231+
3232+ """Test that we handle a problem getting status in the separate
3233+ listening thread by updating the status to UNKNOWN.
3234+ """
3235+
3236+ def expect_unknown(self, state):
3237+ """A convenience callback that fails unless it sees UNKNOWN."""
3238+ self.assertEquals(state, UNKNOWN)
3239+
3240+ def test_exc_in_find_online_state(self):
3241+ """Expect UNKNOWN from find_online_state in case of exception."""
3242+ def fake_check_connected_state():
3243+ "fake a broken check_connected_state"
3244+ raise NetworkFailException()
3245+
3246+ self.patch(darwin, "check_connected_state",
3247+ fake_check_connected_state)
3248+ NetworkManagerState(self.expect_unknown)
3249+
3250+ def test_cant_create_target(self):
3251+ """SCNRCreateWithName returning None -> callback gets UNKNOWN."""
3252+ self.patch(darwin, "SCNRCreateWithName", lambda _1, _2: None)
3253+ nms = NetworkManagerState(self.expect_unknown)
3254+ nms._listen_on_separate_thread()
3255+
3256+ def test_cant_set_callback(self):
3257+ """SCNRSetCallback returning false -> callback gets UNKNOWN."""
3258+ self.patch(darwin, "SCNRSetCallback", lambda _1, _2, _3: False)
3259+ nms = NetworkManagerState(self.expect_unknown)
3260+ nms._listen_on_separate_thread()
3261+
3262+ def test_cant_schedule_with_runloop(self):
3263+ """SCNRScheduleWithRunLoop returning false -> callback gets UNKNOWN."""
3264+ self.patch(darwin, "SCNRScheduleWithRunLoop",
3265+ lambda _1, _2, _3: False)
3266+ nms = NetworkManagerState(self.expect_unknown)
3267+ nms._listen_on_separate_thread()
3268+
3269+
3270+class TestReadingFlags(TestCase):
3271+ """Test interpretation of flags returned from SCNR API"""
3272+
3273+ def test_flag_reachable(self):
3274+ """Reachable by itself is OK."""
3275+ flag = REACHABLE_FLAG
3276+ self.assertTrue(flags_say_reachable(flag))
3277+
3278+ def test_flag_reachable_and_flag_connection_required(self):
3279+ """Reachable and connection-required is NOT OK"""
3280+ flag = REACHABLE_FLAG | CONNECTION_REQUIRED_FLAG
3281+ self.assertFalse(flags_say_reachable(flag))
3282+
3283+ def test_other_flagvals(self):
3284+ """All other flag configurations are false for our purposes.
3285+
3286+ They either indicate an iOS device, which we won't run this
3287+ code on, or that the server we're testing for is on this
3288+ machine or wired directly to it. These cases won't happen.
3289+ """
3290+ for flag in range(0, 17) + [1 << 16, 1 << 17, 1 << 18]:
3291+ # only test cases without the reachable bit set:
3292+ flag = flag & ~ 2
3293+ self.assertEqual(False, flags_say_reachable(flag))
3294+
3295+
3296+class TestNMSListeningForNWStateChanges(TestCase):
3297+ """
3298+ Test that the NetworkManagerState class calls the callback with
3299+ ONLINE/OFFLINE when the state changes appropriately
3300+ """
3301+
3302+ @inlineCallbacks
3303+ def setUp(self):
3304+ """Setup array to hold state changes."""
3305+ yield super(TestNMSListeningForNWStateChanges, self).setUp()
3306+ self.network_changes = []
3307+
3308+ def _listen_network_changes(self, state):
3309+ """Fake callback function, records state changes."""
3310+ self.network_changes.append(state)
3311+
3312+ def test_network_state_change(self):
3313+ """Test the changes in the network connection."""
3314+ nms = NetworkManagerState(self._listen_network_changes)
3315+ nms._state_changed(2)
3316+ nms._state_changed(0) # 0 or anything other than 2.
3317+ nms._state_changed(2)
3318+
3319+ self.assertEqual(self.network_changes,
3320+ [ONLINE, OFFLINE, ONLINE])
3321+
3322+
3323+class TestIsMachineConnectedFunc(TestCase):
3324+ """Simple test of is_machine_connected."""
3325+
3326+ @inlineCallbacks
3327+ def test_not_connected_returns_false(self):
3328+ """test that False comes back False"""
3329+ self.patch(darwin, "check_connected_state",
3330+ lambda: False)
3331+ con = yield is_machine_connected()
3332+ self.assertEqual(con, False)
3333+
3334+ @inlineCallbacks
3335+ def test_connected_returns_true(self):
3336+ """check that True comes back True"""
3337+ self.patch(darwin, "check_connected_state",
3338+ lambda: True)
3339+ con = yield is_machine_connected()
3340+ self.assertEqual(con, True)
3341
3342=== added file 'ubuntuone/networkstate/tests/test_linux.py'
3343--- ubuntuone/networkstate/tests/test_linux.py 1970-01-01 00:00:00 +0000
3344+++ ubuntuone/networkstate/tests/test_linux.py 2016-05-30 13:04:07 +0000
3345@@ -0,0 +1,366 @@
3346+# -*- coding: utf-8 -*-
3347+#
3348+# Copyright 2010-2012 Canonical Ltd.
3349+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
3350+#
3351+# This program is free software: you can redistribute it and/or modify it
3352+# under the terms of the GNU General Public License version 3, as published
3353+# by the Free Software Foundation.
3354+#
3355+# This program is distributed in the hope that it will be useful, but
3356+# WITHOUT ANY WARRANTY; without even the implied warranties of
3357+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3358+# PURPOSE. See the GNU General Public License for more details.
3359+#
3360+# You should have received a copy of the GNU General Public License along
3361+# with this program. If not, see <http://www.gnu.org/licenses/>.
3362+#
3363+# In addition, as a special exception, the copyright holders give
3364+# permission to link the code of portions of this program with the
3365+# OpenSSL library under certain conditions as described in each
3366+# individual source file, and distribute linked combinations
3367+# including the two.
3368+# You must obey the GNU General Public License in all respects
3369+# for all of the code used other than OpenSSL. If you modify
3370+# file(s) with this exception, you may extend this exception to your
3371+# version of the file(s), but you are not obligated to do so. If you
3372+# do not wish to do so, delete this exception statement from your
3373+# version. If you delete this exception statement from all source
3374+# files in the program, then also delete it here.
3375+
3376+"""Tests for the network state detection code."""
3377+
3378+from collections import defaultdict
3379+
3380+from twisted.internet.defer import inlineCallbacks
3381+from mocker import ARGS, KWARGS, ANY, MockerTestCase
3382+
3383+from ubuntuone.networkstate import (
3384+ linux,
3385+ NetworkFailException,
3386+ NetworkManagerState,
3387+)
3388+from ubuntuone.networkstate.linux import (
3389+ is_machine_connected,
3390+ NM_DBUS_INTERFACE,
3391+ NM_DBUS_OBJECTPATH,
3392+)
3393+from ubuntuone.networkstate.networkstates import (
3394+ ONLINE, OFFLINE, UNKNOWN,
3395+ NM_STATE_ASLEEP,
3396+ NM_STATE_ASLEEP_OLD,
3397+ NM_STATE_CONNECTING,
3398+ NM_STATE_CONNECTING_OLD,
3399+ NM_STATE_CONNECTED_OLD,
3400+ NM_STATE_CONNECTED_LOCAL,
3401+ NM_STATE_CONNECTED_SITE,
3402+ NM_STATE_CONNECTED_GLOBAL,
3403+ NM_STATE_DISCONNECTED,
3404+ NM_STATE_DISCONNECTED_OLD,
3405+ NM_STATE_UNKNOWN,
3406+)
3407+from ubuntuone.tests import TestCase
3408+
3409+
3410+class TestException(Exception):
3411+ """An exception to test error conditions."""
3412+ def get_dbus_name(self):
3413+ """A fake dbus name for this exception."""
3414+ return "Test Exception Message"
3415+
3416+
3417+class FakeNetworkManagerState(object):
3418+
3419+ """Fake Network Manager State."""
3420+
3421+ connection_state = None
3422+
3423+ def __init__(self, function):
3424+ """Initialize Fake class."""
3425+ self.call_function = function
3426+
3427+ def find_online_state(self):
3428+ """Fake find_online_state for linux module."""
3429+ self.call_function(self.connection_state)
3430+
3431+
3432+class FakeDBusMatch(object):
3433+
3434+ """Fake a DBus match."""
3435+
3436+ def __init__(self, name, callback, interface):
3437+ self.name = name
3438+ self.callback = callback
3439+ self.interface = interface
3440+ self.removed = False
3441+
3442+ def remove(self):
3443+ """Stop calling the handler function on remove."""
3444+ self.removed = True
3445+
3446+
3447+class FakeDBusInterface(object):
3448+
3449+ """Fake DBus Interface."""
3450+
3451+ def __init__(self):
3452+ self._signals = defaultdict(list)
3453+
3454+ def Get(self, *args, **kwargs):
3455+ """Fake Get."""
3456+
3457+ def connect_to_signal(self, signal_name, handler_function, dbus_interface):
3458+ """Fake connect_to_signal."""
3459+ match = FakeDBusMatch(signal_name, handler_function, dbus_interface)
3460+ self._signals[signal_name].append(match)
3461+ return match
3462+
3463+ def emit_signal(self, signal_name, state):
3464+ """Emit signal for network state change."""
3465+ for match in self._signals[signal_name]:
3466+ match.callback(state)
3467+
3468+
3469+class FakeSystemBus(object):
3470+
3471+ """Fake SystemBus."""
3472+
3473+ objects = {(NM_DBUS_INTERFACE, NM_DBUS_OBJECTPATH, True): object()}
3474+
3475+ def get_object(self, interface, object_path, follow_name_owner_changes):
3476+ """Fake get_object."""
3477+ key = (interface, object_path, follow_name_owner_changes)
3478+ return self.objects[key]
3479+
3480+
3481+class TestConnection(TestCase):
3482+
3483+ """Test the state of the connection.
3484+
3485+ This TestCase tests over all the connection states possible.
3486+
3487+ """
3488+
3489+ @inlineCallbacks
3490+ def setUp(self):
3491+ """Setup the mocker dbus object tree."""
3492+ yield super(TestConnection, self).setUp()
3493+ self.patch(linux, "NetworkManagerState", FakeNetworkManagerState)
3494+ self.patch(linux.dbus, 'SystemBus', FakeSystemBus)
3495+ self.nm_interface = FakeDBusInterface()
3496+ self.patch(linux.dbus, 'Interface', lambda *a: self.nm_interface)
3497+ self.network_changes = []
3498+
3499+ def _listen_network_changes(self, state):
3500+ """Fake callback function."""
3501+ self.network_changes.append(state)
3502+
3503+ def test_network_state_change(self):
3504+ """Test the changes in the network connection."""
3505+ nms = NetworkManagerState(self._listen_network_changes)
3506+
3507+ nms.find_online_state()
3508+
3509+ self.nm_interface.emit_signal('StateChanged',
3510+ NM_STATE_CONNECTED_GLOBAL)
3511+ self.nm_interface.emit_signal('StateChanged', NM_STATE_DISCONNECTED)
3512+ self.nm_interface.emit_signal('StateChanged',
3513+ NM_STATE_CONNECTED_GLOBAL)
3514+
3515+ self.assertEqual(nms.state_signal.name, "StateChanged")
3516+ self.assertEqual(nms.state_signal.callback, nms.state_changed)
3517+ self.assertEqual(nms.state_signal.interface,
3518+ "org.freedesktop.NetworkManager")
3519+ self.assertEqual(self.network_changes,
3520+ [ONLINE, ONLINE, OFFLINE, ONLINE])
3521+ self.assertFalse(nms.state_signal.removed)
3522+
3523+ @inlineCallbacks
3524+ def test_is_machine_connected_nm_state_online(self):
3525+ """Callback given ONLINE should mean we are online"""
3526+ self.patch(FakeNetworkManagerState, "connection_state",
3527+ ONLINE)
3528+ d = yield is_machine_connected()
3529+ self.assertTrue(d)
3530+
3531+ @inlineCallbacks
3532+ def test_is_machine_connected_nm_state_offline(self):
3533+ """Callback given OFFLINE should mean we are offline"""
3534+ self.patch(FakeNetworkManagerState, "connection_state",
3535+ OFFLINE)
3536+ d = yield is_machine_connected()
3537+ self.assertFalse(d)
3538+
3539+ @inlineCallbacks
3540+ def test_is_machine_connected_nm_state_unknown(self):
3541+ """Callback given ONLINE should mean we are not online"""
3542+ self.patch(FakeNetworkManagerState, "connection_state",
3543+ UNKNOWN)
3544+ d = yield is_machine_connected()
3545+ self.assertFalse(d)
3546+
3547+ @inlineCallbacks
3548+ def test_is_machine_connected_callback_error(self):
3549+ """Test bad argument to is_machine_connected's internal callback.
3550+
3551+ Passing anything other than ONLINE/OFFLINE/UNKNOWN should
3552+ cause an exception.
3553+ """
3554+ self.patch(FakeNetworkManagerState, "connection_state",
3555+ NM_STATE_CONNECTED_GLOBAL)
3556+ yield self.assertFailure(is_machine_connected(), NetworkFailException)
3557+
3558+
3559+class NetworkManagerBaseTestCase(MockerTestCase):
3560+ """Base test case for NM state tests."""
3561+
3562+ def setUp(self):
3563+ """Setup the dbus object tree."""
3564+ super(NetworkManagerBaseTestCase, self).setUp()
3565+
3566+ self.dbusmock = self.mocker.mock()
3567+ self.dbusmock.SystemBus()
3568+ sysbusmock = self.mocker.mock()
3569+ self.mocker.result(sysbusmock)
3570+
3571+ sysbusmock.get_object(ARGS, KWARGS)
3572+
3573+ def connect_proxy(self, exc=None):
3574+ """Get a proxy mock object, and allow failing with specified exc."""
3575+ proxymock = self.mocker.mock()
3576+ self.mocker.result(proxymock)
3577+
3578+ self.dbusmock.Interface(proxymock, ANY)
3579+ ifmock = self.mocker.mock()
3580+ self.mocker.result(ifmock)
3581+
3582+ ifmock.connect_to_signal(ARGS, KWARGS)
3583+ signalmock = self.mocker.mock()
3584+ self.mocker.result(signalmock)
3585+
3586+ proxymock.Get(ARGS, KWARGS)
3587+ if exc is not None:
3588+ self.mocker.result(exc)
3589+ self.mocker.replay()
3590+
3591+ def assertOnline(self, state):
3592+ """Check that the state given is ONLINE."""
3593+ self.assertEquals(state, ONLINE)
3594+
3595+ def assertOffline(self, state):
3596+ """Check that the state given is OFFLINE."""
3597+ self.assertEquals(state, OFFLINE)
3598+
3599+ def assertUnknown(self, state):
3600+ """Check that the state was UNKNOWN."""
3601+ self.assertEquals(state, UNKNOWN)
3602+
3603+ def get_nms(self, callback):
3604+ """Get the NetworkManagerState object."""
3605+ nms = NetworkManagerState(callback, self.dbusmock)
3606+ nms.find_online_state()
3607+ return nms
3608+
3609+ def check_nm_error(self, callback, error):
3610+ """Check that the error handling is correct."""
3611+ nms = self.get_nms(callback)
3612+ nms.got_error(error)
3613+
3614+ def check_nm_state(self, callback, state):
3615+ """Check the state handling is correct."""
3616+ nms = self.get_nms(callback)
3617+ nms.got_state(state)
3618+
3619+ def check_nm_state_change(self, callback, fmstate, tostate):
3620+ """Check the state change handling is correct."""
3621+ nms = self.get_nms(callback)
3622+ nms.got_state(fmstate)
3623+ nms.state_changed(tostate)
3624+
3625+
3626+class NetworkManagerStateTestCase(NetworkManagerBaseTestCase):
3627+ """Test NetworkManager state retrieval code."""
3628+
3629+ def setUp(self):
3630+ """Setup the dbus object tree."""
3631+ super(NetworkManagerStateTestCase, self).setUp()
3632+ self.connect_proxy()
3633+
3634+ def test_nm_asleep(self):
3635+ """Asleep status should mean offline."""
3636+ self.check_nm_state(self.assertOffline, NM_STATE_ASLEEP)
3637+
3638+ def test_nm_asleep_old(self):
3639+ """Asleep, old status, should mean offline."""
3640+ self.check_nm_state(self.assertOffline, NM_STATE_ASLEEP_OLD)
3641+
3642+ def test_nm_unknown(self):
3643+ """Unknown status should be treated the same as OFFLINE."""
3644+ self.check_nm_state(self.assertOffline, NM_STATE_UNKNOWN)
3645+
3646+ def test_nm_online_old(self):
3647+ """Check the connected, old status, case."""
3648+ self.check_nm_state(self.assertOnline, NM_STATE_CONNECTED_OLD)
3649+
3650+ def test_nm_offline_local(self):
3651+ """Check the connected, local status, case."""
3652+ self.check_nm_state(self.assertOffline, NM_STATE_CONNECTED_LOCAL)
3653+
3654+ def test_nm_offline_site(self):
3655+ """Check the connected, site status, case."""
3656+ self.check_nm_state(self.assertOffline, NM_STATE_CONNECTED_SITE)
3657+
3658+ def test_nm_online_global(self):
3659+ """Check the connected, global status, case."""
3660+ self.check_nm_state(self.assertOnline, NM_STATE_CONNECTED_GLOBAL)
3661+
3662+ def test_nm_offline_old(self):
3663+ """Check the disconnected, old status, case."""
3664+ self.check_nm_state(self.assertOffline, NM_STATE_DISCONNECTED_OLD)
3665+
3666+ def test_nm_offline(self):
3667+ """Check the disconnected case."""
3668+ self.check_nm_state(self.assertOffline, NM_STATE_DISCONNECTED)
3669+
3670+ def test_nm_connecting_then_online_old(self):
3671+ """Check the waiting for connection, old status, case."""
3672+ self.check_nm_state_change(self.assertOnline,
3673+ NM_STATE_CONNECTING_OLD,
3674+ NM_STATE_CONNECTED_OLD)
3675+
3676+ def test_nm_connecting_then_online(self):
3677+ """Check the waiting for connection case."""
3678+ self.check_nm_state_change(self.assertOnline,
3679+ NM_STATE_CONNECTING,
3680+ NM_STATE_CONNECTED_GLOBAL)
3681+
3682+ def test_nm_connecting_then_offline_old(self):
3683+ """Check the waiting but fail, old status, case."""
3684+ self.check_nm_state_change(self.assertOffline,
3685+ NM_STATE_CONNECTING_OLD,
3686+ NM_STATE_DISCONNECTED_OLD)
3687+
3688+ def test_nm_connecting_then_offline(self):
3689+ """Check the waiting but fail case."""
3690+ self.check_nm_state_change(self.assertOffline,
3691+ NM_STATE_CONNECTING, NM_STATE_DISCONNECTED)
3692+
3693+
3694+class NetworkManagerStateErrorsTestCase(NetworkManagerBaseTestCase):
3695+ """Test NetworkManager state retrieval code."""
3696+
3697+ def mock_except_while_getting_proxy(self, exc):
3698+ """Simulate an exception while getting the DBus proxy object."""
3699+ self.mocker.throw(exc)
3700+ self.mocker.result(exc)
3701+ self.mocker.replay()
3702+
3703+ def test_nm_check_errors(self):
3704+ """Trying to reach NM fails with some error."""
3705+ self.connect_proxy(Exception)
3706+ self.check_nm_error(self.assertOnline, Exception())
3707+
3708+ def test_dbus_problem(self):
3709+ """Check the case when DBus throws some other exception."""
3710+ self.mock_except_while_getting_proxy(TestException)
3711+ self.get_nms(self.assertOnline)
3712
3713=== added file 'ubuntuone/networkstate/tests/test_windows.py'
3714--- ubuntuone/networkstate/tests/test_windows.py 1970-01-01 00:00:00 +0000
3715+++ ubuntuone/networkstate/tests/test_windows.py 2016-05-30 13:04:07 +0000
3716@@ -0,0 +1,191 @@
3717+# -*- coding: utf-8 -*-
3718+#
3719+# Copyright 2011-2012 Canonical Ltd.
3720+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
3721+#
3722+# This program is free software: you can redistribute it and/or modify it
3723+# under the terms of the GNU General Public License version 3, as published
3724+# by the Free Software Foundation.
3725+#
3726+# This program is distributed in the hope that it will be useful, but
3727+# WITHOUT ANY WARRANTY; without even the implied warranties of
3728+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3729+# PURPOSE. See the GNU General Public License for more details.
3730+#
3731+# You should have received a copy of the GNU General Public License along
3732+# with this program. If not, see <http://www.gnu.org/licenses/>.
3733+#
3734+# In addition, as a special exception, the copyright holders give
3735+# permission to link the code of portions of this program with the
3736+# OpenSSL library under certain conditions as described in each
3737+# individual source file, and distribute linked combinations
3738+# including the two.
3739+# You must obey the GNU General Public License in all respects
3740+# for all of the code used other than OpenSSL. If you modify
3741+# file(s) with this exception, you may extend this exception to your
3742+# version of the file(s), but you are not obligated to do so. If you
3743+# do not wish to do so, delete this exception statement from your
3744+# version. If you delete this exception statement from all source
3745+# files in the program, then also delete it here.
3746+
3747+"""Tests for the network manager."""
3748+
3749+from ctypes import windll
3750+from mocker import MockerTestCase
3751+from twisted.internet.defer import inlineCallbacks
3752+
3753+from ubuntuone.networkstate import NetworkFailException
3754+from ubuntuone.networkstate.windows import (
3755+ is_machine_connected,
3756+ NetworkManager,
3757+ NetworkManagerState,
3758+ ONLINE,
3759+ OFFLINE)
3760+from ubuntuone.tests import TestCase
3761+
3762+
3763+class TestNetworkManager(MockerTestCase):
3764+ """Test he Network Manager."""
3765+
3766+ def setUp(self):
3767+ super(TestNetworkManager, self).setUp()
3768+ self.connection_info = self.mocker.mock()
3769+ self.connection_no_info = self.mocker.mock()
3770+ self.disconnected = self.mocker.mock()
3771+ self.manager = NetworkManager(self.connection_no_info,
3772+ self.connection_info, self.disconnected)
3773+
3774+ def test_connection_made(self):
3775+ """Ensure db is called."""
3776+ self.connection_info()
3777+ self.mocker.replay()
3778+ self.manager.ConnectionMade()
3779+
3780+ def test_connection_made_no_cb(self):
3781+ """Ensure db is called."""
3782+ self.manager.connected_cb_info = None
3783+ self.mocker.replay()
3784+ self.manager.ConnectionMade()
3785+
3786+ def test_connection_made_no_info(self):
3787+ """Ensure db is called."""
3788+ self.connection_no_info()
3789+ self.mocker.replay()
3790+ self.manager.ConnectionMadeNoQOCInfo()
3791+
3792+ def test_connection_made_no_info_no_cb(self):
3793+ """Ensure db is called."""
3794+ self.manager.connected_cb = None
3795+ self.mocker.replay()
3796+ self.manager.ConnectionMadeNoQOCInfo()
3797+
3798+ def test_disconnection(self):
3799+ """Ensure db is called."""
3800+ self.disconnected()
3801+ self.mocker.replay()
3802+ self.manager.ConnectionLost()
3803+
3804+ def test_disconnection_no_cb(self):
3805+ """Ensure db is called."""
3806+ self.manager.disconnected_cb = None
3807+ self.mocker.replay()
3808+ self.manager.ConnectionLost()
3809+
3810+
3811+class TestNetworkManagerState(MockerTestCase):
3812+ """Test the Network Manager State."""
3813+
3814+ def setUp(self):
3815+ super(TestNetworkManagerState, self).setUp()
3816+ self.network_manager = self.mocker.mock()
3817+ self.is_connected = self.mocker.replace(
3818+ 'ubuntuone.networkstate.windows.is_machine_connected')
3819+ self.thread = self.mocker.mock()
3820+ self.cb = self.mocker.mock()
3821+ self.state = NetworkManagerState(self.cb)
3822+
3823+ def test_connection_made(self):
3824+ """Test that the cb is actually called."""
3825+ self.cb(ONLINE)
3826+ self.mocker.replay()
3827+ self.state.connection_made()
3828+
3829+ def test_connection_lost(self):
3830+ """Test that the cb is actually called."""
3831+ self.cb(OFFLINE)
3832+ self.mocker.replay()
3833+ self.state.connection_lost()
3834+
3835+ def test_find_online_state_not_connected(self):
3836+ """Test that we do find the online state correctly."""
3837+ self.is_connected()
3838+ self.mocker.result(False)
3839+ self.cb(OFFLINE)
3840+ self.mocker.result(self.thread)
3841+ self.thread.daemon = True
3842+ self.thread.start()
3843+ self.mocker.replay()
3844+ self.state.find_online_state(listener=self.network_manager,
3845+ listener_thread=self.thread)
3846+
3847+ def test_find_online_state_connected(self):
3848+ """Test that we do find the online state correctly."""
3849+ self.is_connected()
3850+ self.mocker.result(ONLINE)
3851+ self.cb(ONLINE)
3852+ self.mocker.result(self.thread)
3853+ self.thread.daemon = True
3854+ self.thread.start()
3855+ self.mocker.replay()
3856+ self.state.find_online_state(listener=self.network_manager,
3857+ listener_thread=self.thread)
3858+
3859+
3860+class FakeWininet(object):
3861+ """Fake wininet for windll."""
3862+
3863+ connection_state = -1
3864+
3865+ def InternetGetConnectedState(self, *args, **kwargs):
3866+ """Fake InternetGetConnectedState function from wininet."""
3867+ return self.connection_state
3868+
3869+
3870+class FakeWininetException(object):
3871+ """Fake wininet for windll."""
3872+
3873+ connection_state = -1
3874+
3875+ def InternetGetConnectedState(self, *args, **kwargs):
3876+ """Fake InternetGetConnectedState function from wininet."""
3877+ raise Exception()
3878+
3879+
3880+class TestConnection(TestCase):
3881+ """Test the state of the connection."""
3882+
3883+ @inlineCallbacks
3884+ def setUp(self):
3885+ """Setup the mocker dbus object tree."""
3886+ yield super(TestConnection, self).setUp()
3887+ self.patch(windll, "wininet", FakeWininet())
3888+
3889+ @inlineCallbacks
3890+ def test_is_machine_connected_connected(self):
3891+ """Fake the NetworkManagerState."""
3892+ self.patch(FakeWininet, "connection_state", 1)
3893+ result = yield is_machine_connected()
3894+ self.assertTrue(result)
3895+
3896+ @inlineCallbacks
3897+ def test_is_machine_connected_disconnected(self):
3898+ """Fake the NetworkManagerState."""
3899+ self.patch(FakeWininet, "connection_state", 0)
3900+ result = yield is_machine_connected()
3901+ self.assertFalse(result)
3902+
3903+ @inlineCallbacks
3904+ def test_is_machine_connected_error(self):
3905+ """Fake the NetworkManagerState."""
3906+ self.patch(windll, "wininet", FakeWininetException())
3907+ yield self.assertFailure(is_machine_connected(), NetworkFailException)
3908
3909=== added file 'ubuntuone/networkstate/windows.py'
3910--- ubuntuone/networkstate/windows.py 1970-01-01 00:00:00 +0000
3911+++ ubuntuone/networkstate/windows.py 2016-05-30 13:04:07 +0000
3912@@ -0,0 +1,212 @@
3913+# -*- coding: utf-8 -*-
3914+#
3915+# Copyright 2011-2012 Canonical Ltd.
3916+# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
3917+#
3918+# This program is free software: you can redistribute it and/or modify it
3919+# under the terms of the GNU General Public License version 3, as published
3920+# by the Free Software Foundation.
3921+#
3922+# This program is distributed in the hope that it will be useful, but
3923+# WITHOUT ANY WARRANTY; without even the implied warranties of
3924+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3925+# PURPOSE. See the GNU General Public License for more details.
3926+#
3927+# You should have received a copy of the GNU General Public License along
3928+# with this program. If not, see <http://www.gnu.org/licenses/>.
3929+#
3930+# In addition, as a special exception, the copyright holders give
3931+# permission to link the code of portions of this program with the
3932+# OpenSSL library under certain conditions as described in each
3933+# individual source file, and distribute linked combinations
3934+# including the two.
3935+# You must obey the GNU General Public License in all respects
3936+# for all of the code used other than OpenSSL. If you modify
3937+# file(s) with this exception, you may extend this exception to your
3938+# version of the file(s), but you are not obligated to do so. If you
3939+# do not wish to do so, delete this exception statement from your
3940+# version. If you delete this exception statement from all source
3941+# files in the program, then also delete it here.
3942+
3943+"""Network status implementation on Windows."""
3944+
3945+
3946+import logging
3947+import pythoncom
3948+
3949+from ctypes import windll, byref
3950+from ctypes.wintypes import DWORD
3951+from threading import Thread
3952+
3953+from twisted.internet import defer
3954+from win32com.server.policy import DesignatedWrapPolicy
3955+from win32com.client import Dispatch
3956+
3957+from ubuntuone.networkstate import NetworkFailException
3958+from ubuntuone.networkstate.networkstates import ONLINE, OFFLINE
3959+
3960+
3961+logger = logging.getLogger(__name__)
3962+
3963+# naming errors are deliberated because we are following the COM naming to make
3964+# it clear for later developers.
3965+
3966+# from EventSys.h
3967+PROGID_EventSystem = "EventSystem.EventSystem"
3968+PROGID_EventSubscription = "EventSystem.EventSubscription"
3969+
3970+# SENS (System Event Notification Service) values for the events,
3971+# this events contain the uuid of the event, the name of the event to be used
3972+# as well as the method name of the method in the ISesNetwork interface that
3973+# will be executed for the event.
3974+# For more info look at:
3975+# http://msdn.microsoft.com/en-us/library/aa377384(v=vs.85).aspx
3976+
3977+SUBSCRIPTION_NETALIVE = ('{cd1dcbd6-a14d-4823-a0d2-8473afde360f}',
3978+ 'UbuntuOne Network Alive',
3979+ 'ConnectionMade')
3980+
3981+SUBSCRIPTION_NETALIVE_NOQOC = ('{a82f0e80-1305-400c-ba56-375ae04264a1}',
3982+ 'UbuntuOne Net Alive No Info',
3983+ 'ConnectionMadeNoQOCInfo')
3984+
3985+SUBSCRIPTION_NETLOST = ('{45233130-b6c3-44fb-a6af-487c47cee611}',
3986+ 'UbuntuOne Network Lost',
3987+ 'ConnectionLost')
3988+
3989+SUBSCRIPTION_REACH = ('{4c6b2afa-3235-4185-8558-57a7a922ac7b}',
3990+ 'UbuntuOne Network Reach',
3991+ 'ConnectionMade')
3992+
3993+SUBSCRIPTION_REACH_NOQOC = ('{db62fa23-4c3e-47a3-aef2-b843016177cf}',
3994+ 'UbuntuOne Network Reach No Info',
3995+ 'ConnectionMadeNoQOCInfo')
3996+
3997+SUBSCRIPTION_REACH_NOQOC2 = ('{d4d8097a-60c6-440d-a6da-918b619ae4b7}',
3998+ 'UbuntuOne Network Reach No Info 2',
3999+ 'ConnectionMadeNoQOCInfo')
4000+
4001+SUBSCRIPTIONS = [SUBSCRIPTION_NETALIVE,
4002+ SUBSCRIPTION_NETALIVE_NOQOC,
4003+ SUBSCRIPTION_NETLOST,
4004+ SUBSCRIPTION_REACH,
4005+ SUBSCRIPTION_REACH_NOQOC,
4006+ SUBSCRIPTION_REACH_NOQOC2]
4007+
4008+SENSGUID_EVENTCLASS_NETWORK = '{d5978620-5b9f-11d1-8dd2-00aa004abd5e}'
4009+SENSGUID_PUBLISHER = "{5fee1bd6-5b9b-11d1-8dd2-00aa004abd5e}"
4010+
4011+# uuid of the implemented com interface
4012+IID_ISesNetwork = '{d597bab1-5b9f-11d1-8dd2-00aa004abd5e}'
4013+
4014+
4015+class NetworkManager(DesignatedWrapPolicy):
4016+ """Implement ISesNetwork to know about the network status."""
4017+
4018+ _com_interfaces_ = [IID_ISesNetwork]
4019+ _public_methods_ = ['ConnectionMade',
4020+ 'ConnectionMadeNoQOCInfo',
4021+ 'ConnectionLost']
4022+ _reg_clsid_ = '{41B032DA-86B5-4907-A7F7-958E59333010}'
4023+ _reg_progid_ = "UbuntuOne.NetworkManager"
4024+
4025+ def __init__(self, connected_cb=None, connected_cb_info=None,
4026+ disconnected_cb=None):
4027+ self._wrap_(self)
4028+ self.connected_cb = connected_cb
4029+ self.connected_cb_info = connected_cb_info
4030+ self.disconnected_cb = disconnected_cb
4031+
4032+ def ConnectionMade(self, *args):
4033+ """Tell that the connection is up again."""
4034+ logger.info('Connection was made.')
4035+ if self.connected_cb_info:
4036+ self.connected_cb_info()
4037+
4038+ def ConnectionMadeNoQOCInfo(self, *args):
4039+ """Tell that the connection is up again."""
4040+ logger.info('Connection was made no info.')
4041+ if self.connected_cb:
4042+ self.connected_cb()
4043+
4044+ def ConnectionLost(self, *args):
4045+ """Tell the connection was lost."""
4046+ logger.info('Connection was lost.')
4047+ if self.disconnected_cb:
4048+ self.disconnected_cb()
4049+
4050+ def register(self):
4051+ """Register to listen to network events."""
4052+ # call the CoInitialize to allow the registration to run in another
4053+ # thread
4054+ pythoncom.CoInitialize()
4055+ # interface to be used by com
4056+ manager_interface = pythoncom.WrapObject(self)
4057+ event_system = Dispatch(PROGID_EventSystem)
4058+ # register to listen to each of the events to make sure that
4059+ # the code will work on all platforms.
4060+ for current_event in SUBSCRIPTIONS:
4061+ # create an event subscription and add it to the event
4062+ # service
4063+ event_subscription = Dispatch(PROGID_EventSubscription)
4064+ event_subscription.EventClassId = SENSGUID_EVENTCLASS_NETWORK
4065+ event_subscription.PublisherID = SENSGUID_PUBLISHER
4066+ event_subscription.SubscriptionID = current_event[0]
4067+ event_subscription.SubscriptionName = current_event[1]
4068+ event_subscription.MethodName = current_event[2]
4069+ event_subscription.SubscriberInterface = manager_interface
4070+ event_subscription.PerUser = True
4071+ # store the event
4072+ try:
4073+ event_system.Store(PROGID_EventSubscription,
4074+ event_subscription)
4075+ except pythoncom.com_error as e:
4076+ logger.error(
4077+ 'Error registering %s to event %s', e, current_event[1])
4078+
4079+ pythoncom.PumpMessages()
4080+
4081+
4082+def is_machine_connected():
4083+ """Return a deferred that when fired, returns if the machine is online."""
4084+ try:
4085+ wininet = windll.wininet
4086+ flags = DWORD()
4087+ connected = wininet.InternetGetConnectedState(byref(flags), None)
4088+ return defer.succeed(connected == 1)
4089+ except Exception as e:
4090+ logger.exception('is_machine_connected failed with:')
4091+ return defer.fail(NetworkFailException(e))
4092+
4093+
4094+class NetworkManagerState(object):
4095+ """Check for status changed in the network on Windows."""
4096+
4097+ def __init__(self, result_cb, **kwargs):
4098+ """Initialize this instance with a result and error callbacks."""
4099+ self.result_cb = result_cb
4100+
4101+ def connection_made(self):
4102+ """Return the connection state over the call back."""
4103+ self.result_cb(ONLINE)
4104+
4105+ def connection_lost(self):
4106+ """Return the connection was lost over the call back."""
4107+ self.result_cb(OFFLINE)
4108+
4109+ def find_online_state(self, listener=None, listener_thread=None):
4110+ """Get the network state and return it thru the set callback."""
4111+ # check the current status right now
4112+ if is_machine_connected():
4113+ self.result_cb(ONLINE)
4114+ else:
4115+ self.result_cb(OFFLINE)
4116+ if listener is None:
4117+ # start listening for network changes
4118+ listener = NetworkManager(connected_cb=self.connection_made,
4119+ disconnected_cb=self.connection_lost)
4120+ if listener_thread is None:
4121+ listener_thread = Thread(target=listener.register,
4122+ name="Network Connection Monitor")
4123+ listener_thread.daemon = True
4124+ listener_thread.start()
4125
4126=== removed directory 'ubuntuone/platform/credentials'
4127=== removed file 'ubuntuone/platform/credentials/__init__.py'
4128--- ubuntuone/platform/credentials/__init__.py 2015-09-29 21:05:26 +0000
4129+++ ubuntuone/platform/credentials/__init__.py 1970-01-01 00:00:00 +0000
4130@@ -1,404 +0,0 @@
4131-# -*- coding: utf-8 -*-
4132-#
4133-# Copyright 2011-2012 Canonical Ltd.
4134-#
4135-# This program is free software: you can redistribute it and/or modify it
4136-# under the terms of the GNU General Public License version 3, as published
4137-# by the Free Software Foundation.
4138-#
4139-# This program is distributed in the hope that it will be useful, but
4140-# WITHOUT ANY WARRANTY; without even the implied warranties of
4141-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4142-# PURPOSE. See the GNU General Public License for more details.
4143-#
4144-# You should have received a copy of the GNU General Public License along
4145-# with this program. If not, see <http://www.gnu.org/licenses/>.
4146-#
4147-# In addition, as a special exception, the copyright holders give
4148-# permission to link the code of portions of this program with the
4149-# OpenSSL library under certain conditions as described in each
4150-# individual source file, and distribute linked combinations
4151-# including the two.
4152-# You must obey the GNU General Public License in all respects
4153-# for all of the code used other than OpenSSL. If you modify
4154-# file(s) with this exception, you may extend this exception to your
4155-# version of the file(s), but you are not obligated to do so. If you
4156-# do not wish to do so, delete this exception statement from your
4157-# version. If you delete this exception statement from all source
4158-# files in the program, then also delete it here.
4159-
4160-"""Common code for the credentials management."""
4161-
4162-import gettext
4163-import logging
4164-import os
4165-import platform
4166-import urllib
4167-import sys
4168-
4169-from functools import partial
4170-
4171-from twisted.internet import defer
4172-
4173-from ubuntu_sso import UI_EXECUTABLE_QT
4174-from ubuntu_sso.credentials import (
4175- PING_URL_KEY,
4176- POLICY_URL_KEY,
4177- UI_EXECUTABLE_KEY,
4178- TC_URL_KEY,
4179-)
4180-
4181-from ubuntuone import clientdefs
4182-from ubuntuone.logger import (
4183- basic_formatter,
4184- CustomRotatingFileHandler,
4185- log_call,
4186-)
4187-from ubuntuone.platform.logger import ubuntuone_log_dir
4188-
4189-LOG_LEVEL = logging.DEBUG
4190-path = os.path.join(ubuntuone_log_dir, 'credentials.log')
4191-MAIN_HANDLER = CustomRotatingFileHandler(path)
4192-MAIN_HANDLER.setFormatter(basic_formatter)
4193-MAIN_HANDLER.setLevel(LOG_LEVEL)
4194-
4195-logger = logging.getLogger("ubuntuone.credentials")
4196-logger.setLevel(LOG_LEVEL)
4197-logger.addHandler(MAIN_HANDLER)
4198-
4199-NO_OP = lambda *args, **kwargs: None
4200-Q_ = lambda string: gettext.dgettext(clientdefs.GETTEXT_PACKAGE, string)
4201-APP_NAME = u"Magicicada"
4202-TC_URL = u"https://one.ubuntu.com/terms/"
4203-POLICY_URL = u"https://one.ubuntu.com/privacy/"
4204-
4205-
4206-def platform_data():
4207- result = {'platform': platform.system(),
4208- 'platform_version': platform.release(),
4209- 'platform_arch': platform.machine(),
4210- 'client_version': clientdefs.VERSION}
4211- # urlencode will not encode unicode, only bytes
4212- result = urllib.urlencode(result)
4213- return result
4214-
4215-
4216-BASE_PING_URL = \
4217- u"https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/{email}"
4218-# the result of platform_data is given by urlencode, encoded with ascii
4219-PING_URL = BASE_PING_URL + u"?" + platform_data().decode('ascii')
4220-UI_PARAMS = {
4221- PING_URL_KEY: PING_URL,
4222- POLICY_URL_KEY: POLICY_URL,
4223- TC_URL_KEY: TC_URL,
4224- UI_EXECUTABLE_KEY: UI_EXECUTABLE_QT,
4225-}
4226-
4227-
4228-class CredentialsError(Exception):
4229- """A general exception when hadling credentilas."""
4230-
4231-
4232-class CredentialsManagementTool(object):
4233- """Wrapper to CredentialsManagement.
4234-
4235- The goal of this class is to abstract the caller from calling the IPC
4236- service implemented in the class CredentialsManagement.
4237-
4238- """
4239-
4240- def __init__(self):
4241- self._cleanup_signals = []
4242- self._proxy = None
4243-
4244- def callback(self, result, deferred):
4245- """Fire 'deferred' with success, sending 'result' as result."""
4246- deferred.callback(result)
4247-
4248- def errback(self, error, deferred):
4249- """Fire 'deferred' with error sending a CredentialsError."""
4250- deferred.errback(CredentialsError(error))
4251-
4252- def cleanup(self, _):
4253- """Disconnect all the DBus signals."""
4254- for sig in self._cleanup_signals:
4255- logger.debug('cleanup: removing signal match %r', sig)
4256- remove = getattr(sig, "remove", None)
4257- if remove:
4258- remove()
4259-
4260- return _
4261-
4262- def get_platform_source(self):
4263- """Platform-specific source."""
4264- if sys.platform in ('win32', 'darwin'):
4265- from ubuntuone.platform.credentials import ipc_service
4266- source = ipc_service
4267- else:
4268- from ubuntuone.platform.credentials import dbus_service
4269- source = dbus_service
4270- return source
4271-
4272- @defer.inlineCallbacks
4273- def get_creds_proxy(self):
4274- """Call the platform-dependent get_creds_proxy caching the result."""
4275- if self._proxy is None:
4276- source = self.get_platform_source()
4277- self._proxy = yield source.get_creds_proxy()
4278- defer.returnValue(self._proxy)
4279-
4280- # do not log returned credentials
4281- @log_call(logger.debug, with_result=False)
4282- @defer.inlineCallbacks
4283- def find_credentials(self):
4284- """Find credentials for Magicicada.
4285-
4286- Return a deferred that, when fired, will return the credentials for
4287- Magicicada for the current logged in user.
4288-
4289- The credentials is a dictionary with both string keys and values. The
4290- dictionary may be either empty if there are no credentials for the
4291- user, or will hold five items as follow:
4292-
4293- - "name"
4294- - "token"
4295- - "token_secret"
4296- - "consumer_key"
4297- - "consumer_secret"
4298-
4299- """
4300- d = defer.Deferred()
4301- d.addBoth(self.cleanup)
4302-
4303- proxy = yield self.get_creds_proxy()
4304-
4305- sig = proxy.connect_to_signal('CredentialsFound', d.callback)
4306- self._cleanup_signals.append(sig)
4307-
4308- sig = proxy.connect_to_signal(
4309- 'CredentialsNotFound',
4310- partial(self.callback, result={}, deferred=d))
4311- self._cleanup_signals.append(sig)
4312-
4313- sig = proxy.connect_to_signal(
4314- 'CredentialsError', partial(self.errback, deferred=d))
4315- self._cleanup_signals.append(sig)
4316-
4317- done = defer.Deferred()
4318- proxy.find_credentials(
4319- reply_handler=partial(self.callback, result=None, deferred=done),
4320- error_handler=partial(self.errback, deferred=done))
4321-
4322- yield done
4323-
4324- result = yield d
4325- defer.returnValue(result)
4326-
4327- @log_call(logger.debug)
4328- @defer.inlineCallbacks
4329- def clear_credentials(self):
4330- """Clear credentials for Magicicada.
4331-
4332- Return a deferred that, when fired, will return no result but will
4333- indicate that the Magicicada credentials for the current user were
4334- removed from the local keyring.
4335-
4336- """
4337- d = defer.Deferred()
4338- d.addBoth(self.cleanup)
4339-
4340- proxy = yield self.get_creds_proxy()
4341-
4342- sig = proxy.connect_to_signal(
4343- 'CredentialsCleared',
4344- partial(self.callback, result=None, deferred=d))
4345- self._cleanup_signals.append(sig)
4346-
4347- sig = proxy.connect_to_signal(
4348- 'CredentialsError', partial(self.errback, deferred=d))
4349- self._cleanup_signals.append(sig)
4350-
4351- done = defer.Deferred()
4352- proxy.clear_credentials(
4353- reply_handler=partial(self.callback, result=None, deferred=done),
4354- error_handler=partial(self.errback, deferred=done))
4355-
4356- yield done
4357-
4358- yield d
4359-
4360- # do not log token
4361- @log_call(logger.debug, with_args=False)
4362- @defer.inlineCallbacks
4363- def store_credentials(self, token):
4364- """Store credentials for Magicicada.
4365-
4366- The parameter 'token' should be a dictionary that matches the
4367- description of the result of 'find_credentials'.
4368-
4369- Return a deferred that, when fired, will return no result but will
4370- indicate that 'token' was stored in the local keyring as the new Ubuntu
4371- One credentials for the current user.
4372-
4373- """
4374- d = defer.Deferred()
4375- d.addBoth(self.cleanup)
4376-
4377- proxy = yield self.get_creds_proxy()
4378-
4379- sig = proxy.connect_to_signal(
4380- 'CredentialsStored',
4381- partial(self.callback, result=None, deferred=d))
4382- self._cleanup_signals.append(sig)
4383-
4384- sig = proxy.connect_to_signal(
4385- 'CredentialsError', partial(self.errback, deferred=d))
4386- self._cleanup_signals.append(sig)
4387-
4388- done = defer.Deferred()
4389- proxy.store_credentials(
4390- token,
4391- reply_handler=partial(self.callback, result=None, deferred=done),
4392- error_handler=partial(self.errback, deferred=done))
4393-
4394- yield done
4395-
4396- yield d
4397-
4398- # do not log returned credentials
4399- @log_call(logger.debug, with_result=False)
4400- @defer.inlineCallbacks
4401- def register(self, window_id=0):
4402- """Register to Magicicada.
4403-
4404- Return a deferred that, when fired, will return the credentials for
4405- Magicicada for the current logged in user.
4406-
4407- If there are no credentials for the current user, a GTK UI will be
4408- opened to invite the user to register to Magicicada. This UI provides
4409- options to either register (main screen) or login (secondary screen).
4410-
4411- You can pass an optional 'window_id' parameter that will be used by the
4412- GTK UI to be set transient for it.
4413-
4414- The returned credentials will be either a non-empty dictionary like the
4415- one described in 'find_credentials', or None. The latter indicates that
4416- there were no credentials for the user in the local keyring and that
4417- the user refused to register to Magicicada.
4418-
4419- """
4420- d = defer.Deferred()
4421- d.addBoth(self.cleanup)
4422-
4423- proxy = yield self.get_creds_proxy()
4424-
4425- sig = proxy.connect_to_signal('CredentialsFound', d.callback)
4426- self._cleanup_signals.append(sig)
4427-
4428- sig = proxy.connect_to_signal(
4429- 'AuthorizationDenied',
4430- partial(self.callback, result=None, deferred=d))
4431- self._cleanup_signals.append(sig)
4432-
4433- sig = proxy.connect_to_signal(
4434- 'CredentialsError', partial(self.errback, deferred=d))
4435- self._cleanup_signals.append(sig)
4436-
4437- done = defer.Deferred()
4438- proxy.register(
4439- {'window_id': str(window_id)},
4440- reply_handler=partial(self.callback, result=None, deferred=done),
4441- error_handler=partial(self.errback, deferred=done))
4442-
4443- yield done
4444-
4445- result = yield d
4446- defer.returnValue(result)
4447-
4448- # do not log returned credentials
4449- @log_call(logger.debug, with_result=False)
4450- @defer.inlineCallbacks
4451- def login(self, window_id=0):
4452- """Login to Magicicada.
4453-
4454- Return a deferred that, when fired, will return the credentials for
4455- Magicicada for the current logged in user.
4456-
4457- If there are no credentials for the current user, a GTK UI will be
4458- opened to invite the user to login to Magicicada. This UI provides
4459- options to either login (main screen) or retrieve password (secondary
4460- screen).
4461-
4462- You can pass an optional 'window_id' parameter that will be used by the
4463- GTK UI to be set transient for it.
4464-
4465- The returned credentials will be either a non-empty dictionary like the
4466- one described in 'find_credentials', or None. The latter indicates that
4467- there were no credentials for the user in the local keyring and that
4468- the user refused to login to Magicicada.
4469-
4470- """
4471- d = defer.Deferred()
4472- d.addBoth(self.cleanup)
4473-
4474- proxy = yield self.get_creds_proxy()
4475-
4476- sig = proxy.connect_to_signal('CredentialsFound', d.callback)
4477- self._cleanup_signals.append(sig)
4478-
4479- sig = proxy.connect_to_signal(
4480- 'AuthorizationDenied',
4481- partial(self.callback, result=None, deferred=d))
4482- self._cleanup_signals.append(sig)
4483-
4484- sig = proxy.connect_to_signal(
4485- 'CredentialsError', partial(self.errback, deferred=d))
4486- self._cleanup_signals.append(sig)
4487-
4488- done = defer.Deferred()
4489- proxy.login(
4490- {'window_id': str(window_id)},
4491- reply_handler=partial(self.callback, result=None, deferred=done),
4492- error_handler=partial(self.errback, deferred=done))
4493-
4494- yield done
4495-
4496- result = yield d
4497- defer.returnValue(result)
4498-
4499- # do not log password nor returned credentials
4500- @log_call(logger.debug, with_args=False, with_result=False)
4501- @defer.inlineCallbacks
4502- def login_email_password(self, email, password):
4503- """Login to Magicicada.
4504-
4505- Return a deferred that, when fired, will return the credentials for
4506- Magicicada for the given email and password.
4507-
4508- The returned credentials will be either a non-empty dictionary like the
4509- one described in 'find_credentials', or None. The latter indicates
4510- invalid or wrong user/password.
4511-
4512- """
4513- d = defer.Deferred()
4514- d.addBoth(self.cleanup)
4515-
4516- proxy = yield self.get_creds_proxy()
4517-
4518- sig = proxy.connect_to_signal('CredentialsFound', d.callback)
4519- self._cleanup_signals.append(sig)
4520-
4521- sig = proxy.connect_to_signal(
4522- 'CredentialsError', partial(self.errback, deferred=d))
4523- self._cleanup_signals.append(sig)
4524-
4525- done = defer.Deferred()
4526- proxy.login_email_password(
4527- {'email': email, 'password': password},
4528- reply_handler=partial(self.callback, result=None, deferred=done),
4529- error_handler=partial(self.errback, deferred=done))
4530-
4531- yield done
4532-
4533- result = yield d
4534- defer.returnValue(result)
4535
4536=== removed file 'ubuntuone/platform/credentials/dbus_service.py'
4537--- ubuntuone/platform/credentials/dbus_service.py 2015-09-19 23:15:50 +0000
4538+++ ubuntuone/platform/credentials/dbus_service.py 1970-01-01 00:00:00 +0000
4539@@ -1,285 +0,0 @@
4540-# -*- coding: utf-8 -*-
4541-#
4542-# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
4543-#
4544-# Copyright 2010-2012 Canonical Ltd.
4545-#
4546-# This program is free software: you can redistribute it and/or modify it
4547-# under the terms of the GNU General Public License version 3, as published
4548-# by the Free Software Foundation.
4549-#
4550-# This program is distributed in the hope that it will be useful, but
4551-# WITHOUT ANY WARRANTY; without even the implied warranties of
4552-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4553-# PURPOSE. See the GNU General Public License for more details.
4554-#
4555-# You should have received a copy of the GNU General Public License along
4556-# with this program. If not, see <http://www.gnu.org/licenses/>.
4557-#
4558-# In addition, as a special exception, the copyright holders give
4559-# permission to link the code of portions of this program with the
4560-# OpenSSL library under certain conditions as described in each
4561-# individual source file, and distribute linked combinations
4562-# including the two.
4563-# You must obey the GNU General Public License in all respects
4564-# for all of the code used other than OpenSSL. If you modify
4565-# file(s) with this exception, you may extend this exception to your
4566-# version of the file(s), but you are not obligated to do so. If you
4567-# do not wish to do so, delete this exception statement from your
4568-# version. If you delete this exception statement from all source
4569-# files in the program, then also delete it here.
4570-"""Magicicada credentials management dbus service."""
4571-
4572-import dbus
4573-import dbus.service
4574-import ubuntu_sso
4575-
4576-from ubuntuone.platform.credentials import (
4577- APP_NAME,
4578- logger,
4579- NO_OP,
4580- UI_PARAMS,
4581-)
4582-
4583-
4584-TIMEOUT_INTERVAL = 10000 # 10 seconds
4585-
4586-# constants
4587-DBUS_BUS_NAME = "com.ubuntuone.Credentials"
4588-DBUS_CREDENTIALS_PATH = "/credentials"
4589-DBUS_CREDENTIALS_IFACE = "com.ubuntuone.CredentialsManagement"
4590-
4591-
4592-class CredentialsManagement(dbus.service.Object):
4593- """DBus object that manages Magicicada credentials."""
4594-
4595- def __init__(self, timeout_func=lambda *a: None,
4596- shutdown_func=lambda *a: None, *args, **kwargs):
4597- super(CredentialsManagement, self).__init__(*args, **kwargs)
4598- self._ref_count = 0
4599- self.timeout_func = timeout_func
4600- self.shutdown_func = shutdown_func
4601-
4602- self.sso_match = None
4603- self.sso_proxy = self._get_sso_proxy()
4604-
4605- def _signal_handler(self, *args, **kwargs):
4606- """Generic signal handler."""
4607- member = kwargs.get('member', None)
4608- app_name = args[0] if len(args) > 0 else None
4609- logger.debug('Handling DBus signal for member: %r, app_name: %r.',
4610- member, app_name)
4611-
4612- if app_name != APP_NAME:
4613- logger.info('Received %r but app_name %r does not match %r, '
4614- 'exiting.', member, app_name, APP_NAME)
4615- return
4616-
4617- sig = getattr(self, member)
4618-
4619- if member in ('CredentialsFound', 'CredentialsError'):
4620- # this are the only signals that will forward the parameter
4621- logger.info('%r', member)
4622- arg = args[1]
4623- sig(arg)
4624- else:
4625- sig()
4626-
4627- def _get_sso_proxy(self):
4628- """Get the SSO dbus proxy."""
4629- bus = dbus.SessionBus()
4630- # register signal handlers for each kind of error
4631- self.sso_match = bus.add_signal_receiver(
4632- self._signal_handler, member_keyword='member',
4633- dbus_interface=ubuntu_sso.DBUS_CREDENTIALS_IFACE)
4634- try:
4635- obj = bus.get_object(ubuntu_sso.DBUS_BUS_NAME,
4636- ubuntu_sso.DBUS_CREDENTIALS_PATH,
4637- follow_name_owner_changes=True)
4638- proxy = dbus.Interface(obj, ubuntu_sso.DBUS_CREDENTIALS_IFACE)
4639- except:
4640- logger.exception('get_sso_proxy:')
4641- raise
4642-
4643- return proxy
4644-
4645- def _get_ref_count(self):
4646- """Get value of ref_count."""
4647- return self._ref_count
4648-
4649- def _set_ref_count(self, new_value):
4650- """Set a new value to ref_count."""
4651- logger.debug('ref_count is %r, changing value to %r.',
4652- self._ref_count, new_value)
4653- if new_value < 0:
4654- self._ref_count = 0
4655- msg = 'Attempting to decrease ref_count to a negative value (%r).'
4656- logger.warning(msg, new_value)
4657- else:
4658- self._ref_count = new_value
4659-
4660- if self._ref_count == 0:
4661- logger.debug('Setting up timer with %r (%r, %r).',
4662- self.timeout_func, TIMEOUT_INTERVAL, self.shutdown)
4663- self.timeout_func(TIMEOUT_INTERVAL, self.shutdown)
4664-
4665- ref_count = property(fget=_get_ref_count, fset=_set_ref_count)
4666-
4667- def shutdown(self):
4668- """If no ongoing requests, call self.shutdown_func."""
4669- logger.debug('shutdown!, ref_count is %r.', self._ref_count)
4670- if self._ref_count == 0:
4671- logger.info('Shutting down, calling %r.', self.shutdown_func)
4672- self.shutdown_func()
4673-
4674- # Operator not preceded by a space (fails with dbus decorators)
4675-
4676- @dbus.service.signal(DBUS_CREDENTIALS_IFACE)
4677- def AuthorizationDenied(self):
4678- """Signal thrown when the user denies the authorization."""
4679- self.ref_count -= 1
4680- logger.info('%s: emitting AuthorizationDenied.',
4681- self.__class__.__name__)
4682-
4683- @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='a{ss}')
4684- def CredentialsFound(self, credentials):
4685- """Signal thrown when the credentials are found."""
4686- self.ref_count -= 1
4687- logger.info('%s: emitting CredentialsFound.',
4688- self.__class__.__name__)
4689-
4690- @dbus.service.signal(DBUS_CREDENTIALS_IFACE)
4691- def CredentialsNotFound(self):
4692- """Signal thrown when the credentials are not found."""
4693- self.ref_count -= 1
4694- logger.info('%s: emitting CredentialsNotFound.',
4695- self.__class__.__name__)
4696-
4697- @dbus.service.signal(DBUS_CREDENTIALS_IFACE)
4698- def CredentialsCleared(self):
4699- """Signal thrown when the credentials were cleared."""
4700- self.ref_count -= 1
4701- logger.info('%s: emitting CredentialsCleared.',
4702- self.__class__.__name__)
4703-
4704- @dbus.service.signal(DBUS_CREDENTIALS_IFACE)
4705- def CredentialsStored(self):
4706- """Signal thrown when the credentials were cleared."""
4707- self.ref_count -= 1
4708- logger.info('%s: emitting CredentialsStored.',
4709- self.__class__.__name__)
4710-
4711- @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='a{ss}')
4712- def CredentialsError(self, error_dict):
4713- """Signal thrown when there is a problem getting the credentials."""
4714- self.ref_count -= 1
4715- logger.error('%s: emitting CredentialsError with error_dict %r.',
4716- self.__class__.__name__, error_dict)
4717-
4718- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4719- async_callbacks=("reply_handler", "error_handler"))
4720- def find_credentials(self, reply_handler=NO_OP, error_handler=NO_OP):
4721- """Ask the Magicicada credentials."""
4722- self.ref_count += 1
4723- self.sso_proxy.find_credentials(
4724- APP_NAME, dbus.Dictionary({}, signature='ss'),
4725- reply_handler=reply_handler, error_handler=error_handler)
4726-
4727- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4728- out_signature="a{ss}",
4729- async_callbacks=("reply_handler", "error_handler"))
4730- def find_credentials_sync(self, reply_handler=NO_OP, error_handler=NO_OP):
4731- """Ask the Magicicada credentials synchronously.
4732-
4733- This method SHOULD NOT be used, is here only for compatibilty issues.
4734-
4735- """
4736-
4737- def decrease_counter_success(credentials):
4738- """Call 'callback' and decrease the root ref counter."""
4739- reply_handler(credentials)
4740- self.ref_count -= 1
4741-
4742- def decrease_counter_error(error):
4743- """Call 'errback' and decrease the root ref counter."""
4744- error_handler(error)
4745- self.ref_count -= 1
4746-
4747- self.ref_count += 1
4748- self.sso_proxy.find_credentials_sync(
4749- APP_NAME,
4750- dbus.Dictionary({}, signature='ss'),
4751- reply_handler=decrease_counter_success,
4752- error_handler=decrease_counter_error)
4753-
4754- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4755- async_callbacks=("reply_handler", "error_handler"))
4756- def clear_credentials(self, reply_handler=NO_OP, error_handler=NO_OP):
4757- """Clear the Magicicada credentials."""
4758- self.ref_count += 1
4759- self.sso_proxy.clear_credentials(
4760- APP_NAME, dbus.Dictionary({}, signature='ss'),
4761- reply_handler=reply_handler, error_handler=error_handler)
4762-
4763- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4764- in_signature='a{ss}',
4765- async_callbacks=("reply_handler", "error_handler"))
4766- def store_credentials(self, credentials,
4767- reply_handler=NO_OP, error_handler=NO_OP):
4768- """Store the token for Magicicada application."""
4769- self.ref_count += 1
4770- self.sso_proxy.store_credentials(
4771- APP_NAME, credentials,
4772- reply_handler=reply_handler, error_handler=error_handler)
4773-
4774- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4775- in_signature='a{ss}',
4776- async_callbacks=("reply_handler", "error_handler"))
4777- def register(self, args, reply_handler=NO_OP, error_handler=NO_OP):
4778- """Get credentials if found else prompt to register to Magicicada."""
4779- self.ref_count += 1
4780- params = dict(UI_PARAMS)
4781- params.update(args)
4782- self.sso_proxy.register(
4783- APP_NAME, params,
4784- reply_handler=reply_handler, error_handler=error_handler)
4785-
4786- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4787- in_signature='a{ss}',
4788- async_callbacks=("reply_handler", "error_handler"))
4789- def login(self, args, reply_handler=NO_OP, error_handler=NO_OP):
4790- """Get credentials if found else prompt to login to Magicicada."""
4791- self.ref_count += 1
4792- params = dict(UI_PARAMS)
4793- params.update(args)
4794- self.sso_proxy.login(
4795- APP_NAME, params,
4796- reply_handler=reply_handler, error_handler=error_handler)
4797-
4798- @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
4799- in_signature='a{ss}',
4800- async_callbacks=("reply_handler", "error_handler"))
4801- def login_email_password(self, args, reply_handler=NO_OP,
4802- error_handler=NO_OP):
4803- """Get credentials if found else prompt to login to Magicicada."""
4804- self.ref_count += 1
4805- params = dict(UI_PARAMS)
4806- params.update(args)
4807- self.sso_proxy.login_email_password(
4808- APP_NAME, params,
4809- reply_handler=reply_handler, error_handler=error_handler)
4810-
4811-
4812-def get_creds_proxy():
4813- """Get the CredentialsManagement proxy."""
4814- bus = dbus.SessionBus()
4815- try:
4816- obj = bus.get_object(DBUS_BUS_NAME,
4817- DBUS_CREDENTIALS_PATH,
4818- follow_name_owner_changes=True)
4819- proxy = dbus.Interface(obj, DBUS_CREDENTIALS_IFACE)
4820- except:
4821- logger.exception('get_creds_proxy:')
4822- raise
4823-
4824- return proxy
4825
4826=== removed file 'ubuntuone/platform/credentials/ipc_service.py'
4827--- ubuntuone/platform/credentials/ipc_service.py 2015-09-19 23:15:50 +0000
4828+++ ubuntuone/platform/credentials/ipc_service.py 1970-01-01 00:00:00 +0000
4829@@ -1,174 +0,0 @@
4830-# -*- coding: utf-8 -*-
4831-#
4832-# Copyright 2011-2012 Canonical Ltd.
4833-#
4834-# This program is free software: you can redistribute it and/or modify it
4835-# under the terms of the GNU General Public License version 3, as published
4836-# by the Free Software Foundation.
4837-#
4838-# This program is distributed in the hope that it will be useful, but
4839-# WITHOUT ANY WARRANTY; without even the implied warranties of
4840-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4841-# PURPOSE. See the GNU General Public License for more details.
4842-#
4843-# You should have received a copy of the GNU General Public License along
4844-# with this program. If not, see <http://www.gnu.org/licenses/>.
4845-#
4846-# In addition, as a special exception, the copyright holders give
4847-# permission to link the code of portions of this program with the
4848-# OpenSSL library under certain conditions as described in each
4849-# individual source file, and distribute linked combinations
4850-# including the two.
4851-# You must obey the GNU General Public License in all respects
4852-# for all of the code used other than OpenSSL. If you modify
4853-# file(s) with this exception, you may extend this exception to your
4854-# version of the file(s), but you are not obligated to do so. If you
4855-# do not wish to do so, delete this exception statement from your
4856-# version. If you delete this exception statement from all source
4857-# files in the program, then also delete it here.
4858-"""Magicicada credentials management IPC service."""
4859-
4860-
4861-from ubuntu_sso.main import get_sso_client
4862-from twisted.internet import defer
4863-
4864-from ubuntuone.platform.credentials import (
4865- APP_NAME,
4866- logger,
4867- NO_OP,
4868- UI_PARAMS,
4869-)
4870-
4871-
4872-class RemovableSignal(object):
4873- """A signal that can be removed."""
4874-
4875- def __init__(self, proxy, signal_name, callback):
4876- """Initialize this instance."""
4877- self.proxy = proxy
4878- self.signal_name = signal_name
4879- self.callback = callback
4880- setattr(self.proxy, signal_name, self)
4881-
4882- def __call__(self, *args, **kwargs):
4883- """Call this instance."""
4884- app_name = args[0] if len(args) > 0 else None
4885- logger.debug('Handling signal_name: %r, app_name: %r.',
4886- self.signal_name, app_name)
4887-
4888- if app_name != APP_NAME:
4889- # This fixed bug #818190: filter signals not related to APP_NAME
4890- logger.info('Received %r but app_name %r does not match %r, '
4891- 'exiting.', self.signal_name, app_name, APP_NAME)
4892- return
4893-
4894- if self.callback is not None:
4895- # drop the app name, callers do not care about it
4896- args = args[1:]
4897- logger.debug('Calling %r with %d args and %d kwargs.',
4898- self.callback, len(args), len(kwargs))
4899- return self.callback(*args, **kwargs)
4900-
4901- def remove(self):
4902- """Remove this signal."""
4903- if getattr(self.proxy, self.signal_name, False):
4904- setattr(self.proxy, self.signal_name, None)
4905-
4906-
4907-class CredentialsManagement(object):
4908- """Object that manages Magicicada credentials."""
4909-
4910- _SIGNAL_TO_CALLBACK_MAPPING = {
4911- 'AuthorizationDenied': 'on_authorization_denied_cb',
4912- 'CredentialsCleared': 'on_credentials_cleared_cb',
4913- 'CredentialsError': 'on_credentials_error_cb',
4914- 'CredentialsFound': 'on_credentials_found_cb',
4915- 'CredentialsNotFound': 'on_credentials_not_found_cb',
4916- 'CredentialsStored': 'on_credentials_stored_cb',
4917- }
4918-
4919- def __init__(self, proxy, *args, **kwargs):
4920- super(CredentialsManagement, self).__init__(*args, **kwargs)
4921- self.sso_proxy = proxy
4922-
4923- def connect_to_signal(self, signal_name, callback):
4924- """Register 'callback' to be called when 'signal_name' is emitted."""
4925- cb_name = self._SIGNAL_TO_CALLBACK_MAPPING[signal_name]
4926- match = RemovableSignal(self.sso_proxy, cb_name, callback)
4927- return match
4928-
4929- def find_credentials(self, reply_handler=NO_OP, error_handler=NO_OP):
4930- """Ask the Magicicada credentials."""
4931- d = self.sso_proxy.find_credentials(APP_NAME, {})
4932- d.addCallbacks(lambda _: reply_handler(), error_handler)
4933-
4934- def clear_credentials(self, reply_handler=NO_OP, error_handler=NO_OP):
4935- """Clear the Magicicada credentials."""
4936- d = self.sso_proxy.clear_credentials(APP_NAME, {})
4937- d.addCallbacks(lambda _: reply_handler(), error_handler)
4938-
4939- def store_credentials(self, credentials,
4940- reply_handler=NO_OP, error_handler=NO_OP):
4941- """Store the token for Magicicada application."""
4942- d = self.sso_proxy.store_credentials(APP_NAME, credentials)
4943- d.addCallbacks(lambda _: reply_handler(), error_handler)
4944-
4945- def register(self, args, reply_handler=NO_OP, error_handler=NO_OP):
4946- """Get credentials if found else prompt to register to Magicicada."""
4947- params = dict(UI_PARAMS)
4948- params.update(args)
4949- d = self.sso_proxy.register(APP_NAME, params)
4950- d.addCallbacks(lambda _: reply_handler(), error_handler)
4951-
4952- def login(self, args, reply_handler=NO_OP, error_handler=NO_OP):
4953- """Get credentials if found else prompt to login to Magicicada."""
4954- params = dict(UI_PARAMS)
4955- params.update(args)
4956- d = self.sso_proxy.login(APP_NAME, params)
4957- d.addCallbacks(lambda _: reply_handler(), error_handler)
4958-
4959- def login_email_password(self, args,
4960- reply_handler=NO_OP, error_handler=NO_OP):
4961- """Get credentials if found else login to Magicicada."""
4962- params = dict(UI_PARAMS)
4963- params.update(args)
4964- d = self.sso_proxy.login_email_password(APP_NAME, params)
4965- d.addCallbacks(lambda _: reply_handler(), error_handler)
4966-
4967- def register_to_credentials_stored(self, callback):
4968- """Register to the CredentialsStored dbus signal."""
4969- return RemovableSignal(self.sso_proxy, "on_credentials_stored_cb",
4970- callback)
4971-
4972- def register_to_credentials_cleared(self, callback):
4973- """Register to the CredentialsCleared dbus signal."""
4974- return RemovableSignal(self.sso_proxy, "on_credentials_cleared_cb",
4975- callback)
4976-
4977- def register_to_credentials_found(self, callback):
4978- """Register to the CredentialsFound dbus signal."""
4979- return RemovableSignal(self.sso_proxy, "on_credentials_found_cb",
4980- callback)
4981-
4982- def register_to_credentials_not_found(self, callback):
4983- """Register to the CredentialsFound dbus signal."""
4984- return RemovableSignal(self.sso_proxy, "on_credentials_not_found_cb",
4985- callback)
4986-
4987- def register_to_authorization_denied(self, callback):
4988- """Register to the AuthorizationDenied dbus signal."""
4989- return RemovableSignal(self.sso_proxy, "on_authorization_denied_cb",
4990- callback)
4991-
4992- def register_to_credentials_error(self, callback):
4993- """Register to the CredentialsError dbus signal."""
4994- return RemovableSignal(self.sso_proxy, "on_credentials_error_cb",
4995- callback)
4996-
4997-
4998-@defer.inlineCallbacks
4999-def get_creds_proxy():
5000- """Get the CredentialsManagement proxy."""
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: