Merge lp:~nataliabidart/ubuntuone-client/split-oauth into lp:ubuntuone-client
- split-oauth
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | 542 |
Merged at revision: | 543 |
Proposed branch: | lp:~nataliabidart/ubuntuone-client/split-oauth |
Merge into: | lp:ubuntuone-client |
Diff against target: |
2363 lines (+10/-2166) 22 files modified
Makefile.am (+0/-1) bin/ubuntuone-login (+0/-238) bin/ubuntuone-preferences (+4/-5) contrib/testing/testcase.py (+1/-1) data/Makefile.am (+2/-7) data/com.ubuntuone.Authentication.service.in (+0/-3) data/oauth_registration.d/ubuntuone (+0/-23) data/oauth_urls (+0/-5) tests/oauthdesktop/__init__.py (+0/-14) tests/oauthdesktop/test_auth.py (+0/-263) tests/oauthdesktop/test_config.py (+0/-90) tests/oauthdesktop/test_key_acls.py (+0/-181) tests/oauthdesktop/test_main.py (+0/-93) tests/test_login.py (+0/-187) tests/test_preferences.py (+2/-0) ubuntuone/oauthdesktop/__init__.py (+0/-16) ubuntuone/oauthdesktop/auth.py (+0/-480) ubuntuone/oauthdesktop/config.py (+0/-47) ubuntuone/oauthdesktop/key_acls.py (+0/-162) ubuntuone/oauthdesktop/logger.py (+0/-49) ubuntuone/oauthdesktop/main.py (+0/-296) ubuntuone/syncdaemon/dbus_interface.py (+1/-5) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-client/split-oauth |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
dobey (community) | Approve | ||
Rodrigo Moya (community) | Approve | ||
Ubuntu One hackers | Pending | ||
Review via email: mp+26946@code.launchpad.net |
Commit message
Split out the code for oauthdesktop.
Description of the change
Split out the code for oauthdesktop.
Until the package for ubuntu-sso-client is built, you'll need to manually add symlinks to run the tests as per:
nessita@
lrwxrwxrwx 1 nessita nessita 49 2010-06-09 15:54 ubuntu-login -> ../../.
nessita@
lrwxrwxrwx 1 nessita nessita 36 2010-06-09 15:54 ubuntu -> ../../ubuntu-
Rodrigo Moya (rodrigo-moya) wrote : | # |
Natalia Bidart (nataliabidart) wrote : | # |
> Let's first finish the renaming of the Python packages and DBus service in
> ubuntu-sso-client, and then let's change this branch to reflect that renaming,
> so that we don't need to do any further changes (at least renames) in
> u1-client
Renamed was finished.
dobey (dobey) wrote : | # |
The tests/test_login.py needs to be moved to the ubuntu-sso-client tree, rather than staying here. Ideally the oauthdesktop code should be fixed to not use twisted for the temporary local http server any more. And in fact, it won't need to have a local server after it is fixed to not use the browser for doing auth. Moving this test will also obviate the need for having the symlink to ubuntu-login.
We're also going to need to have the symlink creation either be automated (or unneccessary).
Given the hour though, we can discuss it more tomorrow.
Rodrigo Moya (rodrigo-moya) wrote : | # |
I think we can solve the test-login thing in another branch, so approve from me
dobey (dobey) wrote : | # |
Can we simplify ubuntu-sso-client to just provide the "ubuntusso" namespace, rather than the empty ubuntu namespace (which may present future conflicts) with the sso sub package?
Natalia Bidart (nataliabidart) wrote : | # |
> Can we simplify ubuntu-sso-client to just provide the "ubuntusso" namespace,
> rather than the empty ubuntu namespace (which may present future conflicts)
> with the sso sub package?
Name simplified to ubuntu_sso.
- 534. By Natalia Bidart
-
Merged trunk in.
- 535. By Natalia Bidart
-
Applying seconds rename for Ubuntu SSO.
- 536. By Natalia Bidart
-
Making tests no depend on sso-client tests.
- 537. By Natalia Bidart
-
Restoting FakeLogin and FakeProcessor.
- 538. By Natalia Bidart
-
Merged trunk in.
- 539. By Natalia Bidart
-
Skipping test_login.
- 540. By Natalia Bidart
-
Poiting to the righ binary.
- 541. By Natalia Bidart
-
Merged trunk in.
Natalia Bidart (nataliabidart) wrote : | # |
Package ubuntu-sso-client is being sponsored and soon to be on main. In the mean time, it can be downloaded from https:/
The split is ready to go into trunk.
dobey (dobey) wrote : | # |
Please just remove the test_login.py in this branch. I have a branch for ubuntu-sso-client nearly ready to propose, which moves it there.
- 542. By Natalia Bidart
-
Removing test_login which will be moved to ubuntu-sso-client project.
dobey (dobey) wrote : | # |
OK. Aside from potential smaller issues that might crop up, and some possible small changes we might need to make to use ubuntu-sso-client uninstalled, this seems ok to me now.
Preview Diff
1 | === modified file 'Makefile.am' |
2 | --- Makefile.am 2010-06-11 15:33:16 +0000 |
3 | +++ Makefile.am 2010-06-16 20:35:34 +0000 |
4 | @@ -25,7 +25,6 @@ |
5 | bin/u1sync |
6 | |
7 | libexec_SCRIPTS = \ |
8 | - bin/ubuntuone-login \ |
9 | bin/ubuntuone-syncdaemon |
10 | |
11 | manfilesdir = $(mandir)/man1 |
12 | |
13 | === removed file 'bin/ubuntuone-login' |
14 | --- bin/ubuntuone-login 2010-04-14 23:31:21 +0000 |
15 | +++ bin/ubuntuone-login 1970-01-01 00:00:00 +0000 |
16 | @@ -1,238 +0,0 @@ |
17 | -#!/usr/bin/python |
18 | - |
19 | -# ubuntuone-login - Client side log-in utility for Ubuntu One |
20 | -# |
21 | -# Author: Rodney Dawes <rodney.dawes@canonical.com> |
22 | -# |
23 | -# Copyright 2009 Canonical Ltd. |
24 | -# |
25 | -# This program is free software: you can redistribute it and/or modify it |
26 | -# under the terms of the GNU General Public License version 3, as published |
27 | -# by the Free Software Foundation. |
28 | -# |
29 | -# This program is distributed in the hope that it will be useful, but |
30 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
31 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
32 | -# PURPOSE. See the GNU General Public License for more details. |
33 | -# |
34 | -# You should have received a copy of the GNU General Public License along |
35 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
36 | - |
37 | -import pygtk |
38 | -pygtk.require('2.0') |
39 | -import gtk |
40 | -import pango |
41 | -import sys |
42 | -import gettext |
43 | -from ubuntuone import clientdefs |
44 | - |
45 | -import dbus.service |
46 | - |
47 | -from dbus.mainloop.glib import DBusGMainLoop |
48 | -from ubuntuone.oauthdesktop.main import Login |
49 | - |
50 | -from ubuntuone.oauthdesktop.logger import setupLogging |
51 | -logger = setupLogging("ubuntuone-login") |
52 | - |
53 | -DBusGMainLoop(set_as_default=True) |
54 | - |
55 | -_ = gettext.gettext |
56 | -ngettext = gettext.ngettext |
57 | - |
58 | -DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication" |
59 | - |
60 | -OAUTH_REALM = "https://ubuntuone.com" |
61 | -OAUTH_CONSUMER = "ubuntuone" |
62 | - |
63 | -NOTIFY_ICON_SIZE = 48 |
64 | - |
65 | -# Why thank you GTK+ for enforcing style-set and breaking API |
66 | -RCSTYLE = """ |
67 | -style 'dialogs' { |
68 | - GtkDialog::action-area-border = 12 |
69 | - GtkDialog::button-spacing = 6 |
70 | - GtkDialog::content-area-border = 0 |
71 | -} |
72 | -widget_class '*Dialog*' style 'dialogs' |
73 | -""" |
74 | - |
75 | - |
76 | -class LoginMain(object): |
77 | - """Main login manager process class.""" |
78 | - |
79 | - def __init__(self, *args, **kw): |
80 | - """Initializes the child threads and dbus monitor.""" |
81 | - gtk.rc_parse_string(RCSTYLE) |
82 | - |
83 | - logger.info(_("Starting Ubuntu One login manager version %s") % |
84 | - clientdefs.VERSION) |
85 | - |
86 | - self.__bus = dbus.SessionBus() |
87 | - |
88 | - def _connect_dbus_signals(self): |
89 | - """Set up some signal handlers for DBus signals.""" |
90 | - self.__bus.add_signal_receiver( |
91 | - handler_function=self.new_credentials, |
92 | - signal_name="NewCredentials", |
93 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
94 | - self.__bus.add_signal_receiver( |
95 | - handler_function=self.auth_denied, |
96 | - signal_name="AuthorizationDenied", |
97 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
98 | - self.__bus.add_signal_receiver( |
99 | - handler_function=self.got_oauth_error, |
100 | - signal_name="OAuthError", |
101 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
102 | - |
103 | - |
104 | - def new_credentials(self, realm=None, consumer_key=None, sender=None): |
105 | - """Signal callback for when we get new credentials.""" |
106 | - self.set_up_desktopcouch_pairing(consumer_key) |
107 | - |
108 | - def got_port(*args, **kwargs): |
109 | - # Discard the value. We don't really care about the port, here. |
110 | - pass |
111 | - |
112 | - def got_error(*args, **kwargs): |
113 | - logger.warn("On trying to start desktopcouch-service via DBus, " |
114 | - "we got an error. %s %s" % (args, kwargs,)) |
115 | - |
116 | - # We have auth, so start desktopcouch service by asking for its port. |
117 | - dc_serv_proxy = self.__bus.get_object("org.desktopcouch.CouchDB", "/") |
118 | - dc_serv_proxy.getPort(reply_handler=got_port, error_handler=got_error) |
119 | - |
120 | - def auth_denied(self): |
121 | - """Signal callback for when auth was denied by user.""" |
122 | - logger.info(_("Access to Ubuntu One was denied.")) |
123 | - |
124 | - from twisted.internet import reactor |
125 | - reactor.stop() |
126 | - |
127 | - def got_oauth_error(self, message=None): |
128 | - """Signal callback for when an OAuth error occured.""" |
129 | - def dialog_response(dialog, response): |
130 | - """Handle the dialog closing.""" |
131 | - dialog.destroy() |
132 | - |
133 | - if message: |
134 | - logger.error(message) |
135 | - dialog = gtk.Dialog(title=_("Ubuntu One: Error"), |
136 | - flags=gtk.DIALOG_NO_SEPARATOR, |
137 | - buttons=(gtk.STOCK_CLOSE, |
138 | - gtk.RESPONSE_CLOSE)) |
139 | - dialog.set_default_response(gtk.RESPONSE_CLOSE) |
140 | - dialog.set_icon_name("ubuntuone-client") |
141 | - |
142 | - area = dialog.get_content_area() |
143 | - |
144 | - hbox = gtk.HBox(spacing=12) |
145 | - hbox.set_border_width(12) |
146 | - area.pack_start(hbox) |
147 | - hbox.show() |
148 | - |
149 | - image = gtk.Image() |
150 | - image.set_from_icon_name("dialog-error", gtk.ICON_SIZE_DIALOG) |
151 | - image.set_alignment(0.5, 0.0) |
152 | - image.show() |
153 | - hbox.pack_start(image, False, False) |
154 | - |
155 | - vbox = gtk.VBox(spacing=12) |
156 | - vbox.show() |
157 | - hbox.pack_start(vbox) |
158 | - |
159 | - label = gtk.Label("<b>%s</b>" % _("Authorization Error")) |
160 | - label.set_use_markup(True) |
161 | - label.set_alignment(0.0, 0.5) |
162 | - label.show() |
163 | - vbox.pack_start(label, False, False) |
164 | - |
165 | - label = gtk.Label(message) |
166 | - label.set_line_wrap(True) |
167 | - label.set_max_width_chars(64) |
168 | - label.set_ellipsize(pango.ELLIPSIZE_MIDDLE) |
169 | - label.set_alignment(0.0, 0.0) |
170 | - label.show() |
171 | - vbox.pack_start(label, True, True) |
172 | - |
173 | - dialog.connect('close', dialog_response, gtk.RESPONSE_CLOSE) |
174 | - dialog.connect('response', dialog_response) |
175 | - |
176 | - dialog.show() |
177 | - else: |
178 | - logger.error(_("Got an OAuth error with no message.")) |
179 | - |
180 | - def set_up_desktopcouch_pairing(self, consumer_key): |
181 | - """Add a pairing record between desktopcouch and Ubuntu One""" |
182 | - try: |
183 | - from desktopcouch.pair.couchdb_pairing.couchdb_io import \ |
184 | - put_static_paired_service |
185 | - from desktopcouch.records.server import CouchDatabase |
186 | - except ImportError: |
187 | - # desktopcouch is not installed |
188 | - logger.debug(_("Not adding desktopcouch pairing since" |
189 | - " desktopcouch is not installed")) |
190 | - return |
191 | - # Check whether there is already a record of the Ubuntu One service |
192 | - db = CouchDatabase("management", create=True) |
193 | - if not db.view_exists("ubuntu_one_pair_record","ubuntu_one_pair_record"): |
194 | - map_js = """function(doc) { |
195 | - if (doc.service_name == "ubuntuone") { |
196 | - if (doc.application_annotations && |
197 | - doc.application_annotations["Ubuntu One"] && |
198 | - doc.application_annotations["Ubuntu One"]["private_application_annotations"] && |
199 | - doc.application_annotations["Ubuntu One"]["private_application_annotations"]["deleted"]) { |
200 | - emit(doc._id, 1); |
201 | - } else { |
202 | - emit(doc._id, 0) |
203 | - } |
204 | - } |
205 | - }""" |
206 | - db.add_view("ubuntu_one_pair_record", map_js, None, |
207 | - "ubuntu_one_pair_record") |
208 | - results = db.execute_view("ubuntu_one_pair_record", |
209 | - "ubuntu_one_pair_record") |
210 | - found = False |
211 | - # Results should contain either one row or no rows |
212 | - # If there is one row, its value will be 0, meaning that there is |
213 | - # already an Ubuntu One pairing record, or 1, meaning that there |
214 | - # was an Ubuntu One pairing record but it has since been unpaired |
215 | - # Only create a new record if there is not one already. Specifically, |
216 | - # do not add the record if there is a deleted one, as this means |
217 | - # that the user explicitly unpaired it! |
218 | - for row in results: |
219 | - found = True |
220 | - if row.value == 1: |
221 | - logger.debug(_("Not adding desktopcouch pairing since" |
222 | - " the user has explicitly unpaired with Ubuntu One")) |
223 | - else: |
224 | - logger.debug(_("Not adding desktopcouch pairing since" |
225 | - " we are already paired")) |
226 | - if not found: |
227 | - put_static_paired_service(None, "ubuntuone") |
228 | - logger.debug(_("Pairing desktopcouch with Ubuntu One")) |
229 | - |
230 | - def main(self): |
231 | - """Starts the gtk main loop.""" |
232 | - self._connect_dbus_signals() |
233 | - |
234 | - from twisted.internet import reactor |
235 | - reactor.run() |
236 | - |
237 | - |
238 | -if __name__ == "__main__": |
239 | - gettext.bindtextdomain(clientdefs.GETTEXT_PACKAGE, clientdefs.LOCALEDIR) |
240 | - gettext.textdomain(clientdefs.GETTEXT_PACKAGE) |
241 | - |
242 | - # Register DBus service for making sure we run only one instance |
243 | - bus = dbus.SessionBus() |
244 | - if bus.request_name(DBUS_IFACE_AUTH_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE) == dbus.bus.REQUEST_NAME_REPLY_EXISTS: |
245 | - print _("Ubuntu One login manager already running, quitting") |
246 | - sys.exit(0) |
247 | - |
248 | - from twisted.internet import gtk2reactor |
249 | - gtk2reactor.install() |
250 | - |
251 | - login = Login(dbus.service.BusName(DBUS_IFACE_AUTH_NAME, |
252 | - bus=dbus.SessionBus())) |
253 | - manager = LoginMain() |
254 | - manager.main() |
255 | |
256 | === modified file 'bin/ubuntuone-preferences' |
257 | --- bin/ubuntuone-preferences 2010-06-11 16:17:34 +0000 |
258 | +++ bin/ubuntuone-preferences 2010-06-16 20:35:34 +0000 |
259 | @@ -33,6 +33,8 @@ |
260 | import subprocess |
261 | from threading import Thread |
262 | from oauth import oauth |
263 | + |
264 | +from ubuntu_sso import DBUS_IFACE_AUTH_NAME, DBUS_PATH_AUTH |
265 | from ubuntuone import clientdefs |
266 | from ubuntuone.syncdaemon.tools import SyncDaemonTool |
267 | from ubuntuone.api.restclient import RestClient |
268 | @@ -68,9 +70,6 @@ |
269 | DBUS_IFACE_CONFIG_NAME = DBUS_IFACE_NAME + ".Config" |
270 | DBUS_IFACE_STATUS_NAME = DBUS_IFACE_NAME + ".Status" |
271 | |
272 | -DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication" |
273 | -DBUS_IFACE_AUTH_PATH = "/" |
274 | - |
275 | # Our own DBus interface |
276 | PREFS_BUS_NAME = "com.ubuntuone.Preferences" |
277 | |
278 | @@ -104,7 +103,7 @@ |
279 | """Make a login request to the login handling daemon.""" |
280 | try: |
281 | client = bus.get_object(DBUS_IFACE_AUTH_NAME, |
282 | - DBUS_IFACE_AUTH_PATH, |
283 | + DBUS_PATH_AUTH, |
284 | follow_name_owner_changes=True) |
285 | iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME) |
286 | iface.login('https://ubuntuone.com', 'ubuntuone', |
287 | @@ -474,7 +473,7 @@ |
288 | if token == local.key: |
289 | try: |
290 | client = self.bus.get_object(DBUS_IFACE_AUTH_NAME, |
291 | - DBUS_IFACE_AUTH_PATH, |
292 | + DBUS_PATH_AUTH, |
293 | follow_name_owner_changes=True) |
294 | iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME) |
295 | iface.clear_token('https://ubuntuone.com', 'ubuntuone', |
296 | |
297 | === modified file 'contrib/testing/testcase.py' |
298 | --- contrib/testing/testcase.py 2010-05-26 21:43:27 +0000 |
299 | +++ contrib/testing/testcase.py 2010-06-16 20:35:34 +0000 |
300 | @@ -25,7 +25,7 @@ |
301 | import shutil |
302 | import itertools |
303 | |
304 | -from ubuntuone.oauthdesktop.main import Login, LoginProcessor |
305 | +from ubuntu_sso.main import Login, LoginProcessor |
306 | from ubuntuone.syncdaemon import ( |
307 | config, |
308 | action_queue, |
309 | |
310 | === modified file 'data/Makefile.am' |
311 | --- data/Makefile.am 2010-06-11 15:33:16 +0000 |
312 | +++ data/Makefile.am 2010-06-16 20:35:34 +0000 |
313 | @@ -7,10 +7,7 @@ |
314 | |
315 | configdir = $(sysconfdir)/xdg/ubuntuone |
316 | config_in_files = logging.conf.in |
317 | -config_DATA = oauth_urls syncdaemon.conf $(config_in_files:.conf.in=.conf) |
318 | - |
319 | -oauthdir = $(configdir)/oauth_registration.d |
320 | -oauth_DATA = oauth_registration.d/ubuntuone |
321 | +config_DATA = syncdaemon.conf $(config_in_files:.conf.in=.conf) |
322 | |
323 | memenudir = $(datadir)/indicators/me |
324 | memenu_in_files = ubuntuone.menu.in |
325 | @@ -45,7 +42,6 @@ |
326 | |
327 | servicedir = $(DBUS_SERVICES_DIR) |
328 | service_in_files = \ |
329 | - com.ubuntuone.Authentication.service.in \ |
330 | com.ubuntuone.SyncDaemon.service.in |
331 | service_DATA = $(service_in_files:.service.in=.service) |
332 | |
333 | @@ -146,8 +142,7 @@ |
334 | stamp-render \ |
335 | $(config_DATA) \ |
336 | $(apport_DATA) \ |
337 | - $(crashdb_DATA) \ |
338 | - oauth_registration.d |
339 | + $(crashdb_DATA) |
340 | |
341 | CLEANFILES = \ |
342 | $(memenu_DATA) \ |
343 | |
344 | === removed file 'data/com.ubuntuone.Authentication.service.in' |
345 | --- data/com.ubuntuone.Authentication.service.in 2009-12-23 19:44:00 +0000 |
346 | +++ data/com.ubuntuone.Authentication.service.in 1970-01-01 00:00:00 +0000 |
347 | @@ -1,3 +0,0 @@ |
348 | -[D-BUS Service] |
349 | -Name=com.ubuntuone.Authentication |
350 | -Exec=@libexecdir@/ubuntuone-login |
351 | |
352 | === removed directory 'data/oauth_registration.d' |
353 | === removed file 'data/oauth_registration.d/ubuntuone' |
354 | --- data/oauth_registration.d/ubuntuone 2010-03-31 16:12:39 +0000 |
355 | +++ data/oauth_registration.d/ubuntuone 1970-01-01 00:00:00 +0000 |
356 | @@ -1,23 +0,0 @@ |
357 | -[storagefs-live] |
358 | -realm = https://ubuntuone.com |
359 | -consumer_key = ubuntuone |
360 | -exe_path = /usr/bin/python |
361 | -application_name = ubuntuone-syncdaemon |
362 | - |
363 | -[storagefs-local] |
364 | -realm = http://localhost |
365 | -consumer_key = ubuntuone |
366 | -exe_path = /usr/bin/python |
367 | -application_name = ubuntuone-syncdaemon |
368 | - |
369 | -[control-panel] |
370 | -realm = https://ubuntuone.com |
371 | -consumer_key = ubuntuone |
372 | -exe_path = /usr/bin/python |
373 | -application_name = ubuntuone-preferences |
374 | - |
375 | -[launcher] |
376 | -realm = https://ubuntuone.com |
377 | -consumer_key = ubuntuone |
378 | -exe_path = /usr/bin/python |
379 | -application_name = ubuntuone-launch |
380 | |
381 | === removed file 'data/oauth_urls' |
382 | --- data/oauth_urls 2009-05-12 13:36:05 +0000 |
383 | +++ data/oauth_urls 1970-01-01 00:00:00 +0000 |
384 | @@ -1,5 +0,0 @@ |
385 | -[default] |
386 | -request_token_url = /oauth/request/ |
387 | -user_authorisation_url = /oauth/authorize/ |
388 | -access_token_url = /oauth/access/ |
389 | -consumer_secret=hammertime |
390 | |
391 | === removed directory 'tests/oauthdesktop' |
392 | === removed file 'tests/oauthdesktop/__init__.py' |
393 | --- tests/oauthdesktop/__init__.py 2009-05-12 13:36:05 +0000 |
394 | +++ tests/oauthdesktop/__init__.py 1970-01-01 00:00:00 +0000 |
395 | @@ -1,14 +0,0 @@ |
396 | -# Copyright 2009 Canonical Ltd. |
397 | -# |
398 | -# This program is free software: you can redistribute it and/or modify it |
399 | -# under the terms of the GNU General Public License version 3, as published |
400 | -# by the Free Software Foundation. |
401 | -# |
402 | -# This program is distributed in the hope that it will be useful, but |
403 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
404 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
405 | -# PURPOSE. See the GNU General Public License for more details. |
406 | -# |
407 | -# You should have received a copy of the GNU General Public License along |
408 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
409 | -"""Tests for OAuth library""" |
410 | |
411 | === removed file 'tests/oauthdesktop/test_auth.py' |
412 | --- tests/oauthdesktop/test_auth.py 2010-01-11 17:46:00 +0000 |
413 | +++ tests/oauthdesktop/test_auth.py 1970-01-01 00:00:00 +0000 |
414 | @@ -1,263 +0,0 @@ |
415 | -# test_auth - Tests for ubuntuone.oauthdesktop.auth module |
416 | -# |
417 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
418 | -# |
419 | -# Copyright 2009 Canonical Ltd. |
420 | -# |
421 | -# This program is free software: you can redistribute it and/or modify it |
422 | -# under the terms of the GNU General Public License version 3, as published |
423 | -# by the Free Software Foundation. |
424 | -# |
425 | -# This program is distributed in the hope that it will be useful, but |
426 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
427 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
428 | -# PURPOSE. See the GNU General Public License for more details. |
429 | -# |
430 | -# You should have received a copy of the GNU General Public License along |
431 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
432 | -"""Tests for the OAuth client code for StorageFS.""" |
433 | - |
434 | -import gnomekeyring |
435 | -from contrib.mocker import ANY, IN, MockerTestCase |
436 | -import testresources |
437 | - |
438 | -from oauth import oauth |
439 | -from twisted.trial.unittest import TestCase as TwistedTestCase |
440 | -from ubuntuone.oauthdesktop.auth import (AuthorisationClient, |
441 | - NoAccessToken) |
442 | - |
443 | - |
444 | -# Adding TwistedTestCase as parent to be able to skip |
445 | -# test_ensure_access_token_no_token |
446 | -class AuthorisationClientTests(MockerTestCase, TwistedTestCase): |
447 | - """Test the GNOME keyring integration portions of the auth code.""" |
448 | - |
449 | - def setUp(self): |
450 | - """Sets up a mock keyring.""" |
451 | - MockerTestCase.setUp(self) |
452 | - self.keyring = self.mocker.mock() |
453 | - self.client = None |
454 | - self.item = self.mocker.mock(gnomekeyring.Found) |
455 | - |
456 | - self.item_id = 999 |
457 | - |
458 | - ex = self.expect(self.item.item_id) |
459 | - ex.result(self.item_id) |
460 | - ex.count(0, None) |
461 | - |
462 | - ex = self.expect(self.item.secret) |
463 | - ex.result('oauth_token=access_key&oauth_token_secret=access_secret') |
464 | - ex.count(0, None) |
465 | - |
466 | - def expect_token_query(self): |
467 | - """Expects the keyring to be queried for a token.""" |
468 | - return self.expect( |
469 | - self.keyring.find_items_sync( |
470 | - gnomekeyring.ITEM_GENERIC_SECRET, |
471 | - {'ubuntuone-realm': 'realm', |
472 | - 'oauth-consumer-key': 'consumer_key'}) |
473 | - ) |
474 | - |
475 | - def expect_token_store(self): |
476 | - """Expects the token to be stored in the keyring.""" |
477 | - return self.expect(self.keyring.item_create_sync( |
478 | - None, gnomekeyring.ITEM_GENERIC_SECRET, |
479 | - 'UbuntuOne token for realm', |
480 | - {'ubuntuone-realm': 'realm', |
481 | - 'oauth-consumer-key': 'consumer_key'}, |
482 | - # Either order for the token and secret is valid |
483 | - IN(['oauth_token=access_key&oauth_token_secret=access_secret', |
484 | - 'oauth_token_secret=access_secret&oauth_token=access_key']), |
485 | - True)) |
486 | - |
487 | - def expect_token_store_denied(self): |
488 | - """Expects the token to be denied for storing in keyring.""" |
489 | - self.expect_token_store().throw(gnomekeyring.DeniedError) |
490 | - |
491 | - def mock_has_token(self): |
492 | - """Mocks a cached token in the keyring.""" |
493 | - self.expect_token_query().result([self.item]) |
494 | - |
495 | - def mock_no_token(self, exception): |
496 | - """Mocks no token in the keyring.""" |
497 | - self.expect_token_query().throw(exception) |
498 | - |
499 | - def replay(self, callback_parent=None, callback_denied=None, |
500 | - do_login=True): |
501 | - """Starts the replay phase and sets up a client object to be tested, |
502 | - wired up to the mock keyring. |
503 | - |
504 | - """ |
505 | - self.mocker.replay() |
506 | - self.client = AuthorisationClient( |
507 | - 'realm', 'request_token_url', 'user_authorisation_url', |
508 | - 'access_token_url', 'consumer_key', 'consumer_secret', |
509 | - callback_parent, callback_denied, do_login, keyring=self.keyring) |
510 | - |
511 | - def test_get_access_token(self): |
512 | - """The get_access_token method returns the access token""" |
513 | - self.mock_has_token() |
514 | - self.replay() |
515 | - token = self.client.get_access_token() |
516 | - self.assertTrue(isinstance(token, oauth.OAuthToken)) |
517 | - self.assertEqual(token.key, 'access_key') |
518 | - self.assertEqual(token.secret, 'access_secret') |
519 | - |
520 | - def test_get_access_token_no_match(self): |
521 | - """The get_access_token method fails if there are no matching items""" |
522 | - self.mock_no_token(gnomekeyring.NoMatchError) |
523 | - self.replay() |
524 | - self.assertRaises(NoAccessToken, self.client.get_access_token) |
525 | - |
526 | - def test_get_access_token_denied(self): |
527 | - """The get_access_token method fails if access is denied""" |
528 | - self.mock_no_token(gnomekeyring.DeniedError) |
529 | - self.replay() |
530 | - self.assertRaises(NoAccessToken, self.client.get_access_token) |
531 | - |
532 | - def test_have_access_token(self): |
533 | - """The `have_access_token` method returns True if a the |
534 | - keyring contains a token.""" |
535 | - self.mock_has_token() |
536 | - self.replay() |
537 | - self.assertEqual(self.client.have_access_token(), True) |
538 | - |
539 | - def test_have_access_token_fail(self): |
540 | - """The `have_access_token` method returns False if the keyring |
541 | - does not contain a token.""" |
542 | - self.mock_no_token(gnomekeyring.NoMatchError) |
543 | - self.replay() |
544 | - self.assertEqual(self.client.have_access_token(), False) |
545 | - |
546 | - def test_store_token(self): |
547 | - """The store_token method correctly stores an item in the keyring, |
548 | - and correctly sets an ACL on it.""" |
549 | - self.expect_token_store().result(self.item_id) |
550 | - saka = self.mocker.replace( |
551 | - "ubuntuone.oauthdesktop.key_acls.set_all_key_acls") |
552 | - saka(item_id=self.item_id) |
553 | - self.mocker.result(None) |
554 | - |
555 | - sleep = self.mocker.replace("time.sleep") |
556 | - sleep(4) |
557 | - self.mocker.result(None) |
558 | - |
559 | - self.replay() |
560 | - self.client.store_token(oauth.OAuthToken('access_key', 'access_secret')) |
561 | - |
562 | - def test_store_token_denied(self): |
563 | - """The store_token method correctly stores an item in the keyring, |
564 | - and correctly sets an ACL on it.""" |
565 | - self.expect_token_store_denied() |
566 | - |
567 | - self.replay() |
568 | - self.client.store_token(oauth.OAuthToken('access_key', 'access_secret')) |
569 | - |
570 | - def test_clear_existing_token(self): |
571 | - """Makes sure that clear token clears an existing token.""" |
572 | - self.mock_has_token() |
573 | - self.expect(self.keyring.item_delete_sync(None, self.item_id)) |
574 | - self.replay() |
575 | - self.client.clear_token() |
576 | - |
577 | - def test_clear_no_existing_token(self): |
578 | - """Makes sure that clear with no existing token still works.""" |
579 | - self.mock_no_token(gnomekeyring.NoMatchError) |
580 | - self.replay() |
581 | - self.client.clear_token() |
582 | - |
583 | - def test_ensure_access_token(self): |
584 | - """If the user already has a token, no new token is requested.""" |
585 | - self.mock_has_token() |
586 | - callback_function = self.mocker.mock() |
587 | - callback_function(ANY) |
588 | - self.replay(callback_parent=callback_function) |
589 | - self.client.ensure_access_token() |
590 | - |
591 | - def test_ensure_access_token_no_token(self): |
592 | - """If the user has no token, a new one is requested and stored, via |
593 | - an OAuth callback to the internal webserver.""" |
594 | - |
595 | - self.mock_no_token(gnomekeyring.NoMatchError) |
596 | - |
597 | - request_token = self.mocker.mock() |
598 | - self.expect(request_token.key).result('access_key').count(0, None) |
599 | - ex = self.expect(request_token.secret) |
600 | - ex.result('access_secret').count(0, None) |
601 | - make_token_request = self.mocker.mock() |
602 | - self.expect(make_token_request(ANY)).result(request_token) |
603 | - |
604 | - open_in_browser = self.mocker.mock() |
605 | - open_in_browser(ANY) |
606 | - |
607 | - ri = self.mocker.replace("random.randint") |
608 | - ri(1000000, 10000000) |
609 | - self.mocker.result(12345678) |
610 | - get_temporary_httpd = self.mocker.mock() |
611 | - get_temporary_httpd(12345678, ANY, True) |
612 | - self.mocker.result("http://callbackurl") |
613 | - |
614 | - self.replay(callback_parent=lambda a: None) |
615 | - |
616 | - self.client.make_token_request = make_token_request |
617 | - self.client.open_in_browser = open_in_browser |
618 | - self.client.get_temporary_httpd = get_temporary_httpd |
619 | - # skip the "are we online, via networkmanager" bit |
620 | - self.client.acquire_access_token_if_online = \ |
621 | - self.client.acquire_access_token |
622 | - self.client.ensure_access_token() |
623 | - test_ensure_access_token_no_token.skip = \ |
624 | - "Fails with exceptions.AssertionError: [Mocker] Unmet expectations, "\ |
625 | - "see bug #488933" |
626 | - |
627 | - |
628 | -class AcquireAccessTokenTests(testresources.ResourcedTestCase): |
629 | - """OAuth token acquisition tests.""" |
630 | - |
631 | - def setUp(self): |
632 | - super(AcquireAccessTokenTests, self).setUp() |
633 | - |
634 | - def tearDown(self): |
635 | - super(AcquireAccessTokenTests, self).tearDown() |
636 | - |
637 | - def test_acquire_access_token(self): |
638 | - """Test that acquire_access_token() can acquire the access token.""" |
639 | - |
640 | - def make_token_request(oauth_request): |
641 | - """Make an OAuth token request via the test browser.""" |
642 | - return oauth.OAuthToken.from_string("oauth_token=access_token&" + |
643 | - "oauth_token_secret=" + |
644 | - "access_secret") |
645 | - |
646 | - def open_in_browser(url): |
647 | - """Just return as we aren't subscribed to the page.""" |
648 | - return |
649 | - |
650 | - def got_token(token): |
651 | - """Called with the token once auth is completed""" |
652 | - self.assertTrue(isinstance(token, oauth.OAuthToken)) |
653 | - |
654 | - def get_temporary_httpd(nonce, retrieve_function, store): |
655 | - """Mock the temporary httpd and return a callback URL""" |
656 | - # returns callback URL; this is an invalid URL, of course, |
657 | - # (port is too high) but we check later that the mechanize |
658 | - # browser tries to navigate there |
659 | - return "http://localhost:99999/?nonce=99999" |
660 | - |
661 | - def store_token(token): |
662 | - """Don't use the keyring; do nothing""" |
663 | - pass |
664 | - |
665 | - client = AuthorisationClient( |
666 | - 'http://ubuntuone/', |
667 | - 'http://ubuntuone/oauth/request/', |
668 | - 'http://ubuntuone/oauth/authorize/', |
669 | - 'http://ubuntuone/oauth/access/', |
670 | - 'consumer_key', 'consumer_secret', |
671 | - callback_parent=got_token) |
672 | - client.make_token_request = make_token_request |
673 | - client.open_in_browser = open_in_browser |
674 | - client.get_temporary_httpd = get_temporary_httpd |
675 | - client.store_token = store_token |
676 | - |
677 | - client.acquire_access_token('token description') |
678 | |
679 | === removed file 'tests/oauthdesktop/test_config.py' |
680 | --- tests/oauthdesktop/test_config.py 2009-07-02 20:22:51 +0000 |
681 | +++ tests/oauthdesktop/test_config.py 1970-01-01 00:00:00 +0000 |
682 | @@ -1,90 +0,0 @@ |
683 | -# test_config - tests for ubuntuone.oauthdesktop.config |
684 | -# |
685 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
686 | -# |
687 | -# Copyright 2009 Canonical Ltd. |
688 | -# |
689 | -# This program is free software: you can redistribute it and/or modify it |
690 | -# under the terms of the GNU General Public License version 3, as published |
691 | -# by the Free Software Foundation. |
692 | -# |
693 | -# This program is distributed in the hope that it will be useful, but |
694 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
695 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
696 | -# PURPOSE. See the GNU General Public License for more details. |
697 | -# |
698 | -# You should have received a copy of the GNU General Public License along |
699 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
700 | -"""Tests for the OAuth client code for StorageFS.""" |
701 | - |
702 | -import os, StringIO |
703 | -from contrib.mocker import MockerTestCase |
704 | -from ubuntuone.oauthdesktop import config |
705 | -from ubuntuone.oauthdesktop.config import get_config |
706 | - |
707 | -class ConfigFileTests(MockerTestCase): |
708 | - """Test the config file finder""" |
709 | - |
710 | - def test_get_file_from_tmp(self): |
711 | - """Does the tmp file get chosen?""" |
712 | - |
713 | - # mock that tmp config file exists |
714 | - tmp_config_file = os.path.realpath(os.path.join( |
715 | - os.path.split(config.__file__)[0], "../../data/oauth_urls" |
716 | - )) |
717 | - osp = self.mocker.replace("os.path") |
718 | - osp.isfile(tmp_config_file) |
719 | - self.mocker.result(True) |
720 | - self.mocker.replay() |
721 | - |
722 | - conf = get_config() |
723 | - self.assertEqual(conf.FILENAME, tmp_config_file) |
724 | - |
725 | - def test_get_file_from_home(self): |
726 | - """Does our home config file get chosen?""" |
727 | - |
728 | - # mock that home config file exists |
729 | - home_config_file = os.path.expanduser("~/.config/ubuntuone/oauth_urls") |
730 | - osp = self.mocker.replace("os.path") |
731 | - osp.exists(home_config_file) |
732 | - self.mocker.result(True) |
733 | - self.mocker.replay() |
734 | - |
735 | - conf = get_config(use_tmpconfig=False) # pretend not in source tree |
736 | - self.assertEqual(conf.FILENAME, home_config_file) |
737 | - |
738 | - def test_get_file_from_etc(self): |
739 | - """Does our /etc config file get chosen?""" |
740 | - |
741 | - # mock that etc config file exists |
742 | - etc_config_file = os.path.expanduser("/etc/xdg/ubuntuone/oauth_urls") |
743 | - osp = self.mocker.replace("os.path") |
744 | - osp.exists(etc_config_file) |
745 | - self.mocker.result(True) |
746 | - self.mocker.replay() |
747 | - |
748 | - conf = get_config(use_tmpconfig=False) # pretend not in source tree |
749 | - self.assertEqual(conf.FILENAME, etc_config_file) |
750 | - |
751 | - def test_tmp_file_parses(self): |
752 | - """Does the tmp file get chosen and parse correctly?""" |
753 | - |
754 | - # mock that tmp config file exists |
755 | - tmp_config_file = os.path.realpath(os.path.join( |
756 | - os.path.split(config.__file__)[0], "../../data/oauth_urls" |
757 | - )) |
758 | - osp = self.mocker.replace("os.path") |
759 | - osp.isfile(tmp_config_file) |
760 | - self.mocker.result(True) |
761 | - |
762 | - sio = StringIO.StringIO("[default]\n") |
763 | - mock_open = self.mocker.replace(open) |
764 | - mock_open(tmp_config_file) |
765 | - self.mocker.result(sio) |
766 | - |
767 | - self.mocker.replay() |
768 | - |
769 | - conf = get_config() |
770 | - self.assertTrue(conf.has_section("default")) |
771 | - |
772 | - |
773 | |
774 | === removed file 'tests/oauthdesktop/test_key_acls.py' |
775 | --- tests/oauthdesktop/test_key_acls.py 2009-06-26 17:01:42 +0000 |
776 | +++ tests/oauthdesktop/test_key_acls.py 1970-01-01 00:00:00 +0000 |
777 | @@ -1,181 +0,0 @@ |
778 | -# test_key_acls - tests for ubuntuone.oauthdesktop.key_acls |
779 | -# |
780 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
781 | -# |
782 | -# Copyright 2009 Canonical Ltd. |
783 | -# |
784 | -# This program is free software: you can redistribute it and/or modify it |
785 | -# under the terms of the GNU General Public License version 3, as published |
786 | -# by the Free Software Foundation. |
787 | -# |
788 | -# This program is distributed in the hope that it will be useful, but |
789 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
790 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
791 | -# PURPOSE. See the GNU General Public License for more details. |
792 | -# |
793 | -# You should have received a copy of the GNU General Public License along |
794 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
795 | -"""Tests for the ACL setting code for ubuntuone-oauth-login.""" |
796 | - |
797 | -import os, StringIO |
798 | -import xdg.BaseDirectory |
799 | -from contrib.mocker import MockerTestCase, IN, ANY |
800 | -from ubuntuone.oauthdesktop.key_acls import ( |
801 | - get_privileged_config_folder, get_acl_preregistrations, set_all_key_acls, |
802 | - set_single_acl) |
803 | - |
804 | -class Loader(MockerTestCase): |
805 | - """Confirm only the /etc/xdg config files are loaded""" |
806 | - |
807 | - def test_etc_found(self): |
808 | - """Is the /etc/xdg folder found if present?""" |
809 | - |
810 | - ETC_FOLDER = "/etc/xdg/ubuntuone" |
811 | - osp = self.mocker.replace("os.path") |
812 | - osp.exists(ETC_FOLDER) |
813 | - self.mocker.result(True) |
814 | - self.mocker.replay() |
815 | - |
816 | - self.assertEqual(ETC_FOLDER, |
817 | - get_privileged_config_folder(use_source_tree_folder=False)) |
818 | - |
819 | - def test_etc_found_despite_home(self): |
820 | - """Is the /etc/xdg folder found even if the $HOME folder is present?""" |
821 | - |
822 | - ETC_FOLDER = "/etc/xdg/ubuntuone" |
823 | - HOME_FOLDER = os.path.join(xdg.BaseDirectory.xdg_config_home, |
824 | - "ubuntuone") |
825 | - osp = self.mocker.replace("os.path") |
826 | - osp.exists(ETC_FOLDER) |
827 | - self.mocker.result(True) |
828 | - osp.exists(HOME_FOLDER) |
829 | - self.mocker.result(True) |
830 | - self.mocker.replay() |
831 | - |
832 | - self.assertEqual(ETC_FOLDER, |
833 | - get_privileged_config_folder(use_source_tree_folder=False)) |
834 | - |
835 | - def test_etc_files_found(self): |
836 | - """Are files in /etc found?""" |
837 | - ETC_FOLDER = "/etc/xdg/ubuntuone" |
838 | - ETC_FILES_FOLDER = "/etc/xdg/ubuntuone/oauth_registration.d" |
839 | - FILE_LIST = ["a", "b"] |
840 | - osp = self.mocker.replace("os.path") |
841 | - osp.exists(ETC_FOLDER) |
842 | - self.mocker.result(True) |
843 | - osp.isdir(ETC_FILES_FOLDER) |
844 | - self.mocker.result(True) |
845 | - listdir = self.mocker.replace("os.listdir") |
846 | - listdir(ETC_FILES_FOLDER) |
847 | - self.mocker.result(FILE_LIST) |
848 | - self.mocker.replay() |
849 | - |
850 | - our_list = sorted( |
851 | - [os.path.join(ETC_FILES_FOLDER, x) for x in FILE_LIST]) |
852 | - their_list = sorted( |
853 | - get_acl_preregistrations(use_source_tree_folder=False)) |
854 | - self.assertEqual(our_list, their_list) |
855 | - |
856 | - def test_set_single_acl(self): |
857 | - """Does set_single_acl work?""" |
858 | - |
859 | - giifr = self.mocker.replace( |
860 | - "ubuntuone.oauthdesktop.key_acls.get_item_ids_for_realm") |
861 | - igas = self.mocker.replace("gnomekeyring.item_get_acl_sync") |
862 | - isas = self.mocker.replace("gnomekeyring.item_set_acl_sync") |
863 | - acon = self.mocker.replace("gnomekeyring.AccessControl") |
864 | - self.expect(giifr("realm", "consumer_key")).result([999]) |
865 | - self.expect(igas(None, 999)).result([]) |
866 | - ac1 = self.mocker.mock() |
867 | - self.expect(acon(ANY, ANY)).result(ac1) |
868 | - ac1.set_display_name("application_name") |
869 | - ac1.set_path_name("/tmp/exe_path") |
870 | - self.expect(isas(None, 999, [ac1])).result(None) |
871 | - self.expect(giifr("realm2", "consumer_key2")).result([9999]) |
872 | - self.expect(igas(None, 9999)).result([]) |
873 | - ac2 = self.mocker.mock() |
874 | - self.expect(acon(ANY, ANY)).result(ac2) |
875 | - ac2.set_display_name("application_name2") |
876 | - ac2.set_path_name("/tmp/exe_path2") |
877 | - self.expect(isas(None, 9999, [ac2])).result(None) |
878 | - self.mocker.replay() |
879 | - |
880 | - set_single_acl([ |
881 | - ("realm", "consumer_key", "/tmp/exe_path", "application_name"), |
882 | - ("realm2", "consumer_key2", "/tmp/exe_path2", "application_name2"), |
883 | - ]) |
884 | - |
885 | - |
886 | - def test_etc_files_parsed(self): |
887 | - """Are files in /etc parsed correctly?""" |
888 | - |
889 | - ETC_FOLDER = "/etc/xdg/ubuntuone" |
890 | - ETC_FILES_FOLDER = "/etc/xdg/ubuntuone/oauth_registration.d" |
891 | - FILE_LIST = ["a", "b"] |
892 | - osp = self.mocker.replace("os.path") |
893 | - osp.exists(ETC_FOLDER) |
894 | - self.mocker.result(True) |
895 | - osp.isdir(ETC_FILES_FOLDER) |
896 | - self.mocker.result(True) |
897 | - listdir = self.mocker.replace("os.listdir") |
898 | - listdir(ETC_FILES_FOLDER) |
899 | - self.mocker.result(FILE_LIST) |
900 | - |
901 | - sio1 = StringIO.StringIO("""[app1] |
902 | -realm = https://realm.example.com/ |
903 | -consumer_key = example_key |
904 | -exe_path = /nowhere/executable/path |
905 | -application_name = example_app_name |
906 | - |
907 | -[app2] |
908 | -realm = https://other.example.com/ |
909 | -consumer_key = example_key |
910 | -exe_path = /nowhere/executable/path |
911 | -application_name = example_app_name |
912 | - |
913 | -""") |
914 | - sio2 = StringIO.StringIO("""[app3] |
915 | -exe_path = /nowhere/path/2 |
916 | -application_name = example_app_name2 |
917 | -consumer_key = example_key2 |
918 | -realm = https://realm2.example.com/ |
919 | -""") |
920 | - mock_open = self.mocker.replace(open) |
921 | - mock_open(os.path.join(ETC_FILES_FOLDER, "a")) |
922 | - self.mocker.result(sio1) |
923 | - mock_open(os.path.join(ETC_FILES_FOLDER, "b")) |
924 | - self.mocker.result(sio2) |
925 | - |
926 | - ssa = self.mocker.replace( |
927 | - "ubuntuone.oauthdesktop.key_acls.set_single_acl") |
928 | - # list may come up in any order |
929 | - ssa(IN([ |
930 | - [ |
931 | - ("https://realm.example.com/", "example_key", |
932 | - "/nowhere/executable/path", "example_app_name"), |
933 | - ("https://other.example.com/", "example_key", |
934 | - "/nowhere/executable/path", "example_app_name"), |
935 | - ], |
936 | - [ |
937 | - ("https://other.example.com/", "example_key", |
938 | - "/nowhere/executable/path", "example_app_name"), |
939 | - ("https://realm.example.com/", "example_key", |
940 | - "/nowhere/executable/path", "example_app_name"), |
941 | - ], |
942 | - ]), specific_item_id=None |
943 | - ) |
944 | - self.mocker.result(None) |
945 | - ssa([ |
946 | - ("https://realm2.example.com/", "example_key2", |
947 | - "/nowhere/path/2", "example_app_name2") |
948 | - ], specific_item_id=None) |
949 | - self.mocker.result(None) |
950 | - self.mocker.replay() |
951 | - |
952 | - set_all_key_acls(use_source_tree_folder=False) |
953 | - |
954 | - |
955 | - |
956 | - |
957 | - |
958 | - |
959 | |
960 | === removed file 'tests/oauthdesktop/test_main.py' |
961 | --- tests/oauthdesktop/test_main.py 2009-06-26 17:01:42 +0000 |
962 | +++ tests/oauthdesktop/test_main.py 1970-01-01 00:00:00 +0000 |
963 | @@ -1,93 +0,0 @@ |
964 | -# test_main - tests for ubuntuone.oauthdesktop.main |
965 | -# |
966 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
967 | -# |
968 | -# Copyright 2009 Canonical Ltd. |
969 | -# |
970 | -# This program is free software: you can redistribute it and/or modify it |
971 | -# under the terms of the GNU General Public License version 3, as published |
972 | -# by the Free Software Foundation. |
973 | -# |
974 | -# This program is distributed in the hope that it will be useful, but |
975 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
976 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
977 | -# PURPOSE. See the GNU General Public License for more details. |
978 | -# |
979 | -# You should have received a copy of the GNU General Public License along |
980 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
981 | -"""Tests for the OAuth client code for StorageFS.""" |
982 | - |
983 | -import os, StringIO |
984 | -from contrib.mocker import MockerTestCase |
985 | -from ubuntuone.oauthdesktop import config |
986 | -from ubuntuone.oauthdesktop.main import LoginProcessor, BadRealmError |
987 | - |
988 | -class Realm(MockerTestCase): |
989 | - """Test the realm handling finder""" |
990 | - |
991 | - def test_invalid_realm(self): |
992 | - """Are invalid realms rejected?""" |
993 | - |
994 | - login = LoginProcessor(None, use_libnotify=False) |
995 | - self.assertRaises(BadRealmError, login.login, "bad realm", "key") |
996 | - |
997 | - def test_realms(self): |
998 | - """Are realm URLs correctly retrieved from the config?""" |
999 | - |
1000 | - # mock that tmp config file exists |
1001 | - tmp_config_file = os.path.realpath(os.path.join( |
1002 | - os.path.split(config.__file__)[0], "../../data/oauth_urls" |
1003 | - )) |
1004 | - osp = self.mocker.replace("os.path") |
1005 | - osp.isfile(tmp_config_file) |
1006 | - self.mocker.result(True) |
1007 | - |
1008 | - sio = StringIO.StringIO("""[default] |
1009 | -request_token_url = /rtu-default |
1010 | -user_authorisation_url = /uau-default |
1011 | -access_token_url = /atu-default |
1012 | -consumer_secret = foo-default |
1013 | - |
1014 | -[http://localhost] |
1015 | -request_token_url = /rtu-localhost |
1016 | -user_authorisation_url = /uau-localhost |
1017 | -access_token_url = /atu-localhost |
1018 | -consumer_secret = foo-localhost |
1019 | - |
1020 | -[http://ubuntuone.com] |
1021 | -request_token_url = /rtu-ubuntuone |
1022 | -user_authorisation_url = /uau-ubuntuone |
1023 | -access_token_url = /atu-ubuntuone |
1024 | -consumer_secret = foo-ubuntuone |
1025 | - |
1026 | -""") |
1027 | - mock_open = self.mocker.replace(open) |
1028 | - mock_open(tmp_config_file) |
1029 | - self.mocker.result(sio) |
1030 | - |
1031 | - self.mocker.replay() |
1032 | - |
1033 | - login = LoginProcessor(None, use_libnotify=False) |
1034 | - |
1035 | - # are localhost:XXXX URLs correctly fetched? |
1036 | - (rtu, uau, atu, cs) = login.get_config_urls("http://localhost:9876") |
1037 | - self.assertEqual(rtu, "http://localhost:9876/rtu-localhost") |
1038 | - self.assertEqual(uau, "http://localhost:9876/uau-localhost") |
1039 | - self.assertEqual(atu, "http://localhost:9876/atu-localhost") |
1040 | - self.assertEqual(cs, "foo-localhost") |
1041 | - |
1042 | - # is a URL specifically in the config correctly fetched? |
1043 | - (rtu, uau, atu, cs) = login.get_config_urls("http://ubuntuone.com") |
1044 | - self.assertEqual(rtu, "http://ubuntuone.com/rtu-ubuntuone") |
1045 | - self.assertEqual(uau, "http://ubuntuone.com/uau-ubuntuone") |
1046 | - self.assertEqual(atu, "http://ubuntuone.com/atu-ubuntuone") |
1047 | - self.assertEqual(cs, "foo-ubuntuone") |
1048 | - |
1049 | - # is a URL not in the config correctly fetched as default? |
1050 | - (rtu, uau, atu, cs) = login.get_config_urls("http://other.example.net") |
1051 | - self.assertEqual(rtu, "http://other.example.net/rtu-default") |
1052 | - self.assertEqual(uau, "http://other.example.net/uau-default") |
1053 | - self.assertEqual(atu, "http://other.example.net/atu-default") |
1054 | - self.assertEqual(cs, "foo-default") |
1055 | - |
1056 | - |
1057 | |
1058 | === removed file 'tests/test_login.py' |
1059 | --- tests/test_login.py 2010-01-15 16:32:12 +0000 |
1060 | +++ tests/test_login.py 1970-01-01 00:00:00 +0000 |
1061 | @@ -1,187 +0,0 @@ |
1062 | -# -*- coding: utf-8 -*- |
1063 | -# |
1064 | -# Author: Rodney Dawes <rodney.dawes@canonical.com> |
1065 | -# |
1066 | -# Copyright 2010 Canonical Ltd. |
1067 | -# |
1068 | -# This program is free software: you can redistribute it and/or modify it |
1069 | -# under the terms of the GNU General Public License version 3, as published |
1070 | -# by the Free Software Foundation. |
1071 | -# |
1072 | -# This program is distributed in the hope that it will be useful, but |
1073 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1074 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1075 | -# PURPOSE. See the GNU General Public License for more details. |
1076 | -# |
1077 | -# You should have received a copy of the GNU General Public License along |
1078 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1079 | -""" Tests for the ubuntuone-login script """ |
1080 | - |
1081 | -import dbus.service |
1082 | -import new |
1083 | -import os |
1084 | - |
1085 | -from contrib.testing.testcase import DBusTwistedTestCase, FakeLogin |
1086 | -from twisted.internet import defer |
1087 | -from twisted.python.failure import Failure |
1088 | -from ubuntuone.syncdaemon import dbus_interface |
1089 | - |
1090 | -class InvalidSignalError(Exception): |
1091 | - """Exception for when we get the wrong signal called.""" |
1092 | - pass |
1093 | - |
1094 | -class LoginTests(DBusTwistedTestCase): |
1095 | - """Basic tests for the ubuntuone-login script """ |
1096 | - |
1097 | - _path = os.path.join(os.getcwd(), "bin", "ubuntuone-login") |
1098 | - u1login = new.module('u1login') |
1099 | - execfile(_path, u1login.__dict__) |
1100 | - |
1101 | - def setUp(self): |
1102 | - DBusTwistedTestCase.setUp(self) |
1103 | - self.oauth = FakeLogin(self.bus) |
1104 | - self._old_path = dbus_interface.DBUS_PATH_AUTH |
1105 | - dbus_interface.DBUS_PATH_AUTH = '/oauthdesktop' |
1106 | - |
1107 | - def tearDown(self): |
1108 | - # collect all signal receivers registered during the test |
1109 | - signal_receivers = set() |
1110 | - with self.bus._signals_lock: |
1111 | - for group in self.bus._signal_recipients_by_object_path.values(): |
1112 | - for matches in group.values(): |
1113 | - for match in matches.values(): |
1114 | - signal_receivers.update(match) |
1115 | - d = self.cleanup_signal_receivers(signal_receivers) |
1116 | - def shutdown(r): |
1117 | - self.oauth.shutdown() |
1118 | - dbus_interface.DBUS_PATH_AUTH = self._old_path |
1119 | - d.addBoth(shutdown) |
1120 | - d.addBoth(lambda _: DBusTwistedTestCase.tearDown(self)) |
1121 | - return d |
1122 | - |
1123 | - def test_new_credentials(self): |
1124 | - """ Test logging in """ |
1125 | - def new_creds(realm=None, consumer_key=None, sender=None): |
1126 | - """ Override the callback """ |
1127 | - d.callback(True) |
1128 | - |
1129 | - def auth_denied(): |
1130 | - """ Override the callback """ |
1131 | - d.errback(Failure(InvalidSignalError())) |
1132 | - |
1133 | - def got_oauth_error(message=None): |
1134 | - """ Override the callback """ |
1135 | - d.errback(Failure(InvalidSignalError())) |
1136 | - |
1137 | - def set_up_desktopcouch_pairing(consumer_key): |
1138 | - """ Override the method """ |
1139 | - return |
1140 | - |
1141 | - def main(): |
1142 | - """ Override LoginMain.main """ |
1143 | - return |
1144 | - |
1145 | - login = self.u1login.LoginMain() |
1146 | - login.main = main |
1147 | - login.new_credentials = new_creds |
1148 | - login.auth_denied = auth_denied |
1149 | - login.got_oauth_error = got_oauth_error |
1150 | - login.set_up_desktopcouch_pairing = set_up_desktopcouch_pairing |
1151 | - |
1152 | - login._connect_dbus_signals() |
1153 | - |
1154 | - client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME, |
1155 | - '/oauthdesktop', |
1156 | - follow_name_owner_changes=True) |
1157 | - d = defer.Deferred() |
1158 | - |
1159 | - def login_handler(): |
1160 | - """ login handler """ |
1161 | - return |
1162 | - |
1163 | - iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME) |
1164 | - iface.login('http://localhost', self.u1login.OAUTH_CONSUMER, |
1165 | - reply_handler=login_handler, |
1166 | - error_handler=self.error_handler) |
1167 | - return d |
1168 | - |
1169 | - def test_auth_denied(self): |
1170 | - """ Test that denying authorization works correctly. """ |
1171 | - |
1172 | - def new_creds(realm=None, consumer_key=None, sender=None): |
1173 | - """ Override the callback """ |
1174 | - d.errback(Failure(InvalidSignalError())) |
1175 | - |
1176 | - def auth_denied(): |
1177 | - """ Override the callback """ |
1178 | - d.callback(True) |
1179 | - |
1180 | - def got_oauth_error(message=None): |
1181 | - """ override the callback """ |
1182 | - d.errback(Failure(InvalidSignalError())) |
1183 | - |
1184 | - login = self.u1login.LoginMain() |
1185 | - login.main = self.main |
1186 | - login.new_credentials = new_creds |
1187 | - login.auth_denied = auth_denied |
1188 | - login.got_oauth_error = got_oauth_error |
1189 | - |
1190 | - login._connect_dbus_signals() |
1191 | - |
1192 | - self.oauth.processor.next_login_with(self.oauth.processor.got_denial) |
1193 | - |
1194 | - client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME, |
1195 | - '/oauthdesktop', |
1196 | - follow_name_owner_changes=True) |
1197 | - d = defer.Deferred() |
1198 | - |
1199 | - def login_handler(): |
1200 | - """ login handler """ |
1201 | - return |
1202 | - |
1203 | - iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME) |
1204 | - iface.login('http://localhost', self.u1login.OAUTH_CONSUMER, |
1205 | - reply_handler=login_handler, |
1206 | - error_handler=self.error_handler) |
1207 | - return d |
1208 | - |
1209 | - def test_oauth_error(self): |
1210 | - """ Test that getting an error works correctly. """ |
1211 | - |
1212 | - def new_creds(realm=None, consumer_key=None, sender=None): |
1213 | - """ Override the callback """ |
1214 | - d.errback(Failure(InvalidSignalError())) |
1215 | - |
1216 | - def auth_denied(): |
1217 | - """ Override the callback """ |
1218 | - d.errback(Failure(InvalidSignalError())) |
1219 | - |
1220 | - def got_oauth_error(message=None): |
1221 | - """ override the callback """ |
1222 | - d.callback(True) |
1223 | - |
1224 | - login = self.u1login.LoginMain() |
1225 | - login.main = self.main |
1226 | - login.new_credentials = new_creds |
1227 | - login.auth_denied = auth_denied |
1228 | - login.got_oauth_error = got_oauth_error |
1229 | - |
1230 | - login._connect_dbus_signals() |
1231 | - |
1232 | - self.oauth.processor.next_login_with(self.oauth.processor.got_error, |
1233 | - ('error!',)) |
1234 | - |
1235 | - client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME, |
1236 | - '/oauthdesktop', |
1237 | - follow_name_owner_changes=True) |
1238 | - d = defer.Deferred() |
1239 | - |
1240 | - def login_handler(): |
1241 | - """ login handler """ |
1242 | - return |
1243 | - |
1244 | - iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME) |
1245 | - iface.login('http://localhost', self.u1login.OAUTH_CONSUMER, |
1246 | - reply_handler=login_handler, |
1247 | - error_handler=self.error_handler) |
1248 | - return d |
1249 | |
1250 | === modified file 'tests/test_preferences.py' |
1251 | --- tests/test_preferences.py 2010-06-15 09:16:48 +0000 |
1252 | +++ tests/test_preferences.py 2010-06-16 20:35:34 +0000 |
1253 | @@ -498,3 +498,5 @@ |
1254 | error_handler=got_dbus_error) |
1255 | |
1256 | return d |
1257 | + test_login_check.skip = \ |
1258 | + "This shouldn't depend on the real DBus service (see bug #591340)." |
1259 | |
1260 | === removed directory 'ubuntuone/oauthdesktop' |
1261 | === removed file 'ubuntuone/oauthdesktop/__init__.py' |
1262 | --- ubuntuone/oauthdesktop/__init__.py 2009-06-26 17:01:42 +0000 |
1263 | +++ ubuntuone/oauthdesktop/__init__.py 1970-01-01 00:00:00 +0000 |
1264 | @@ -1,16 +0,0 @@ |
1265 | -# ubuntuone.oauthdesktop - OAuth client support for desktop apps |
1266 | -# |
1267 | -# Copyright 2009 Canonical Ltd. |
1268 | -# |
1269 | -# This program is free software: you can redistribute it and/or modify it |
1270 | -# under the terms of the GNU General Public License version 3, as published |
1271 | -# by the Free Software Foundation. |
1272 | -# |
1273 | -# This program is distributed in the hope that it will be useful, but |
1274 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1275 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1276 | -# PURPOSE. See the GNU General Public License for more details. |
1277 | -# |
1278 | -# You should have received a copy of the GNU General Public License along |
1279 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1280 | -"""OAuth client authorisation code.""" |
1281 | |
1282 | === removed file 'ubuntuone/oauthdesktop/auth.py' |
1283 | --- ubuntuone/oauthdesktop/auth.py 2010-02-10 17:35:26 +0000 |
1284 | +++ ubuntuone/oauthdesktop/auth.py 1970-01-01 00:00:00 +0000 |
1285 | @@ -1,480 +0,0 @@ |
1286 | -# ubuntuone.oauthdesktop.auth - Client authorization module |
1287 | -# |
1288 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
1289 | -# |
1290 | -# Copyright 2009 Canonical Ltd. |
1291 | -# |
1292 | -# This program is free software: you can redistribute it and/or modify it |
1293 | -# under the terms of the GNU General Public License version 3, as published |
1294 | -# by the Free Software Foundation. |
1295 | -# |
1296 | -# This program is distributed in the hope that it will be useful, but |
1297 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1298 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1299 | -# PURPOSE. See the GNU General Public License for more details. |
1300 | -# |
1301 | -# You should have received a copy of the GNU General Public License along |
1302 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1303 | -"""OAuth client authorisation code. |
1304 | - |
1305 | -This code handles acquisition of an OAuth access token for a service, |
1306 | -managed through the GNOME keyring for future use, and asynchronously. |
1307 | -""" |
1308 | - |
1309 | -__metaclass__ = type |
1310 | - |
1311 | -import subprocess |
1312 | -import random |
1313 | -import dbus |
1314 | -import os |
1315 | -import socket, httplib, urllib |
1316 | - |
1317 | -import gnomekeyring |
1318 | -from oauth import oauth |
1319 | -try: |
1320 | - from ubuntuone.clientdefs import VERSION |
1321 | - ubuntuone_client_version = VERSION |
1322 | -except ImportError: |
1323 | - ubuntuone_client_version = "Unknown" |
1324 | -from ubuntuone.oauthdesktop.key_acls import set_all_key_acls |
1325 | - |
1326 | -from threading import Thread |
1327 | -from twisted.internet import reactor |
1328 | -from twisted.web import server, resource |
1329 | - |
1330 | -from ubuntuone.oauthdesktop.logger import setupLogging |
1331 | -logger = setupLogging("UbuntuOne.OAuthDesktop.auth") |
1332 | - |
1333 | - |
1334 | -class NoAccessToken(Exception): |
1335 | - """No access token available.""" |
1336 | - |
1337 | -# NetworkManager State constants |
1338 | -NM_STATE_UNKNOWN = 0 |
1339 | -NM_STATE_ASLEEP = 1 |
1340 | -NM_STATE_CONNECTING = 2 |
1341 | -NM_STATE_CONNECTED = 3 |
1342 | -NM_STATE_DISCONNECTED = 4 |
1343 | - |
1344 | -# Monkeypatch httplib so that urllib will fail on invalid certificate |
1345 | -# Only patch if we can import ssl to work around fail in 2.5 |
1346 | -try: |
1347 | - import ssl |
1348 | -except ImportError: |
1349 | - pass |
1350 | -else: |
1351 | - def _connect_wrapper(self): |
1352 | - """Override HTTPSConnection.connect to require certificate checks""" |
1353 | - sock = socket.create_connection((self.host, self.port), self.timeout) |
1354 | - try: |
1355 | - if self._tunnel_host: |
1356 | - self.sock = sock |
1357 | - self._tunnel() |
1358 | - except AttributeError: |
1359 | - pass |
1360 | - self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, |
1361 | - cert_reqs=ssl.CERT_REQUIRED, |
1362 | - ca_certs="/etc/ssl/certs/ca-certificates.crt") |
1363 | - httplib.HTTPSConnection.connect = _connect_wrapper |
1364 | - |
1365 | - |
1366 | -class FancyURLOpenerWithRedirectedPOST(urllib.FancyURLopener): |
1367 | - """FancyURLopener does not redirect postdata when redirecting POSTs""" |
1368 | - version = "Ubuntu One/Login (%s)" % ubuntuone_client_version |
1369 | - def redirect_internal(self, url, fp, errcode, errmsg, headers, data): |
1370 | - """Actually perform a redirect""" |
1371 | - # All the same as the original, except passing data, below |
1372 | - if 'location' in headers: |
1373 | - newurl = headers['location'] |
1374 | - elif 'uri' in headers: |
1375 | - newurl = headers['uri'] |
1376 | - else: |
1377 | - return |
1378 | - fp.read() |
1379 | - fp.close() |
1380 | - # In case the server sent a relative URL, join with original: |
1381 | - newurl = urllib.basejoin(self.type + ":" + url, newurl) |
1382 | - |
1383 | - # pass data if present when we redirect |
1384 | - if data: |
1385 | - return self.open(newurl, data) |
1386 | - else: |
1387 | - return self.open(newurl) |
1388 | - |
1389 | -class AuthorisationClient(object): |
1390 | - """OAuth authorisation client.""" |
1391 | - def __init__(self, realm, request_token_url, user_authorisation_url, |
1392 | - access_token_url, consumer_key, consumer_secret, |
1393 | - callback_parent, callback_denied=None, |
1394 | - callback_notoken=None, callback_error=None, do_login=True, |
1395 | - keyring=gnomekeyring): |
1396 | - """Create an `AuthorisationClient` instance. |
1397 | - |
1398 | - @param realm: the OAuth realm. |
1399 | - @param request_token_url: the OAuth request token URL. |
1400 | - @param user_authorisation_url: the OAuth user authorisation URL. |
1401 | - @param access_token_url: the OAuth access token URL. |
1402 | - @param consumer_key: the OAuth consumer key. |
1403 | - @param consumer_secret: the OAuth consumer secret. |
1404 | - @param callback_parent: a function in the includer to call with a token |
1405 | - |
1406 | - The preceding parameters are defined in sections 3 and 4.1 of the |
1407 | - OAuth Core 1.0 specification. The following parameters are not: |
1408 | - |
1409 | - @param callback_denied: a function to call if no token is available |
1410 | - @param do_login: whether to create a token if one is not cached |
1411 | - @param keychain: the keyring object to use (defaults to gnomekeyring) |
1412 | - |
1413 | - """ |
1414 | - self.realm = realm |
1415 | - self.request_token_url = request_token_url |
1416 | - self.user_authorisation_url = user_authorisation_url |
1417 | - self.access_token_url = access_token_url |
1418 | - self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) |
1419 | - self.callback_parent = callback_parent |
1420 | - self.callback_denied = callback_denied |
1421 | - self.callback_notoken = callback_notoken |
1422 | - self.callback_error = callback_error |
1423 | - self.do_login = do_login |
1424 | - self.request_token = None |
1425 | - self.saved_acquire_details = (None, None, None) |
1426 | - self.keyring = keyring |
1427 | - logger.debug("auth.AuthorisationClient created with parameters "+ \ |
1428 | - "realm='%s', request_token_url='%s', user_authorisation_url='%s',"+\ |
1429 | - "access_token_url='%s', consumer_key='%s', callback_parent='%s'", |
1430 | - realm, request_token_url, user_authorisation_url, access_token_url, |
1431 | - consumer_key, callback_parent) |
1432 | - |
1433 | - def _get_keyring_items(self): |
1434 | - """Raw interface to obtain keyring items.""" |
1435 | - return self.keyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET, |
1436 | - {'ubuntuone-realm': self.realm, |
1437 | - 'oauth-consumer-key': |
1438 | - self.consumer.key}) |
1439 | - |
1440 | - def _forward_error_callback(self, error): |
1441 | - """Forward an error through callback_error()""" |
1442 | - if self.callback_error: |
1443 | - self.callback_error(str(error)) |
1444 | - else: |
1445 | - raise error |
1446 | - |
1447 | - def get_access_token(self): |
1448 | - """Get the access token from the keyring. |
1449 | - |
1450 | - If no token is available in the keyring, `NoAccessToken` is raised. |
1451 | - """ |
1452 | - logger.debug("Trying to fetch the token from the keyring") |
1453 | - try: |
1454 | - items = self._get_keyring_items() |
1455 | - except (gnomekeyring.NoMatchError, |
1456 | - gnomekeyring.DeniedError): |
1457 | - logger.debug("Access token was not in the keyring") |
1458 | - raise NoAccessToken("No access token found.") |
1459 | - logger.debug("Access token successfully found in the keyring") |
1460 | - return oauth.OAuthToken.from_string(items[0].secret) |
1461 | - |
1462 | - def clear_token(self): |
1463 | - """Clear any stored tokens from the keyring.""" |
1464 | - logger.debug("Searching keyring for existing tokens to delete.") |
1465 | - try: |
1466 | - items = self._get_keyring_items() |
1467 | - except (gnomekeyring.NoMatchError, |
1468 | - gnomekeyring.DeniedError): |
1469 | - logger.debug("No preexisting tokens found") |
1470 | - else: |
1471 | - logger.debug("Deleting %s tokens from the keyring" % len(items)) |
1472 | - for item in items: |
1473 | - try: |
1474 | - self.keyring.item_delete_sync(None, item.item_id) |
1475 | - except gnomekeyring.DeniedError: |
1476 | - logger.debug("Permission denied deleting token") |
1477 | - |
1478 | - def store_token(self, access_token): |
1479 | - """Store the given access token in the keyring. |
1480 | - |
1481 | - The keyring item is identified by the OAuth realm and consumer |
1482 | - key to support multiple instances. |
1483 | - """ |
1484 | - logger.debug("Trying to store the token in the keyring") |
1485 | - try: |
1486 | - item_id = self.keyring.item_create_sync( |
1487 | - None, |
1488 | - gnomekeyring.ITEM_GENERIC_SECRET, |
1489 | - 'UbuntuOne token for %s' % self.realm, |
1490 | - {'ubuntuone-realm': self.realm, |
1491 | - 'oauth-consumer-key': self.consumer.key}, |
1492 | - access_token.to_string(), |
1493 | - True) |
1494 | - except gnomekeyring.DeniedError: |
1495 | - logger.debug("Permission denied storing token") |
1496 | - else: |
1497 | - # set ACLs on the key for all apps listed in xdg BaseDir, but only |
1498 | - # the root level one, not the user-level one |
1499 | - logger.debug("Setting ACLs on the token in the keyring") |
1500 | - set_all_key_acls(item_id=item_id) |
1501 | - |
1502 | - # keyring seems to take a while to actually apply the change |
1503 | - # for when other people retrieve it, so sleep a bit. |
1504 | - # this ought to get fixed. |
1505 | - import time |
1506 | - time.sleep(4) |
1507 | - |
1508 | - def have_access_token(self): |
1509 | - """Returns true if an access token is available from the keyring.""" |
1510 | - try: |
1511 | - self.get_access_token() |
1512 | - except NoAccessToken: |
1513 | - return False |
1514 | - else: |
1515 | - return True |
1516 | - |
1517 | - def make_token_request(self, oauth_request): |
1518 | - """Perform the given `OAuthRequest` and return the associated token.""" |
1519 | - |
1520 | - logger.debug("Making a token request") |
1521 | - # Note that we monkeypatched httplib above to handle invalid certs |
1522 | - # Ways this urlopen can fail: |
1523 | - # bad certificate |
1524 | - # raises IOError, e.args[1] == SSLError, e.args[1].errno == 1 |
1525 | - # No such server |
1526 | - # raises IOError, e.args[1] == SSLError, e.args[1].errno == -2 |
1527 | - try: |
1528 | - opener = FancyURLOpenerWithRedirectedPOST() |
1529 | - fp = opener.open(oauth_request.http_url, oauth_request.to_postdata()) |
1530 | - data = fp.read() |
1531 | - except IOError, e: |
1532 | - self._forward_error_callback(e) |
1533 | - return |
1534 | - |
1535 | - # we deliberately trap anything that might go wrong when parsing the |
1536 | - # token, because we do not want this to explicitly fail |
1537 | - # pylint: disable-msg=W0702 |
1538 | - try: |
1539 | - out_token = oauth.OAuthToken.from_string(data) |
1540 | - logger.debug("Token successfully requested") |
1541 | - return out_token |
1542 | - except: |
1543 | - error = Exception(data) |
1544 | - logger.error("Token was not successfully retrieved: data was '%s'", |
1545 | - str(error)) |
1546 | - self._forward_error_callback(error) |
1547 | - |
1548 | - def open_in_browser(self, url): |
1549 | - """Open the given URL in the user's web browser.""" |
1550 | - logger.debug("Opening '%s' in the browser", url) |
1551 | - p = subprocess.Popen(["xdg-open", url], bufsize=4096, |
1552 | - stderr=subprocess.PIPE) |
1553 | - p.wait() |
1554 | - if p.returncode != 0: |
1555 | - errors = "".join(p.stderr.readlines()) |
1556 | - if errors != "": |
1557 | - self._forward_error_callback(IOError(errors)) |
1558 | - |
1559 | - def acquire_access_token_if_online(self, description=None, store=False): |
1560 | - """Check to see if we are online before trying to acquire""" |
1561 | - # Get NetworkManager state |
1562 | - logger.debug("Checking whether we are online") |
1563 | - try: |
1564 | - nm = dbus.SystemBus().get_object('org.freedesktop.NetworkManager', |
1565 | - '/org/freedesktop/NetworkManager', |
1566 | - follow_name_owner_changes=True) |
1567 | - except dbus.exceptions.DBusException: |
1568 | - logger.warn("Unable to connect to NetworkManager. Trying anyway.") |
1569 | - self.acquire_access_token(description, store) |
1570 | - else: |
1571 | - iface = dbus.Interface(nm, 'org.freedesktop.NetworkManager') |
1572 | - |
1573 | - def got_state(state): |
1574 | - """Handler for when state() call succeeds.""" |
1575 | - if state == NM_STATE_CONNECTED: |
1576 | - logger.debug("We are online") |
1577 | - self.acquire_access_token(description, store) |
1578 | - elif state == NM_STATE_CONNECTING: |
1579 | - logger.debug("We are currently going online") |
1580 | - # attach to NM's StateChanged signal |
1581 | - signal_match = nm.connect_to_signal( |
1582 | - signal_name="StateChanged", |
1583 | - handler_function=self.connection_established, |
1584 | - dbus_interface="org.freedesktop.NetworkManager") |
1585 | - # stash the details so the handler_function can get at them |
1586 | - self.saved_acquire_details = (signal_match, description, |
1587 | - store) |
1588 | - else: |
1589 | - # NM is not connected: fail |
1590 | - logger.debug("We are not online") |
1591 | - |
1592 | - def got_error(error): |
1593 | - """Handler for D-Bus errors when calling state().""" |
1594 | - if error.get_dbus_name() == \ |
1595 | - 'org.freedesktop.DBus.Error.ServiceUnknown': |
1596 | - logger.debug("NetworkManager not available.") |
1597 | - self.acquire_access_token(description, store) |
1598 | - else: |
1599 | - logger.error("Error contacting NetworkManager: %s" % \ |
1600 | - str(error)) |
1601 | - |
1602 | - |
1603 | - iface.state(reply_handler=got_state, error_handler=got_error) |
1604 | - |
1605 | - def connection_established(self, state): |
1606 | - """NetworkManager's state has changed, and we're watching for |
1607 | - a connection""" |
1608 | - logger.debug("Online status has changed to %s" % state) |
1609 | - if int(state) == NM_STATE_CONNECTED: |
1610 | - signal_match, description, store = self.saved_acquire_details |
1611 | - # disconnect the signal so we don't get called again |
1612 | - signal_match.remove() |
1613 | - # call the real acquire_access_token now it has a connection |
1614 | - logger.debug("Correctly connected: now starting auth process") |
1615 | - self.acquire_access_token(description, store) |
1616 | - else: |
1617 | - # connection changed but not to "connected", so keep waiting |
1618 | - logger.debug("Not yet connected: continuing to wait") |
1619 | - |
1620 | - def acquire_access_token(self, description=None, store=False): |
1621 | - """Create an OAuth access token authorised against the user.""" |
1622 | - signature_method = oauth.OAuthSignatureMethod_PLAINTEXT() |
1623 | - |
1624 | - # Create a request token ... |
1625 | - logger.debug("Creating a request token to begin access request") |
1626 | - parameters = {} |
1627 | - if description: |
1628 | - parameters['description'] = description |
1629 | - # Add a nonce to the query so we know the callback (to our temp |
1630 | - # webserver) came from us |
1631 | - nonce = random.randint(1000000, 10000000) |
1632 | - |
1633 | - # start temporary webserver to receive browser response |
1634 | - callback_url = self.get_temporary_httpd(nonce, |
1635 | - self.retrieve_access_token, store) |
1636 | - |
1637 | - oauth_request = oauth.OAuthRequest.from_consumer_and_token( |
1638 | - callback=callback_url, |
1639 | - http_url=self.request_token_url, |
1640 | - oauth_consumer=self.consumer, |
1641 | - parameters=parameters) |
1642 | - oauth_request.sign_request(signature_method, self.consumer, None) |
1643 | - logger.debug("Making token request") |
1644 | - self.request_token = self.make_token_request(oauth_request) |
1645 | - |
1646 | - # Request authorisation from the user |
1647 | - oauth_request = oauth.OAuthRequest.from_token_and_callback( |
1648 | - http_url=self.user_authorisation_url, |
1649 | - token=self.request_token) |
1650 | - nodename = os.uname()[1] |
1651 | - if nodename: |
1652 | - oauth_request.set_parameter("description", nodename) |
1653 | - Thread(target=self.open_in_browser, name="authorization", |
1654 | - args=(oauth_request.to_url(),)).start() |
1655 | - |
1656 | - def get_temporary_httpd(self, nonce, retrieve_function, store): |
1657 | - "A separate class so it can be mocked in testing" |
1658 | - logger.debug("Creating a listening temp web server") |
1659 | - site = TemporaryTwistedWebServer(nonce=nonce, |
1660 | - retrieve_function=retrieve_function, store_yes_no=store) |
1661 | - temphttpd = server.Site(site) |
1662 | - temphttpdport = reactor.listenTCP(0, temphttpd) |
1663 | - callback_url = "http://localhost:%s/?nonce=%s" % ( |
1664 | - temphttpdport.getHost().port, nonce) |
1665 | - site.set_port(temphttpdport) |
1666 | - logger.debug("Webserver listening on port '%s'", temphttpdport) |
1667 | - return callback_url |
1668 | - |
1669 | - def retrieve_access_token(self, store=False, verifier=None): |
1670 | - """Retrieve the access token, once OAuth is done. This is a callback.""" |
1671 | - logger.debug("Access token callback from temp webserver") |
1672 | - signature_method = oauth.OAuthSignatureMethod_PLAINTEXT() |
1673 | - oauth_request = oauth.OAuthRequest.from_consumer_and_token( |
1674 | - http_url=self.access_token_url, |
1675 | - oauth_consumer=self.consumer, |
1676 | - token=self.request_token) |
1677 | - oauth_request.set_parameter("oauth_verifier", verifier) |
1678 | - oauth_request.sign_request( |
1679 | - signature_method, self.consumer, self.request_token) |
1680 | - logger.debug("Retrieving access token from OAuth") |
1681 | - access_token = self.make_token_request(oauth_request) |
1682 | - if not access_token: |
1683 | - logger.error("Failed to get access token.") |
1684 | - if self.callback_denied is not None: |
1685 | - self.callback_denied() |
1686 | - else: |
1687 | - if store: |
1688 | - logger.debug("Storing access token in keyring") |
1689 | - self.store_token(access_token) |
1690 | - logger.debug("Calling the callback_parent") |
1691 | - self.callback_parent(access_token) |
1692 | - |
1693 | - def ensure_access_token(self, description=None): |
1694 | - """Returns an access token, either from the keyring or newly acquired. |
1695 | - |
1696 | - If a new token is acquired, it will be stored in the keyring |
1697 | - for future use. |
1698 | - """ |
1699 | - try: |
1700 | - access_token = self.get_access_token() |
1701 | - self.callback_parent(access_token) |
1702 | - except NoAccessToken: |
1703 | - if self.do_login: |
1704 | - access_token = self.acquire_access_token_if_online( |
1705 | - description, |
1706 | - store=True) |
1707 | - else: |
1708 | - if self.callback_notoken is not None: |
1709 | - self.callback_notoken() |
1710 | - |
1711 | - |
1712 | -class TemporaryTwistedWebServer(resource.Resource): |
1713 | - """A temporary httpd for the oauth process to call back to""" |
1714 | - isLeaf = True |
1715 | - def __init__(self, nonce, store_yes_no, retrieve_function): |
1716 | - """Initialize the temporary web server.""" |
1717 | - resource.Resource.__init__(self) |
1718 | - self.nonce = nonce |
1719 | - self.store_yes_no = store_yes_no |
1720 | - self.retrieve_function = retrieve_function |
1721 | - reactor.callLater(600, self.stop) # ten minutes |
1722 | - self.port = None |
1723 | - def set_port(self, port): |
1724 | - """Save the Twisted port object so we can stop it later""" |
1725 | - self.port = port |
1726 | - def stop(self): |
1727 | - """Stop the httpd""" |
1728 | - logger.debug("Stopping temp webserver") |
1729 | - self.port.stopListening() |
1730 | - def render_GET(self, request): |
1731 | - """Handle incoming web requests""" |
1732 | - logger.debug("Incoming temp webserver hit received") |
1733 | - nonce = request.args.get("nonce", [None])[0] |
1734 | - url = request.args.get("return", ["https://one.ubuntu.com/"])[0] |
1735 | - verifier = request.args.get("oauth_verifier", [None])[0] |
1736 | - logger.debug("Got verifier %s" % verifier) |
1737 | - if nonce and (str(nonce) == str(self.nonce) and verifier): |
1738 | - self.retrieve_function(store=self.store_yes_no, verifier=verifier) |
1739 | - reactor.callLater(3, self.stop) |
1740 | - return """<!doctype html> |
1741 | - <html><head><meta http-equiv="refresh" |
1742 | - content="0;url=%(url)s"> |
1743 | - </head> |
1744 | - <body> |
1745 | - <p>You should now automatically <a |
1746 | - href="%(url)s">return to %(url)s</a>.</p> |
1747 | - </body> |
1748 | - </html> |
1749 | - """ % { 'url' : url } |
1750 | - else: |
1751 | - self.retrieve_function(store=self.store_yes_no, verifier=verifier) |
1752 | - reactor.callLater(3, self.stop) |
1753 | - request.setResponseCode(400) |
1754 | - return """<!doctype html> |
1755 | - <html><head><title>Error</title></head> |
1756 | - <body> |
1757 | - <h1>There was an error</h1> |
1758 | - <p>The authentication process has not succeeded. This may be a |
1759 | - temporary problem; please try again in a few minutes.</p> |
1760 | - </body> |
1761 | - </html> |
1762 | - """ |
1763 | - |
1764 | - |
1765 | - |
1766 | |
1767 | === removed file 'ubuntuone/oauthdesktop/config.py' |
1768 | --- ubuntuone/oauthdesktop/config.py 2009-07-22 18:14:52 +0000 |
1769 | +++ ubuntuone/oauthdesktop/config.py 1970-01-01 00:00:00 +0000 |
1770 | @@ -1,47 +0,0 @@ |
1771 | -# ubuntuone.oauthdesktop.config - Configuration for OAuthDesktop |
1772 | -# |
1773 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
1774 | -# |
1775 | -# Copyright 2009 Canonical Ltd. |
1776 | -# |
1777 | -# This program is free software: you can redistribute it and/or modify it |
1778 | -# under the terms of the GNU General Public License version 3, as published |
1779 | -# by the Free Software Foundation. |
1780 | -# |
1781 | -# This program is distributed in the hope that it will be useful, but |
1782 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1783 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1784 | -# PURPOSE. See the GNU General Public License for more details. |
1785 | -# |
1786 | -# You should have received a copy of the GNU General Public License along |
1787 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1788 | -"Find the config file for ubuntuone-oauth-config" |
1789 | -import os, ConfigParser |
1790 | -from xdg.BaseDirectory import load_first_config |
1791 | - |
1792 | -def get_config(use_tmpconfig=True): |
1793 | - """Return a ConfigParser object from a config file. |
1794 | - Config file is looked for in the source tree first and then in the |
1795 | - FreeDesktop BaseDirectory folders. |
1796 | - """ |
1797 | - # if tmpconfig exists, then we are running out of the source tree |
1798 | - tmpconfig = os.path.realpath(os.path.join( |
1799 | - __file__, "../../../data/oauth_urls" |
1800 | - )) |
1801 | - if os.path.isfile(tmpconfig) and use_tmpconfig: |
1802 | - config_file = tmpconfig |
1803 | - else: |
1804 | - config_file = load_first_config('ubuntuone','oauth_urls') |
1805 | - |
1806 | - cfp = ConfigParser.ConfigParser() |
1807 | - cfp.FILENAME = None |
1808 | - if config_file is not None: |
1809 | - try: |
1810 | - cfp.read(config_file) |
1811 | - cfp.FILENAME = config_file |
1812 | - except ConfigParser.Error: |
1813 | - cfp = ConfigParser.ConfigParser() |
1814 | - cfp.FILENAME = None |
1815 | - return cfp |
1816 | - |
1817 | - |
1818 | |
1819 | === removed file 'ubuntuone/oauthdesktop/key_acls.py' |
1820 | --- ubuntuone/oauthdesktop/key_acls.py 2010-03-10 21:11:45 +0000 |
1821 | +++ ubuntuone/oauthdesktop/key_acls.py 1970-01-01 00:00:00 +0000 |
1822 | @@ -1,162 +0,0 @@ |
1823 | -# ubuntuone.oauthdesktop.key_acls - OAuth ACL handling for keyring |
1824 | -# |
1825 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
1826 | -# |
1827 | -# Copyright 2009 Canonical Ltd. |
1828 | -# |
1829 | -# This program is free software: you can redistribute it and/or modify it |
1830 | -# under the terms of the GNU General Public License version 3, as published |
1831 | -# by the Free Software Foundation. |
1832 | -# |
1833 | -# This program is distributed in the hope that it will be useful, but |
1834 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1835 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1836 | -# PURPOSE. See the GNU General Public License for more details. |
1837 | -# |
1838 | -# You should have received a copy of the GNU General Public License along |
1839 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1840 | -"""OAuth client authorisation code. |
1841 | - |
1842 | -This code finds all apps that have pre-registered themselves as wanting to |
1843 | -access OAuth tokens from the Gnome keyring without the user having to approve |
1844 | -that, and sets ACLs on relevant keys so they can do so. |
1845 | - |
1846 | -Apps pre-register themselves by dropping an ini file in |
1847 | -/etc/xdg/ubuntuone/oauth_registration.d/ in which each section has keys |
1848 | -realm, consumer_key, exe_path, application_name. |
1849 | -""" |
1850 | - |
1851 | -import xdg.BaseDirectory, os, ConfigParser, gnomekeyring |
1852 | - |
1853 | -def get_privileged_config_folder(use_source_tree_folder=True): |
1854 | - """Find the XDG config folder to use which is not the user's personal |
1855 | - config (i.e., ~/.config) so that files in it are root-owned""" |
1856 | - |
1857 | - # First, check for folder (if we're running from the source tree) |
1858 | - if use_source_tree_folder: |
1859 | - source_tree_folder = os.path.join( |
1860 | - os.path.split(__file__)[0], |
1861 | - "../../data/oauth_registration.d") |
1862 | - if os.path.isdir(source_tree_folder): |
1863 | - return os.path.join(source_tree_folder, "..") |
1864 | - |
1865 | - # Otherwise, check for proper XDG folders |
1866 | - privileged_folders = [x for x in |
1867 | - xdg.BaseDirectory.load_config_paths('ubuntuone') |
1868 | - if not x.startswith(xdg.BaseDirectory.xdg_config_home)] |
1869 | - if privileged_folders: |
1870 | - return privileged_folders[0] |
1871 | - else: |
1872 | - return None |
1873 | - |
1874 | -def get_acl_preregistrations(use_source_tree_folder=True): |
1875 | - "Return a list of all config files in the pre-registration folder" |
1876 | - config_folder = get_privileged_config_folder(use_source_tree_folder) |
1877 | - if config_folder: |
1878 | - conf_dir = os.path.join(config_folder, |
1879 | - "oauth_registration.d") |
1880 | - if os.path.isdir(conf_dir): |
1881 | - return [os.path.join(conf_dir, x) for x in os.listdir(conf_dir)] |
1882 | - return [] |
1883 | - |
1884 | -def get_item_ids_for_realm(realm, consumer_key): |
1885 | - "Find all keyring tokens for a specific realm/consumer_key" |
1886 | - if realm == "http://localhost": |
1887 | - # if realm (from the config file) is localhost, then the token |
1888 | - # will have ubuntuone-realm == http://localhost:SOMETHING |
1889 | - # so find all keys with this consumer_key, and pass on any |
1890 | - # where ubuntuone-realm begins with http://localhost: |
1891 | - try: |
1892 | - items = gnomekeyring.find_items_sync( |
1893 | - gnomekeyring.ITEM_GENERIC_SECRET, |
1894 | - { |
1895 | - 'oauth-consumer-key': consumer_key}) |
1896 | - except (gnomekeyring.NoMatchError, |
1897 | - gnomekeyring.DeniedError): |
1898 | - return [] |
1899 | - items = [x.item_id for x in items if |
1900 | - x.attributes.get("ubuntuone-realm", "").startswith("http://localhost:")] |
1901 | - return items |
1902 | - else: |
1903 | - # realm was not localhost, so search for it explicitly |
1904 | - try: |
1905 | - items = gnomekeyring.find_items_sync( |
1906 | - gnomekeyring.ITEM_GENERIC_SECRET, |
1907 | - {'ubuntuone-realm': realm, |
1908 | - 'oauth-consumer-key': consumer_key}) |
1909 | - except (gnomekeyring.NoMatchError, |
1910 | - gnomekeyring.DeniedError): |
1911 | - return [] |
1912 | - return [x.item_id for x in items] |
1913 | - |
1914 | -def set_single_acl(app_sets, specific_item_id=None): |
1915 | - """Allow a specified set of apps to access a matching keyring |
1916 | - token without prompts""" |
1917 | - for realm, consumer_key, exe_path, application_name in app_sets: |
1918 | - if specific_item_id is None: |
1919 | - items = get_item_ids_for_realm(realm, consumer_key) |
1920 | - else: |
1921 | - # item_id specified |
1922 | - items = [specific_item_id] |
1923 | - |
1924 | - # set an ACL on the key so the calling app can read it without |
1925 | - # a prompt dialog |
1926 | - for item_id in items: |
1927 | - acl = gnomekeyring.item_get_acl_sync(None, item_id) |
1928 | - new_acls = False |
1929 | - real_exe_path = os.path.realpath(exe_path) |
1930 | - for acl_item in acl: |
1931 | - if acl_item.get_display_name() == application_name and \ |
1932 | - acl_item.get_path_name() == real_exe_path: |
1933 | - # this ACL is already set |
1934 | - break |
1935 | - else: |
1936 | - appref = gnomekeyring.ApplicationRef() |
1937 | - ac = gnomekeyring.AccessControl(appref, |
1938 | - gnomekeyring.ACCESS_READ | |
1939 | - gnomekeyring.ACCESS_WRITE | gnomekeyring.ACCESS_REMOVE) |
1940 | - ac.set_display_name(application_name) |
1941 | - ac.set_path_name(real_exe_path) |
1942 | - acl.append(ac) |
1943 | - new_acls = True |
1944 | - if new_acls: |
1945 | - gnomekeyring.item_set_acl_sync(None, item_id, acl) |
1946 | - |
1947 | -def set_all_key_acls(item_id=None, use_source_tree_folder=True): |
1948 | - """For each file in the config folder, get the (realm, key) pair that |
1949 | - the program therein is interested in and register the program as able |
1950 | - to access those keys by setting an ACL on them.""" |
1951 | - for config_file in get_acl_preregistrations(use_source_tree_folder): |
1952 | - cfp = ConfigParser.ConfigParser() |
1953 | - try: |
1954 | - cfp.read(config_file) |
1955 | - except ConfigParser.Error: |
1956 | - continue |
1957 | - |
1958 | - app_sets = [] |
1959 | - |
1960 | - for section in cfp.sections(): |
1961 | - try: |
1962 | - realm = cfp.get(section, "realm") |
1963 | - except ConfigParser.NoOptionError: |
1964 | - realm = None |
1965 | - try: |
1966 | - consumer_key = cfp.get(section, "consumer_key") |
1967 | - except ConfigParser.NoOptionError: |
1968 | - consumer_key = None |
1969 | - try: |
1970 | - exe_path = cfp.get(section, "exe_path") |
1971 | - except ConfigParser.NoOptionError: |
1972 | - exe_path = None |
1973 | - try: |
1974 | - application_name = cfp.get(section, "application_name") |
1975 | - except ConfigParser.NoOptionError: |
1976 | - application_name = None |
1977 | - if realm and consumer_key and exe_path and application_name: |
1978 | - app_sets.append((realm, consumer_key, exe_path, |
1979 | - application_name)) |
1980 | - |
1981 | - if app_sets: |
1982 | - set_single_acl(app_sets, specific_item_id=item_id) |
1983 | - |
1984 | - |
1985 | |
1986 | === removed file 'ubuntuone/oauthdesktop/logger.py' |
1987 | --- ubuntuone/oauthdesktop/logger.py 2009-10-16 19:56:57 +0000 |
1988 | +++ ubuntuone/oauthdesktop/logger.py 1970-01-01 00:00:00 +0000 |
1989 | @@ -1,49 +0,0 @@ |
1990 | -# ubuntuone.oauthdesktop.logger - logging miscellany |
1991 | -# |
1992 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
1993 | -# |
1994 | -# Copyright 2009 Canonical Ltd. |
1995 | -# |
1996 | -# This program is free software: you can redistribute it and/or modify it |
1997 | -# under the terms of the GNU General Public License version 3, as published |
1998 | -# by the Free Software Foundation. |
1999 | -# |
2000 | -# This program is distributed in the hope that it will be useful, but |
2001 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
2002 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2003 | -# PURPOSE. See the GNU General Public License for more details. |
2004 | -# |
2005 | -# You should have received a copy of the GNU General Public License along |
2006 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
2007 | -"""Miscellaneous logging functions.""" |
2008 | -import os |
2009 | -import logging |
2010 | -import xdg.BaseDirectory |
2011 | - |
2012 | -from logging.handlers import RotatingFileHandler |
2013 | - |
2014 | -home = xdg.BaseDirectory.xdg_cache_home |
2015 | -LOGFOLDER = os.path.join(home, 'ubuntuone', 'log') |
2016 | -# create log folder if it doesn't exists |
2017 | -if not os.path.exists(LOGFOLDER): |
2018 | - os.makedirs(LOGFOLDER) |
2019 | - |
2020 | -LOGFILENAME = os.path.join(LOGFOLDER, 'oauth-login.log') |
2021 | - |
2022 | -# Only log this level and above |
2023 | -LOG_LEVEL = logging.INFO |
2024 | - |
2025 | -root_formatter = logging.Formatter( |
2026 | - fmt="%(asctime)s:%(msecs)s %(name)s %(message)s") |
2027 | -root_handler = RotatingFileHandler(LOGFILENAME, maxBytes=1048576, |
2028 | - backupCount=1) |
2029 | -root_handler.setLevel(LOG_LEVEL) |
2030 | -root_handler.setFormatter(root_formatter) |
2031 | - |
2032 | -def setupLogging(log_domain): |
2033 | - """Create basic logger to set filename""" |
2034 | - logger = logging.getLogger(log_domain) |
2035 | - logger.propagate = False |
2036 | - logger.setLevel(LOG_LEVEL) |
2037 | - logger.addHandler(root_handler) |
2038 | - return logger |
2039 | |
2040 | === removed file 'ubuntuone/oauthdesktop/main.py' |
2041 | --- ubuntuone/oauthdesktop/main.py 2010-03-30 21:11:52 +0000 |
2042 | +++ ubuntuone/oauthdesktop/main.py 1970-01-01 00:00:00 +0000 |
2043 | @@ -1,296 +0,0 @@ |
2044 | -#!/usr/bin/python |
2045 | - |
2046 | -# ubuntuone.oauthdesktop.main - main login handling interface |
2047 | -# |
2048 | -# Author: Stuart Langridge <stuart.langridge@canonical.com> |
2049 | -# |
2050 | -# Copyright 2009 Canonical Ltd. |
2051 | -# |
2052 | -# This program is free software: you can redistribute it and/or modify it |
2053 | -# under the terms of the GNU General Public License version 3, as published |
2054 | -# by the Free Software Foundation. |
2055 | -# |
2056 | -# This program is distributed in the hope that it will be useful, but |
2057 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
2058 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2059 | -# PURPOSE. See the GNU General Public License for more details. |
2060 | -# |
2061 | -# You should have received a copy of the GNU General Public License along |
2062 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
2063 | -"""OAuth login handler. |
2064 | - |
2065 | -A command-line utility which accepts requests for OAuth login over D-Bus, |
2066 | -handles the OAuth process (including adding the OAuth access token to the |
2067 | -gnome keyring), and then alerts the calling app (and others) with a D-Bus |
2068 | -signal so they can retrieve the new token. |
2069 | -""" |
2070 | - |
2071 | -import dbus.service, urlparse, time, gobject |
2072 | -import pynotify |
2073 | - |
2074 | -from dbus.mainloop.glib import DBusGMainLoop |
2075 | - |
2076 | -from ubuntuone.oauthdesktop.config import get_config |
2077 | - |
2078 | -from ubuntuone.oauthdesktop.logger import setupLogging |
2079 | -logger = setupLogging("UbuntuOne.OAuthDesktop.main") |
2080 | - |
2081 | -DBusGMainLoop(set_as_default=True) |
2082 | - |
2083 | -# Disable the invalid name warning, as we have a lot of DBus style names |
2084 | -# pylint: disable-msg=C0103 |
2085 | - |
2086 | -class NoDefaultConfigError(Exception): |
2087 | - """No default section in configuration file""" |
2088 | - pass |
2089 | - |
2090 | -class BadRealmError(Exception): |
2091 | - """Realm must be a URL""" |
2092 | - pass |
2093 | - |
2094 | -class LoginProcessor: |
2095 | - """Actually do the work of processing passed parameters""" |
2096 | - def __init__(self, dbus_object, use_libnotify=True): |
2097 | - """Initialize the login processor.""" |
2098 | - logger.debug("Creating a LoginProcessor") |
2099 | - self.use_libnotify = use_libnotify |
2100 | - if self.use_libnotify and pynotify: |
2101 | - logger.debug("Hooking libnotify") |
2102 | - pynotify.init("UbuntuOne Login") |
2103 | - self.note1 = None |
2104 | - self.realm = None |
2105 | - self.consumer_key = None |
2106 | - self.dbus_object = dbus_object |
2107 | - logger.debug("Getting configuration") |
2108 | - self.config = get_config() |
2109 | - |
2110 | - def login(self, realm, consumer_key, do_login=True): |
2111 | - """Initiate an OAuth login""" |
2112 | - logger.debug("Initiating OAuth login in LoginProcessor") |
2113 | - self.realm = str(realm) # because they are dbus.Strings, not str |
2114 | - self.consumer_key = str(consumer_key) |
2115 | - |
2116 | - logger.debug("Obtaining OAuth urls") |
2117 | - (request_token_url, user_authorisation_url, |
2118 | - access_token_url, consumer_secret) = self.get_config_urls(realm) |
2119 | - logger.debug("OAuth URLs are: request='%s', userauth='%s', " +\ |
2120 | - "access='%s', secret='%s'", request_token_url, |
2121 | - user_authorisation_url, access_token_url, consumer_secret) |
2122 | - |
2123 | - from ubuntuone.oauthdesktop.auth import AuthorisationClient |
2124 | - client = AuthorisationClient(self.realm, |
2125 | - request_token_url, |
2126 | - user_authorisation_url, |
2127 | - access_token_url, self.consumer_key, |
2128 | - consumer_secret, |
2129 | - callback_parent=self.got_token, |
2130 | - callback_denied=self.got_denial, |
2131 | - callback_notoken=self.got_no_token, |
2132 | - callback_error=self.got_error, |
2133 | - do_login=do_login) |
2134 | - |
2135 | - logger.debug("Calling auth.client.ensure_access_token in thread") |
2136 | - gobject.timeout_add_seconds(1, client.ensure_access_token) |
2137 | - |
2138 | - def clear_token(self, realm, consumer_key): |
2139 | - """Remove the currently stored OAuth token from the keyring.""" |
2140 | - self.realm = str(realm) |
2141 | - self.consumer_key = str(consumer_key) |
2142 | - (request_token_url, user_authorisation_url, |
2143 | - access_token_url, consumer_secret) = self.get_config_urls(self.realm) |
2144 | - from ubuntuone.oauthdesktop.auth import AuthorisationClient |
2145 | - client = AuthorisationClient(self.realm, |
2146 | - request_token_url, |
2147 | - user_authorisation_url, |
2148 | - access_token_url, |
2149 | - self.consumer_key, consumer_secret, |
2150 | - callback_parent=self.got_token, |
2151 | - callback_denied=self.got_denial, |
2152 | - callback_notoken=self.got_no_token, |
2153 | - callback_error=self.got_error) |
2154 | - gobject.timeout_add_seconds(1, client.clear_token) |
2155 | - |
2156 | - def error_handler(self, failure): |
2157 | - """Deal with errors returned from auth process""" |
2158 | - logger.debug("Error returned from auth process") |
2159 | - self.dbus_object.currently_authing = False # not block future requests |
2160 | - |
2161 | - def get_config_urls(self, realm): |
2162 | - """Look up the URLs to use in the config file""" |
2163 | - logger.debug("Fetching config URLs for realm='%s'", realm) |
2164 | - if self.config.has_section(realm): |
2165 | - logger.debug("Realm '%s' is in config", realm) |
2166 | - request_token_url = self.__get_url(realm, "request_token_url") |
2167 | - user_authorisation_url = self.__get_url(realm, |
2168 | - "user_authorisation_url") |
2169 | - access_token_url = self.__get_url(realm, "access_token_url") |
2170 | - consumer_secret = self.__get_option(realm, "consumer_secret") |
2171 | - elif realm.startswith("http://localhost") and \ |
2172 | - self.config.has_section("http://localhost"): |
2173 | - logger.debug("Realm is localhost and is in config") |
2174 | - request_token_url = self.__get_url("http://localhost", |
2175 | - "request_token_url", realm) |
2176 | - user_authorisation_url = self.__get_url("http://localhost", |
2177 | - "user_authorisation_url", realm) |
2178 | - access_token_url = self.__get_url("http://localhost", |
2179 | - "access_token_url", realm) |
2180 | - consumer_secret = self.__get_option("http://localhost", |
2181 | - "consumer_secret") |
2182 | - elif self.is_valid_url(realm): |
2183 | - logger.debug("Realm '%s' is not in config", realm) |
2184 | - request_token_url = self.__get_url("default", |
2185 | - "request_token_url", realm) |
2186 | - user_authorisation_url = self.__get_url("default", |
2187 | - "user_authorisation_url", realm) |
2188 | - access_token_url = self.__get_url("default", |
2189 | - "access_token_url", realm) |
2190 | - consumer_secret = self.__get_option(realm, "consumer_secret") |
2191 | - else: |
2192 | - logger.debug("Realm '%s' is a bad realm", realm) |
2193 | - raise BadRealmError |
2194 | - return (request_token_url, user_authorisation_url, |
2195 | - access_token_url, consumer_secret) |
2196 | - |
2197 | - def is_valid_url(self, url): |
2198 | - """Simple check for URL validity""" |
2199 | - # pylint: disable-msg=W0612 |
2200 | - scheme, netloc, path, query, fragment = urlparse.urlsplit(url) |
2201 | - if scheme and netloc: |
2202 | - return True |
2203 | - else: |
2204 | - return False |
2205 | - |
2206 | - def got_token(self, access_token): |
2207 | - """Callback function when access token has been retrieved""" |
2208 | - logger.debug("Token retrieved, calling NewCredentials function") |
2209 | - self.dbus_object.NewCredentials(self.realm, self.consumer_key) |
2210 | - |
2211 | - def got_denial(self): |
2212 | - """Callback function when request token has been denied""" |
2213 | - self.dbus_object.AuthorizationDenied() |
2214 | - |
2215 | - def got_no_token(self): |
2216 | - """Callback function when access token is not in keyring.""" |
2217 | - self.dbus_object.NoCredentials() |
2218 | - |
2219 | - def got_error(self, message): |
2220 | - """Callback function to emit an error message over DBus.""" |
2221 | - self.dbus_object.OAuthError(message) |
2222 | - |
2223 | - def __get_url(self, realm, option, actual_realm=None): |
2224 | - """Construct a full URL from realm and a URLpath for that realm in |
2225 | - the config file.""" |
2226 | - if actual_realm: |
2227 | - realm_to_use = actual_realm |
2228 | - else: |
2229 | - realm_to_use = realm |
2230 | - urlstub = self.__get_option(realm, option) |
2231 | - return urlparse.urljoin(realm_to_use, urlstub) |
2232 | - |
2233 | - def __get_option(self, realm, option): |
2234 | - """Return a specific option for that realm in |
2235 | - the config file. If the realm does not exist in the config file, |
2236 | - fall back to the [default] section.""" |
2237 | - if self.config.has_section(realm) and \ |
2238 | - self.config.has_option(realm, option): |
2239 | - urlstub = self.config.get(realm, option) |
2240 | - return urlstub |
2241 | - |
2242 | - # either the realm exists and this url does not, or |
2243 | - # the realm doesn't exist; either way, fall back to [default] section |
2244 | - urlstub = self.config.get("default", option, None) |
2245 | - if urlstub is not None: |
2246 | - return urlstub |
2247 | - |
2248 | - # this url does not exist in default section either |
2249 | - # this shouldn't happen |
2250 | - raise NoDefaultConfigError("No default configuration for %s" % option) |
2251 | - |
2252 | - |
2253 | -class Login(dbus.service.Object): |
2254 | - """Object which listens for D-Bus OAuth requests""" |
2255 | - def __init__(self, bus_name): |
2256 | - """Initiate the Login object.""" |
2257 | - dbus.service.Object.__init__(self, object_path="/", bus_name=bus_name) |
2258 | - self.processor = LoginProcessor(self) |
2259 | - self.currently_authing = False |
2260 | - logger.debug("Login D-Bus service starting up") |
2261 | - |
2262 | - @dbus.service.method(dbus_interface='com.ubuntuone.Authentication', |
2263 | - in_signature='ss', out_signature='') |
2264 | - def login(self, realm, consumer_key): |
2265 | - """D-Bus method, exported over the bus, to initiate an OAuth login""" |
2266 | - logger.debug("login() D-Bus message received with realm='%s', " + |
2267 | - "consumer_key='%s'", realm, consumer_key) |
2268 | - if self.currently_authing: |
2269 | - logger.debug("Currently in the middle of OAuth: rejecting this") |
2270 | - return |
2271 | - self.currently_authing = True |
2272 | - self.processor.login(realm, consumer_key) |
2273 | - |
2274 | - @dbus.service.method(dbus_interface='com.ubuntuone.Authentication', |
2275 | - in_signature='ssb', out_signature='') |
2276 | - def maybe_login(self, realm, consumer_key, do_login): |
2277 | - """ |
2278 | - D-Bus method, exported over the bus, to maybe initiate an OAuth login |
2279 | - """ |
2280 | - logger.debug("maybe_login() D-Bus message received with realm='%s', " + |
2281 | - "consumer_key='%s'", realm, consumer_key) |
2282 | - if self.currently_authing: |
2283 | - logger.debug("Currently in the middle of OAuth: rejecting this") |
2284 | - return |
2285 | - self.currently_authing = True |
2286 | - self.processor.login(realm, consumer_key, do_login) |
2287 | - |
2288 | - @dbus.service.method(dbus_interface='com.ubuntuone.Authentication', |
2289 | - in_signature='ss', out_signature='') |
2290 | - def clear_token(self, realm, consumer_key): |
2291 | - """ |
2292 | - D-Bus method, exported over the bus, to clear the existing token. |
2293 | - """ |
2294 | - self.processor.clear_token(realm, consumer_key) |
2295 | - |
2296 | - @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication', |
2297 | - signature='ss') |
2298 | - def NewCredentials(self, realm, consumer_key): |
2299 | - """Fire D-Bus signal when the user accepts authorization.""" |
2300 | - logger.debug("Firing the NewCredentials signal") |
2301 | - self.currently_authing = False |
2302 | - return (self.processor.realm, self.processor.consumer_key) |
2303 | - |
2304 | - @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication') |
2305 | - def AuthorizationDenied(self): |
2306 | - """Fire the signal when the user denies authorization.""" |
2307 | - self.currently_authing = False |
2308 | - |
2309 | - @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication') |
2310 | - def NoCredentials(self): |
2311 | - """Fired when the user does not have a token in the keyring.""" |
2312 | - self.currently_authing = False |
2313 | - |
2314 | - @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication', |
2315 | - signature='s') |
2316 | - def OAuthError(self, message): |
2317 | - """Fire the signal when an error needs to be propagated to the user.""" |
2318 | - self.currently_authing = False |
2319 | - return message |
2320 | - |
2321 | -def main(): |
2322 | - """Start everything""" |
2323 | - logger.debug("Starting up at %s", time.asctime()) |
2324 | - logger.debug("Installing the Twisted glib2reactor") |
2325 | - from twisted.internet import glib2reactor # for non-GUI apps |
2326 | - glib2reactor.install() |
2327 | - from twisted.internet import reactor |
2328 | - |
2329 | - logger.debug("Creating the D-Bus service") |
2330 | - Login(dbus.service.BusName("com.ubuntuone.Authentication", |
2331 | - bus=dbus.SessionBus())) |
2332 | - # cleverness here to say: |
2333 | - # am I already running (bound to this d-bus name)? |
2334 | - # if so, send a signal to the already running instance |
2335 | - # this means that this app can be started from an x-ubutnuone: URL |
2336 | - # to kick off the signin process |
2337 | - logger.debug("Starting the reactor mainloop") |
2338 | - reactor.run() |
2339 | - |
2340 | |
2341 | === modified file 'ubuntuone/syncdaemon/dbus_interface.py' |
2342 | --- ubuntuone/syncdaemon/dbus_interface.py 2010-06-02 18:00:29 +0000 |
2343 | +++ ubuntuone/syncdaemon/dbus_interface.py 2010-06-16 20:35:34 +0000 |
2344 | @@ -33,7 +33,7 @@ |
2345 | from ubuntuone.syncdaemon.interfaces import IMarker |
2346 | from ubuntuone.syncdaemon import config |
2347 | from ubuntuone.syncdaemon.volume_manager import Share, UDF, VolumeDoesNotExist |
2348 | - |
2349 | +from ubuntu_sso import DBUS_IFACE_AUTH_NAME, DBUS_PATH_AUTH |
2350 | |
2351 | # Disable the "Invalid Name" check here, as we have lots of DBus style names |
2352 | # pylint: disable-msg=C0103 |
2353 | @@ -58,10 +58,6 @@ |
2354 | NM_STATE_EVENTS = {NM_STATE_CONNECTED: 'SYS_NET_CONNECTED', |
2355 | NM_STATE_DISCONNECTED: 'SYS_NET_DISCONNECTED'} |
2356 | |
2357 | -# OAuthDesktop constants |
2358 | -DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication" |
2359 | -DBUS_PATH_AUTH = "/" |
2360 | - |
2361 | logger = logging.getLogger("ubuntuone.SyncDaemon.DBus") |
2362 | |
2363 | def get_classname(thing): |
Let's first finish the renaming of the Python packages and DBus service in ubuntu-sso-client, and then let's change this branch to reflect that renaming, so that we don't need to do any further changes (at least renames) in u1-client