Merge lp:~alecu/ubuntuone-client/proxy-tunnel-webcalls into lp:ubuntuone-client
- proxy-tunnel-webcalls
- Merge into trunk
Proposed by
Alejandro J. Cura
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Alejandro J. Cura | ||||||||
Approved revision: | 1231 | ||||||||
Merged at revision: | 1208 | ||||||||
Proposed branch: | lp:~alecu/ubuntuone-client/proxy-tunnel-webcalls | ||||||||
Merge into: | lp:ubuntuone-client | ||||||||
Diff against target: |
524 lines (+97/-205) 2 files modified
tests/syncdaemon/test_action_queue.py (+51/-137) ubuntuone/syncdaemon/action_queue.py (+46/-68) |
||||||||
To merge this branch: | bzr merge lp:~alecu/ubuntuone-client/proxy-tunnel-webcalls | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Eric Casteleijn (community) | Approve | ||
Diego Sarmentero (community) | Approve | ||
Review via email: mp+97134@code.launchpad.net |
Description of the change
- Use the txweb webclient from sso for webcalls, so they can be proxied too. (LP: #929207, LP: #929212)
**NOTE** This branch depends on this SSO branch: lp:~alecu/ubuntu-sso-client/updated-txweb
To post a comment you must log in.
- 1231. By Alejandro J. Cura
-
timestamp is already corrected by txweb in sso
Revision history for this message
Alejandro J. Cura (alecu) wrote : | # |
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote : | # |
GREAT WORK!
+1
review:
Approve
Revision history for this message
Eric Casteleijn (thisfred) wrote : | # |
Looks good to me
review:
Approve
Revision history for this message
Roberto Alsina (ralsina) wrote : | # |
looks good, seems to work...
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'tests/syncdaemon/test_action_queue.py' | |||
2 | --- tests/syncdaemon/test_action_queue.py 2012-03-06 20:32:14 +0000 | |||
3 | +++ tests/syncdaemon/test_action_queue.py 2012-03-13 03:12:18 +0000 | |||
4 | @@ -23,7 +23,6 @@ | |||
5 | 23 | import operator | 23 | import operator |
6 | 24 | import os | 24 | import os |
7 | 25 | import unittest | 25 | import unittest |
8 | 26 | import urllib2 | ||
9 | 27 | import uuid | 26 | import uuid |
10 | 28 | 27 | ||
11 | 29 | from functools import wraps | 28 | from functools import wraps |
12 | @@ -33,7 +32,7 @@ | |||
13 | 33 | 32 | ||
14 | 34 | from mocker import Mocker, MockerTestCase, ANY, expect | 33 | from mocker import Mocker, MockerTestCase, ANY, expect |
15 | 35 | from oauth import oauth | 34 | from oauth import oauth |
17 | 36 | from twisted.internet import defer, threads, reactor | 35 | from twisted.internet import defer, reactor |
18 | 37 | from twisted.internet import error as twisted_error | 36 | from twisted.internet import error as twisted_error |
19 | 38 | from twisted.python.failure import DefaultException, Failure | 37 | from twisted.python.failure import DefaultException, Failure |
20 | 39 | from twisted.web import server | 38 | from twisted.web import server |
21 | @@ -399,6 +398,21 @@ | |||
22 | 399 | self.assertEqual(defined_args[0], 'self') | 398 | self.assertEqual(defined_args[0], 'self') |
23 | 400 | self.assertEqual(set(defined_args[1:]), set(evtargs)) | 399 | self.assertEqual(set(defined_args[1:]), set(evtargs)) |
24 | 401 | 400 | ||
25 | 401 | @defer.inlineCallbacks | ||
26 | 402 | def test_get_webclient(self): | ||
27 | 403 | """The webclient is created if it does not exist.""" | ||
28 | 404 | self.assertEqual(self.action_queue.webclient, None) | ||
29 | 405 | webclient = yield self.action_queue.get_webclient() | ||
30 | 406 | self.assertNotEqual(webclient, None) | ||
31 | 407 | |||
32 | 408 | @defer.inlineCallbacks | ||
33 | 409 | def test_get_webclient_existing(self): | ||
34 | 410 | """The webclient is not created again if it exists.""" | ||
35 | 411 | fake_wc = object() | ||
36 | 412 | self.patch(self.action_queue, "webclient", fake_wc) | ||
37 | 413 | webclient = yield self.action_queue.get_webclient() | ||
38 | 414 | self.assertEqual(webclient, fake_wc) | ||
39 | 415 | |||
40 | 402 | 416 | ||
41 | 403 | class TestLoggingStorageClient(TwistedTestCase): | 417 | class TestLoggingStorageClient(TwistedTestCase): |
42 | 404 | """Tests for ensuring magic hash dont show in logs.""" | 418 | """Tests for ensuring magic hash dont show in logs.""" |
43 | @@ -2519,67 +2533,26 @@ | |||
44 | 2519 | self.assertEqual(NODE, self.command.node_id) | 2533 | self.assertEqual(NODE, self.command.node_id) |
45 | 2520 | self.assertEqual(True, self.command.is_public) | 2534 | self.assertEqual(True, self.command.is_public) |
46 | 2521 | 2535 | ||
67 | 2522 | def test_run_defers_work_to_thread(self): | 2536 | @defer.inlineCallbacks |
48 | 2523 | """Test that work is deferred to a thread.""" | ||
49 | 2524 | original = threads.deferToThread | ||
50 | 2525 | self.called = False | ||
51 | 2526 | |||
52 | 2527 | def check(function): | ||
53 | 2528 | self.called = True | ||
54 | 2529 | self.assertEqual( | ||
55 | 2530 | self.command._change_public_access_http, function) | ||
56 | 2531 | return defer.Deferred() | ||
57 | 2532 | |||
58 | 2533 | threads.deferToThread = check | ||
59 | 2534 | try: | ||
60 | 2535 | res = self.command._run() | ||
61 | 2536 | finally: | ||
62 | 2537 | threads.deferToThread = original | ||
63 | 2538 | |||
64 | 2539 | self.assertIsInstance(res, defer.Deferred) | ||
65 | 2540 | self.assertTrue(self.called, "deferToThread was called") | ||
66 | 2541 | |||
68 | 2542 | def test_change_public_access_http(self): | 2537 | def test_change_public_access_http(self): |
74 | 2543 | """Test the blocking portion of the command.""" | 2538 | """Test the command.""" |
75 | 2544 | self.called = False | 2539 | |
76 | 2545 | def check(request): | 2540 | def check_webcall(request_iri, method=None, post_content=None): |
77 | 2546 | self.called = True | 2541 | """Check the webcall made by this command.""" |
78 | 2547 | url = 'https://one.ubuntu.com/files/api/set_public/%s:%s' % ( | 2542 | iri = u'https://one.ubuntu.com/files/api/set_public/%s:%s' % ( |
79 | 2548 | base64.urlsafe_b64encode(VOLUME.bytes).strip("="), | 2543 | base64.urlsafe_b64encode(VOLUME.bytes).strip("="), |
80 | 2549 | base64.urlsafe_b64encode(NODE.bytes).strip("=")) | 2544 | base64.urlsafe_b64encode(NODE.bytes).strip("=")) |
95 | 2550 | self.assertEqual(url, request.get_full_url()) | 2545 | self.assertEqual(iri, request_iri) |
96 | 2551 | self.assertEqual("is_public=True", request.get_data()) | 2546 | self.assertEqual("is_public=True", post_content) |
97 | 2552 | return StringIO( | 2547 | content = '{"is_public": true, "public_url": "http://example.com"}' |
98 | 2553 | '{"is_public": true, "public_url": "http://example.com"}') | 2548 | response = action_queue.txweb.Response(content) |
99 | 2554 | 2549 | return defer.succeed(response) | |
100 | 2555 | from ubuntuone.syncdaemon import action_queue | 2550 | |
101 | 2556 | self.patch(action_queue.timestamp_checker, "get_faithful_time", | 2551 | self.patch(self.action_queue, "webcall", check_webcall) |
102 | 2557 | lambda: 1) | 2552 | res = yield self.command._run() |
89 | 2558 | action_queue.urlopen = check | ||
90 | 2559 | try: | ||
91 | 2560 | res = self.command._change_public_access_http() | ||
92 | 2561 | finally: | ||
93 | 2562 | action_queue.urlopen = urllib2.urlopen | ||
94 | 2563 | |||
103 | 2564 | self.assertEqual( | 2553 | self.assertEqual( |
104 | 2565 | {'is_public': True, 'public_url': 'http://example.com'}, res) | 2554 | {'is_public': True, 'public_url': 'http://example.com'}, res) |
105 | 2566 | 2555 | ||
106 | 2567 | def test_change_public_access_http_uses_timestamp(self): | ||
107 | 2568 | """The timestamp is used for oauth signing.""" | ||
108 | 2569 | fake_timestamp = 12345678 | ||
109 | 2570 | |||
110 | 2571 | def fake_urlopen(request): | ||
111 | 2572 | """A fake urlopen.""" | ||
112 | 2573 | auth = request.headers["Authorization"] | ||
113 | 2574 | expected = 'oauth_timestamp="%d"' % fake_timestamp | ||
114 | 2575 | self.assertIn(expected, auth) | ||
115 | 2576 | return StringIO("[]") | ||
116 | 2577 | |||
117 | 2578 | self.patch(action_queue.timestamp_checker, "get_faithful_time", | ||
118 | 2579 | lambda: fake_timestamp) | ||
119 | 2580 | self.patch(action_queue, "urlopen", fake_urlopen) | ||
120 | 2581 | self.command._change_public_access_http() | ||
121 | 2582 | |||
122 | 2583 | def test_handle_success_push_event(self): | 2556 | def test_handle_success_push_event(self): |
123 | 2584 | """Test AQ_CHANGE_PUBLIC_ACCESS_OK is pushed on success.""" | 2557 | """Test AQ_CHANGE_PUBLIC_ACCESS_OK is pushed on success.""" |
124 | 2585 | response = {'is_public': True, 'public_url': 'http://example.com'} | 2558 | response = {'is_public': True, 'public_url': 'http://example.com'} |
125 | @@ -2592,8 +2565,7 @@ | |||
126 | 2592 | def test_handle_failure_push_event(self): | 2565 | def test_handle_failure_push_event(self): |
127 | 2593 | """Test AQ_CHANGE_PUBLIC_ACCESS_ERROR is pushed on failure.""" | 2566 | """Test AQ_CHANGE_PUBLIC_ACCESS_ERROR is pushed on failure.""" |
128 | 2594 | msg = 'Something went wrong' | 2567 | msg = 'Something went wrong' |
131 | 2595 | failure = Failure(urllib2.HTTPError( | 2568 | failure = Failure(action_queue.txweb.WebClientError("Misc Error", msg)) |
130 | 2596 | "http://example.com", 500, "Error", [], StringIO(msg))) | ||
132 | 2597 | self.command.handle_failure(failure=failure) | 2569 | self.command.handle_failure(failure=failure) |
133 | 2598 | event = ('AQ_CHANGE_PUBLIC_ACCESS_ERROR', | 2570 | event = ('AQ_CHANGE_PUBLIC_ACCESS_ERROR', |
134 | 2599 | {'share_id': VOLUME, 'node_id': NODE, 'error': msg}) | 2571 | {'share_id': VOLUME, 'node_id': NODE, 'error': msg}) |
135 | @@ -2620,11 +2592,11 @@ | |||
136 | 2620 | default_url = 'https://one.ubuntu.com/files/api/public_files' | 2592 | default_url = 'https://one.ubuntu.com/files/api/public_files' |
137 | 2621 | request_queue = RequestQueue(action_queue=self.action_queue) | 2593 | request_queue = RequestQueue(action_queue=self.action_queue) |
138 | 2622 | command = GetPublicFiles(request_queue) | 2594 | command = GetPublicFiles(request_queue) |
140 | 2623 | self.assertEqual(command._url, default_url) | 2595 | self.assertEqual(command._iri, default_url) |
141 | 2624 | custom_url = 'http://example.com:1234/files/api/public_files' | 2596 | custom_url = 'http://example.com:1234/files/api/public_files' |
142 | 2625 | command_2 = GetPublicFiles(request_queue, | 2597 | command_2 = GetPublicFiles(request_queue, |
145 | 2626 | base_url='http://example.com:1234') | 2598 | base_iri=u'http://example.com:1234') |
146 | 2627 | self.assertEqual(command_2._url, custom_url) | 2599 | self.assertEqual(command_2._iri, custom_url) |
147 | 2628 | 2600 | ||
148 | 2629 | def test_change_public_access(self): | 2601 | def test_change_public_access(self): |
149 | 2630 | """Test the get_public_files method..""" | 2602 | """Test the get_public_files method..""" |
150 | @@ -2634,75 +2606,39 @@ | |||
151 | 2634 | """Test proper inheritance.""" | 2606 | """Test proper inheritance.""" |
152 | 2635 | self.assertTrue(isinstance(self.command, ActionQueueCommand)) | 2607 | self.assertTrue(isinstance(self.command, ActionQueueCommand)) |
153 | 2636 | 2608 | ||
174 | 2637 | def test_run_defers_work_to_thread(self): | 2609 | @defer.inlineCallbacks |
155 | 2638 | """Test that work is deferred to a thread.""" | ||
156 | 2639 | original = threads.deferToThread | ||
157 | 2640 | self.called = False | ||
158 | 2641 | |||
159 | 2642 | def check(function): | ||
160 | 2643 | self.called = True | ||
161 | 2644 | self.assertEqual( | ||
162 | 2645 | self.command._get_public_files_http, function) | ||
163 | 2646 | return defer.Deferred() | ||
164 | 2647 | |||
165 | 2648 | threads.deferToThread = check | ||
166 | 2649 | try: | ||
167 | 2650 | res = self.command._run() | ||
168 | 2651 | finally: | ||
169 | 2652 | threads.deferToThread = original | ||
170 | 2653 | |||
171 | 2654 | self.assertIsInstance(res, defer.Deferred) | ||
172 | 2655 | self.assertTrue(self.called, "deferToThread was called") | ||
173 | 2656 | |||
175 | 2657 | def test_get_public_files_http(self): | 2610 | def test_get_public_files_http(self): |
178 | 2658 | """Test the blocking portion of the command.""" | 2611 | """Test the _run method of the command.""" |
177 | 2659 | self.called = False | ||
179 | 2660 | node_id = uuid.uuid4() | 2612 | node_id = uuid.uuid4() |
180 | 2661 | nodekey = '%s' % (base64.urlsafe_b64encode(node_id.bytes).strip("=")) | 2613 | nodekey = '%s' % (base64.urlsafe_b64encode(node_id.bytes).strip("=")) |
181 | 2662 | node_id_2 = uuid.uuid4() | 2614 | node_id_2 = uuid.uuid4() |
182 | 2663 | nodekey_2 = '%s' % (base64.urlsafe_b64encode( | 2615 | nodekey_2 = '%s' % (base64.urlsafe_b64encode( |
183 | 2664 | node_id_2.bytes).strip("=")) | 2616 | node_id_2.bytes).strip("=")) |
184 | 2665 | volume_id = uuid.uuid4() | 2617 | volume_id = uuid.uuid4() |
190 | 2666 | def check(request): | 2618 | |
191 | 2667 | self.called = True | 2619 | def check_webcall(request_iri, method=None): |
192 | 2668 | url = 'https://one.ubuntu.com/files/api/public_files' | 2620 | """Check the webcall made by this command.""" |
193 | 2669 | self.assertEqual(url, request.get_full_url()) | 2621 | """Check the webcall made by this command.""" |
194 | 2670 | return StringIO( | 2622 | iri = u'https://one.ubuntu.com/files/api/public_files' |
195 | 2623 | self.assertEqual(method.upper(), "GET") | ||
196 | 2624 | self.assertEqual(iri, request_iri) | ||
197 | 2625 | content = ( | ||
198 | 2671 | '[{"nodekey": "%s", "volume_id": null,"public_url": ' | 2626 | '[{"nodekey": "%s", "volume_id": null,"public_url": ' |
199 | 2672 | '"http://example.com"}, ' | 2627 | '"http://example.com"}, ' |
200 | 2673 | '{"nodekey": "%s", "volume_id": "%s", "public_url": ' | 2628 | '{"nodekey": "%s", "volume_id": "%s", "public_url": ' |
201 | 2674 | '"http://example.com"}]' % (nodekey, nodekey_2, volume_id)) | 2629 | '"http://example.com"}]' % (nodekey, nodekey_2, volume_id)) |
211 | 2675 | 2630 | response = action_queue.txweb.Response(content) | |
212 | 2676 | from ubuntuone.syncdaemon import action_queue | 2631 | return defer.succeed(response) |
213 | 2677 | self.patch(action_queue.timestamp_checker, "get_faithful_time", | 2632 | |
214 | 2678 | lambda: 1) | 2633 | self.patch(self.action_queue, "webcall", check_webcall) |
215 | 2679 | action_queue.urlopen = check | 2634 | res = yield self.command._run() |
216 | 2680 | try: | 2635 | |
208 | 2681 | res = self.command._get_public_files_http() | ||
209 | 2682 | finally: | ||
210 | 2683 | action_queue.urlopen = urllib2.urlopen | ||
217 | 2684 | self.assertEqual([{'node_id': str(node_id), 'volume_id': '', | 2636 | self.assertEqual([{'node_id': str(node_id), 'volume_id': '', |
218 | 2685 | 'public_url': 'http://example.com'}, | 2637 | 'public_url': 'http://example.com'}, |
219 | 2686 | {'node_id': str(node_id_2), | 2638 | {'node_id': str(node_id_2), |
220 | 2687 | 'volume_id': str(volume_id), | 2639 | 'volume_id': str(volume_id), |
221 | 2688 | 'public_url': 'http://example.com'}], res) | 2640 | 'public_url': 'http://example.com'}], res) |
222 | 2689 | 2641 | ||
223 | 2690 | def test_get_public_files_http_uses_timestamp(self): | ||
224 | 2691 | """The timestamp is used for oauth signing.""" | ||
225 | 2692 | fake_timestamp = 12345678 | ||
226 | 2693 | |||
227 | 2694 | def fake_urlopen(request): | ||
228 | 2695 | """A fake urlopen.""" | ||
229 | 2696 | auth = request.headers["Authorization"] | ||
230 | 2697 | expected = 'oauth_timestamp="%d"' % fake_timestamp | ||
231 | 2698 | self.assertIn(expected, auth) | ||
232 | 2699 | return StringIO("[]") | ||
233 | 2700 | |||
234 | 2701 | self.patch(action_queue.timestamp_checker, "get_faithful_time", | ||
235 | 2702 | lambda: fake_timestamp) | ||
236 | 2703 | self.patch(action_queue, "urlopen", fake_urlopen) | ||
237 | 2704 | self.command._get_public_files_http() | ||
238 | 2705 | |||
239 | 2706 | def test_handle_success_push_event(self): | 2642 | def test_handle_success_push_event(self): |
240 | 2707 | """Test AQ_PUBLIC_FILES_LIST_OK is pushed on success.""" | 2643 | """Test AQ_PUBLIC_FILES_LIST_OK is pushed on success.""" |
241 | 2708 | response = [{'node_id': uuid.uuid4(), 'volume_id':None, | 2644 | response = [{'node_id': uuid.uuid4(), 'volume_id':None, |
242 | @@ -2714,8 +2650,7 @@ | |||
243 | 2714 | def test_handle_failure_push_event(self): | 2650 | def test_handle_failure_push_event(self): |
244 | 2715 | """Test AQ_PUBLIC_FILES_LIST_ERROR is pushed on failure.""" | 2651 | """Test AQ_PUBLIC_FILES_LIST_ERROR is pushed on failure.""" |
245 | 2716 | msg = 'Something went wrong' | 2652 | msg = 'Something went wrong' |
248 | 2717 | failure = Failure(urllib2.HTTPError( | 2653 | failure = Failure(action_queue.txweb.WebClientError("Misc Error", msg)) |
247 | 2718 | "http://example.com", 500, "Error", [], StringIO(msg))) | ||
249 | 2719 | self.command.handle_failure(failure=failure) | 2654 | self.command.handle_failure(failure=failure) |
250 | 2720 | event = ('AQ_PUBLIC_FILES_LIST_ERROR', {'error': msg}) | 2655 | event = ('AQ_PUBLIC_FILES_LIST_ERROR', {'error': msg}) |
251 | 2721 | self.assertIn(event, self.command.action_queue.event_queue.events) | 2656 | self.assertIn(event, self.command.action_queue.event_queue.events) |
252 | @@ -3700,27 +3635,6 @@ | |||
253 | 3700 | self.assertEqual('share_name', name) | 3635 | self.assertEqual('share_name', name) |
254 | 3701 | self.assertTrue(read_only) | 3636 | self.assertTrue(read_only) |
255 | 3702 | 3637 | ||
256 | 3703 | @defer.inlineCallbacks | ||
257 | 3704 | def test_create_share_http_uses_timestamp(self): | ||
258 | 3705 | """The timestamp is used for oauth signing.""" | ||
259 | 3706 | fake_timestamp = 12345678 | ||
260 | 3707 | |||
261 | 3708 | def fake_urlopen(request): | ||
262 | 3709 | """A fake urlopen.""" | ||
263 | 3710 | auth = request.headers["Authorization"] | ||
264 | 3711 | expected = 'oauth_timestamp="%d"' % fake_timestamp | ||
265 | 3712 | self.assertIn(expected, auth) | ||
266 | 3713 | |||
267 | 3714 | self.patch(action_queue.timestamp_checker, "get_faithful_time", | ||
268 | 3715 | lambda: fake_timestamp) | ||
269 | 3716 | self.patch(action_queue, "urlopen", fake_urlopen) | ||
270 | 3717 | self.user_connect() | ||
271 | 3718 | command = CreateShare(self.request_queue, 'node_id', | ||
272 | 3719 | 'share_to@example.com', 'share_name', | ||
273 | 3720 | ACCESS_LEVEL_RO, 'marker', 'path') | ||
274 | 3721 | self.assertTrue(command.use_http, 'CreateShare should be in http mode') | ||
275 | 3722 | yield command._run() | ||
276 | 3723 | |||
277 | 3724 | def test_possible_markers(self): | 3638 | def test_possible_markers(self): |
278 | 3725 | """Test that it returns the correct values.""" | 3639 | """Test that it returns the correct values.""" |
279 | 3726 | cmd = CreateShare(self.request_queue, 'node_id', 'shareto@example.com', | 3640 | cmd = CreateShare(self.request_queue, 'node_id', 'shareto@example.com', |
280 | 3727 | 3641 | ||
281 | === modified file 'ubuntuone/syncdaemon/action_queue.py' | |||
282 | --- ubuntuone/syncdaemon/action_queue.py 2012-03-10 00:39:26 +0000 | |||
283 | +++ ubuntuone/syncdaemon/action_queue.py 2012-03-13 03:12:18 +0000 | |||
284 | @@ -31,19 +31,18 @@ | |||
285 | 31 | from collections import deque, defaultdict | 31 | from collections import deque, defaultdict |
286 | 32 | from functools import partial | 32 | from functools import partial |
287 | 33 | from urllib import urlencode | 33 | from urllib import urlencode |
288 | 34 | from urllib2 import urlopen, Request, HTTPError | ||
289 | 35 | from urlparse import urljoin | 34 | from urlparse import urljoin |
290 | 36 | 35 | ||
291 | 37 | import OpenSSL.SSL | 36 | import OpenSSL.SSL |
292 | 38 | 37 | ||
293 | 39 | from zope.interface import implements | 38 | from zope.interface import implements |
295 | 40 | from twisted.internet import reactor, defer, threads, task | 39 | from twisted.internet import reactor, defer, task |
296 | 41 | from twisted.internet import error as twisted_errors | 40 | from twisted.internet import error as twisted_errors |
297 | 42 | from twisted.names import client as dns_client | 41 | from twisted.names import client as dns_client |
298 | 43 | from twisted.python.failure import Failure, DefaultException | 42 | from twisted.python.failure import Failure, DefaultException |
299 | 44 | 43 | ||
300 | 45 | from oauth import oauth | 44 | from oauth import oauth |
302 | 46 | from ubuntu_sso.utils import timestamp_checker | 45 | from ubuntu_sso.utils.webclient import txweb |
303 | 47 | from ubuntuone import clientdefs | 46 | from ubuntuone import clientdefs |
304 | 48 | from ubuntuone.platform import platform, remove_file | 47 | from ubuntuone.platform import platform, remove_file |
305 | 49 | from ubuntuone.storageprotocol import protocol_pb2, content_hash | 48 | from ubuntuone.storageprotocol import protocol_pb2, content_hash |
306 | @@ -685,6 +684,8 @@ | |||
307 | 685 | # credentials | 684 | # credentials |
308 | 686 | self.token = None | 685 | self.token = None |
309 | 687 | self.consumer = None | 686 | self.consumer = None |
310 | 687 | self.credentials = None | ||
311 | 688 | self.webclient = None | ||
312 | 688 | 689 | ||
313 | 689 | self.client = None # an instance of self.protocol | 690 | self.client = None # an instance of self.protocol |
314 | 690 | 691 | ||
315 | @@ -730,6 +731,7 @@ | |||
316 | 730 | 731 | ||
317 | 731 | def handle_SYS_USER_CONNECT(self, access_token): | 732 | def handle_SYS_USER_CONNECT(self, access_token): |
318 | 732 | """Stow the access token away for later use.""" | 733 | """Stow the access token away for later use.""" |
319 | 734 | self.credentials = access_token | ||
320 | 733 | self.token = oauth.OAuthToken(access_token['token'], | 735 | self.token = oauth.OAuthToken(access_token['token'], |
321 | 734 | access_token['token_secret']) | 736 | access_token['token_secret']) |
322 | 735 | self.consumer = oauth.OAuthConsumer(access_token['consumer_key'], | 737 | self.consumer = oauth.OAuthConsumer(access_token['consumer_key'], |
323 | @@ -818,6 +820,25 @@ | |||
324 | 818 | else: | 820 | else: |
325 | 819 | return defer.succeed((self.host, self.port)) | 821 | return defer.succeed((self.host, self.port)) |
326 | 820 | 822 | ||
327 | 823 | |||
328 | 824 | @defer.inlineCallbacks | ||
329 | 825 | def webcall(self, iri, **kwargs): | ||
330 | 826 | """Perform a web call to the api servers.""" | ||
331 | 827 | webclient = yield self.get_webclient() | ||
332 | 828 | response = yield webclient.request(iri, | ||
333 | 829 | oauth_credentials=self.credentials, **kwargs) | ||
334 | 830 | defer.returnValue(response) | ||
335 | 831 | |||
336 | 832 | @defer.inlineCallbacks | ||
337 | 833 | def get_webclient(self): | ||
338 | 834 | """Get the webclient, creating it if needed.""" | ||
339 | 835 | if self.webclient is None: | ||
340 | 836 | client = yield self.tunnel_runner.get_client() | ||
341 | 837 | self.webclient = txweb.WebClient(connector=client, | ||
342 | 838 | appname="Ubuntu One", | ||
343 | 839 | oauth_sign_plain=True) | ||
344 | 840 | defer.returnValue(self.webclient) | ||
345 | 841 | |||
346 | 821 | @defer.inlineCallbacks | 842 | @defer.inlineCallbacks |
347 | 822 | def _make_connection(self, result): | 843 | def _make_connection(self, result): |
348 | 823 | """Do the real connect call.""" | 844 | """Do the real connect call.""" |
349 | @@ -1778,36 +1799,23 @@ | |||
350 | 1778 | if share_to and re.match(EREGEX, share_to): | 1799 | if share_to and re.match(EREGEX, share_to): |
351 | 1779 | self.use_http = True | 1800 | self.use_http = True |
352 | 1780 | 1801 | ||
353 | 1802 | @defer.inlineCallbacks | ||
354 | 1781 | def _create_share_http(self, node_id, user, name, read_only): | 1803 | def _create_share_http(self, node_id, user, name, read_only): |
355 | 1782 | """Create a share using the HTTP Web API method.""" | 1804 | """Create a share using the HTTP Web API method.""" |
356 | 1783 | 1805 | ||
369 | 1784 | url = "https://one.ubuntu.com/files/api/offer_share/" | 1806 | iri = u"https://one.ubuntu.com/files/api/offer_share/" |
358 | 1785 | method = oauth.OAuthSignatureMethod_PLAINTEXT() | ||
359 | 1786 | timestamp = timestamp_checker.get_faithful_time() | ||
360 | 1787 | parameters = {"oauth_timestamp": timestamp} | ||
361 | 1788 | request = oauth.OAuthRequest.from_consumer_and_token( | ||
362 | 1789 | http_url=url, | ||
363 | 1790 | http_method="POST", | ||
364 | 1791 | parameters=parameters, | ||
365 | 1792 | oauth_consumer=self.action_queue.consumer, | ||
366 | 1793 | token=self.action_queue.token) | ||
367 | 1794 | request.sign_request(method, self.action_queue.consumer, | ||
368 | 1795 | self.action_queue.token) | ||
370 | 1796 | data = dict(offer_to_email=user, | 1807 | data = dict(offer_to_email=user, |
371 | 1797 | read_only=read_only, | 1808 | read_only=read_only, |
372 | 1798 | node_id=node_id, | 1809 | node_id=node_id, |
373 | 1799 | share_name=name) | 1810 | share_name=name) |
374 | 1800 | pdata = urlencode(data) | 1811 | pdata = urlencode(data) |
378 | 1801 | headers = request.to_header() | 1812 | yield self.action_queue.webcall(iri, method="POST", post_content=pdata) |
376 | 1802 | req = Request(url, pdata, headers) | ||
377 | 1803 | urlopen(req) | ||
379 | 1804 | 1813 | ||
380 | 1805 | def _run(self): | 1814 | def _run(self): |
381 | 1806 | """Do the actual running.""" | 1815 | """Do the actual running.""" |
382 | 1807 | if self.use_http: | 1816 | if self.use_http: |
383 | 1808 | # External user, do the HTTP REST method | 1817 | # External user, do the HTTP REST method |
386 | 1809 | return threads.deferToThread(self._create_share_http, | 1818 | return self._create_share_http(self.node_id, self.share_to, |
385 | 1810 | self.node_id, self.share_to, | ||
387 | 1811 | self.name, | 1819 | self.name, |
388 | 1812 | self.access_level != ACCESS_LEVEL_RW) | 1820 | self.access_level != ACCESS_LEVEL_RW) |
389 | 1813 | else: | 1821 | else: |
390 | @@ -1831,7 +1839,7 @@ | |||
391 | 1831 | """It didn't work! Push the event.""" | 1839 | """It didn't work! Push the event.""" |
392 | 1832 | self.action_queue.event_queue.push('AQ_CREATE_SHARE_ERROR', | 1840 | self.action_queue.event_queue.push('AQ_CREATE_SHARE_ERROR', |
393 | 1833 | marker=self.marker, | 1841 | marker=self.marker, |
395 | 1834 | error=failure.getErrorMessage()) | 1842 | error=failure.value[1]) |
396 | 1835 | 1843 | ||
397 | 1836 | def _acquire_pathlock(self): | 1844 | def _acquire_pathlock(self): |
398 | 1837 | """Acquire pathlock.""" | 1845 | """Acquire pathlock.""" |
399 | @@ -2113,6 +2121,7 @@ | |||
400 | 2113 | self.node_id = node_id | 2121 | self.node_id = node_id |
401 | 2114 | self.is_public = is_public | 2122 | self.is_public = is_public |
402 | 2115 | 2123 | ||
403 | 2124 | @defer.inlineCallbacks | ||
404 | 2116 | def _change_public_access_http(self): | 2125 | def _change_public_access_http(self): |
405 | 2117 | """Change public access using the HTTP Web API method.""" | 2126 | """Change public access using the HTTP Web API method.""" |
406 | 2118 | 2127 | ||
407 | @@ -2123,28 +2132,16 @@ | |||
408 | 2123 | base64.urlsafe_b64encode(self.share_id.bytes).strip("="), | 2132 | base64.urlsafe_b64encode(self.share_id.bytes).strip("="), |
409 | 2124 | node_key) | 2133 | node_key) |
410 | 2125 | 2134 | ||
423 | 2126 | url = "https://one.ubuntu.com/files/api/set_public/%s" % (node_key,) | 2135 | iri = u"https://one.ubuntu.com/files/api/set_public/%s" % (node_key,) |
412 | 2127 | method = oauth.OAuthSignatureMethod_PLAINTEXT() | ||
413 | 2128 | timestamp = timestamp_checker.get_faithful_time() | ||
414 | 2129 | parameters = {"oauth_timestamp": timestamp} | ||
415 | 2130 | request = oauth.OAuthRequest.from_consumer_and_token( | ||
416 | 2131 | http_url=url, | ||
417 | 2132 | http_method="POST", | ||
418 | 2133 | parameters=parameters, | ||
419 | 2134 | oauth_consumer=self.action_queue.consumer, | ||
420 | 2135 | token=self.action_queue.token) | ||
421 | 2136 | request.sign_request(method, self.action_queue.consumer, | ||
422 | 2137 | self.action_queue.token) | ||
424 | 2138 | data = dict(is_public=bool(self.is_public)) | 2136 | data = dict(is_public=bool(self.is_public)) |
425 | 2139 | pdata = urlencode(data) | 2137 | pdata = urlencode(data) |
430 | 2140 | headers = request.to_header() | 2138 | response = yield self.action_queue.webcall(iri, method="POST", |
431 | 2141 | req = Request(url, pdata, headers) | 2139 | post_content=pdata) |
432 | 2142 | response = urlopen(req) | 2140 | defer.returnValue(simplejson.loads(response.content)) |
429 | 2143 | return simplejson.load(response) | ||
433 | 2144 | 2141 | ||
434 | 2145 | def _run(self): | 2142 | def _run(self): |
435 | 2146 | """See ActionQueueCommand.""" | 2143 | """See ActionQueueCommand.""" |
437 | 2147 | return threads.deferToThread(self._change_public_access_http) | 2144 | return self._change_public_access_http() |
438 | 2148 | 2145 | ||
439 | 2149 | def handle_success(self, success): | 2146 | def handle_success(self, success): |
440 | 2150 | """See ActionQueueCommand.""" | 2147 | """See ActionQueueCommand.""" |
441 | @@ -2156,51 +2153,36 @@ | |||
442 | 2156 | 2153 | ||
443 | 2157 | def handle_failure(self, failure): | 2154 | def handle_failure(self, failure): |
444 | 2158 | """It didn't work! Push the event.""" | 2155 | """It didn't work! Push the event.""" |
445 | 2159 | if issubclass(failure.type, HTTPError): | ||
446 | 2160 | message = failure.value.read() | ||
447 | 2161 | else: | ||
448 | 2162 | message = failure.getErrorMessage() | ||
449 | 2163 | self.action_queue.event_queue.push('AQ_CHANGE_PUBLIC_ACCESS_ERROR', | 2156 | self.action_queue.event_queue.push('AQ_CHANGE_PUBLIC_ACCESS_ERROR', |
450 | 2164 | share_id=self.share_id, | 2157 | share_id=self.share_id, |
451 | 2165 | node_id=self.node_id, | 2158 | node_id=self.node_id, |
453 | 2166 | error=message) | 2159 | error=failure.value[1]) |
454 | 2167 | 2160 | ||
455 | 2168 | 2161 | ||
456 | 2169 | class GetPublicFiles(ActionQueueCommand): | 2162 | class GetPublicFiles(ActionQueueCommand): |
457 | 2170 | """Get the list of public files.""" | 2163 | """Get the list of public files.""" |
458 | 2171 | 2164 | ||
460 | 2172 | __slots__ = ('_url',) | 2165 | __slots__ = ('_iri',) |
461 | 2173 | logged_attrs = ActionQueueCommand.logged_attrs + __slots__ | 2166 | logged_attrs = ActionQueueCommand.logged_attrs + __slots__ |
462 | 2174 | 2167 | ||
464 | 2175 | def __init__(self, request_queue, base_url='https://one.ubuntu.com'): | 2168 | def __init__(self, request_queue, base_iri=u'https://one.ubuntu.com'): |
465 | 2176 | super(GetPublicFiles, self).__init__(request_queue) | 2169 | super(GetPublicFiles, self).__init__(request_queue) |
467 | 2177 | self._url = urljoin(base_url, 'files/api/public_files') | 2170 | self._iri = urljoin(base_iri, u'files/api/public_files') |
468 | 2178 | 2171 | ||
469 | 2172 | @defer.inlineCallbacks | ||
470 | 2179 | def _get_public_files_http(self): | 2173 | def _get_public_files_http(self): |
471 | 2180 | """Get public files list using the HTTP Web API method.""" | 2174 | """Get public files list using the HTTP Web API method.""" |
472 | 2181 | 2175 | ||
488 | 2182 | method = oauth.OAuthSignatureMethod_PLAINTEXT() | 2176 | response = yield self.action_queue.webcall(self._iri, method="GET") |
489 | 2183 | timestamp = timestamp_checker.get_faithful_time() | 2177 | |
490 | 2184 | parameters = {"oauth_timestamp": timestamp} | 2178 | files = simplejson.loads(response.content) |
476 | 2185 | request = oauth.OAuthRequest.from_consumer_and_token( | ||
477 | 2186 | http_url=self._url, | ||
478 | 2187 | http_method="GET", | ||
479 | 2188 | parameters=parameters, | ||
480 | 2189 | oauth_consumer=self.action_queue.consumer, | ||
481 | 2190 | token=self.action_queue.token) | ||
482 | 2191 | request.sign_request(method, self.action_queue.consumer, | ||
483 | 2192 | self.action_queue.token) | ||
484 | 2193 | headers = request.to_header() | ||
485 | 2194 | req = Request(self._url, headers=headers) | ||
486 | 2195 | response = urlopen(req) | ||
487 | 2196 | files = simplejson.load(response) | ||
491 | 2197 | # translate nodekeys to (volume_id, node_id) | 2179 | # translate nodekeys to (volume_id, node_id) |
492 | 2198 | for pf in files: | 2180 | for pf in files: |
493 | 2199 | _, node_id = self.split_nodekey(pf.pop('nodekey')) | 2181 | _, node_id = self.split_nodekey(pf.pop('nodekey')) |
494 | 2200 | volume_id = pf['volume_id'] | 2182 | volume_id = pf['volume_id'] |
495 | 2201 | pf['volume_id'] = '' if volume_id is None else volume_id | 2183 | pf['volume_id'] = '' if volume_id is None else volume_id |
496 | 2202 | pf['node_id'] = node_id | 2184 | pf['node_id'] = node_id |
498 | 2203 | return files | 2185 | defer.returnValue(files) |
499 | 2204 | 2186 | ||
500 | 2205 | @property | 2187 | @property |
501 | 2206 | def uniqueness(self): | 2188 | def uniqueness(self): |
502 | @@ -2213,7 +2195,7 @@ | |||
503 | 2213 | 2195 | ||
504 | 2214 | def _run(self): | 2196 | def _run(self): |
505 | 2215 | """See ActionQueueCommand.""" | 2197 | """See ActionQueueCommand.""" |
507 | 2216 | return threads.deferToThread(self._get_public_files_http) | 2198 | return self._get_public_files_http() |
508 | 2217 | 2199 | ||
509 | 2218 | def handle_success(self, success): | 2200 | def handle_success(self, success): |
510 | 2219 | """See ActionQueueCommand.""" | 2201 | """See ActionQueueCommand.""" |
511 | @@ -2222,12 +2204,8 @@ | |||
512 | 2222 | 2204 | ||
513 | 2223 | def handle_failure(self, failure): | 2205 | def handle_failure(self, failure): |
514 | 2224 | """It didn't work! Push the event.""" | 2206 | """It didn't work! Push the event.""" |
515 | 2225 | if issubclass(failure.type, HTTPError): | ||
516 | 2226 | message = failure.value.read() | ||
517 | 2227 | else: | ||
518 | 2228 | message = failure.getErrorMessage() | ||
519 | 2229 | self.action_queue.event_queue.push('AQ_PUBLIC_FILES_LIST_ERROR', | 2207 | self.action_queue.event_queue.push('AQ_PUBLIC_FILES_LIST_ERROR', |
521 | 2230 | error=message) | 2208 | error=failure.value[1]) |
522 | 2231 | 2209 | ||
523 | 2232 | def split_nodekey(self, nodekey): | 2210 | def split_nodekey(self, nodekey): |
524 | 2233 | """Split a node key into a share_id, node_id.""" | 2211 | """Split a node key into a share_id, node_id.""" |
*** NOTE ***
This branch depends on this SSO branch: lp:~alecu/ubuntu-sso-client/updated-txweb