Merge lp:~facundo/ubuntuone-storage-protocol/simple_auth into lp:ubuntuone-storage-protocol

Proposed by Facundo Batista
Status: Superseded
Proposed branch: lp:~facundo/ubuntuone-storage-protocol/simple_auth
Merge into: lp:ubuntuone-storage-protocol
Diff against target: 402 lines (+40/-187)
4 files modified
tests/test_client.py (+6/-121)
tests/test_context.py (+7/-2)
ubuntuone/storageprotocol/client.py (+17/-60)
ubuntuone/storageprotocol/context.py (+10/-4)
To merge this branch: bzr merge lp:~facundo/ubuntuone-storage-protocol/simple_auth
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Review via email: mp+259407@code.launchpad.net

This proposal has been superseded by a proposal from 2015-05-20.

Commit message

Use simple authentication against the server.

Description of the change

Use simple authentication against the server.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) :
review: Approve

Unmerged revisions

165. By Facundo Batista

Use simple authentication against the server.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/test_client.py'
2--- tests/test_client.py 2013-02-06 22:06:03 +0000
3+++ tests/test_client.py 2015-05-18 17:24:40 +0000
4@@ -1,9 +1,6 @@
5 # -*- coding: utf-8 -*-
6 #
7-# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
8-# Author: Alejandro J. Cura <alecu@canonical.com>
9-#
10-# Copyright (C) 2009-2012 Canonical Ltd.
11+# Copyright (C) 2009-2015 Canonical Ltd.
12 #
13 # This program is free software: you can redistribute it and/or modify it
14 # under the terms of the GNU Affero General Public License version 3,
15@@ -34,15 +31,12 @@
16 import StringIO
17 import os
18 import sys
19-import time
20 import unittest
21 import uuid
22
23-from urlparse import urlparse, parse_qsl
24-
25 from twisted.application import internet, service
26 from twisted.internet import defer
27-from twisted.internet.defer import Deferred, inlineCallbacks
28+from twisted.internet.defer import Deferred
29 from twisted.trial.unittest import TestCase as TwistedTestCase
30 from twisted.web import server, resource
31
32@@ -50,12 +44,10 @@
33 from ubuntuone.storageprotocol.client import (
34 StorageClient, CreateUDF, ListVolumes, DeleteVolume, GetDelta, Unlink,
35 Authenticate, MakeFile, MakeDir, PutContent, Move, BytesMessageProducer,
36- TwistedTimestampChecker, tx_timestamp_checker,
37 )
38
39 from ubuntuone.storageprotocol import volumes
40 from tests import test_delta_info
41-from oauthlib.oauth1 import Client
42
43 # let's not get picky about aatributes outside __init__ in tests
44 # pylint: disable=W0201
45@@ -797,46 +789,6 @@
46 self.assertEqual(len(msg.auth_parameters), 1)
47 self.assertEqual(len(msg.metadata), 0)
48
49- def test_oauth_authenticate_uses_server_timestamp(self):
50- """The oauth authentication uses the server timestamp."""
51- timestamps = []
52-
53- original_sign = Client.sign
54-
55- def fake_sign(self, *args, **kwargs):
56- """A fake Client.sign()."""
57- url, headers, body = original_sign(self, *args, **kwargs)
58- timestamps.extend(
59- value for name, value in parse_qsl(urlparse(url).query)
60- if name == 'oauth_timestamp')
61- return url, headers, body
62-
63- fake_timestamp = '801'
64- timestamp_d = Deferred()
65- self.patch(tx_timestamp_checker, "get_faithful_time",
66- lambda: timestamp_d)
67-
68- self.patch(Client, 'sign', fake_sign)
69- protocol = FakedProtocol()
70- # For backward compatibility of the API, oauth_authenticate() takes a
71- # consumer object and a token object. Both of those must have .key
72- # and .secret attributes. In modern OAuth1 (i.e. RFC 5849)
73- # terminology, the consumer is really the OAuth client, and the token
74- # is really the OAuth resource owner.
75-
76- class OAuthClient:
77- key = 'consumer_key'
78- secret = 'consumer_secret'
79-
80- class OAuthResourceOwner:
81- key = 'token'
82- secret = 'token_secret'
83- protocol.oauth_authenticate(OAuthResourceOwner, OAuthClient)
84- self.assertEqual(len(timestamps), 0)
85- timestamp_d.callback(fake_timestamp)
86- self.assertEqual(len(timestamps), 1)
87- self.assertEqual(timestamps[0], fake_timestamp)
88-
89
90 class RootResource(resource.Resource):
91 """A root resource that logs the number of calls."""
92@@ -878,73 +830,6 @@
93 self.service_collection.stopService()
94
95
96-class TimestampCheckerTestCase(TwistedTestCase):
97- """Tests for the timestamp checker."""
98-
99- @defer.inlineCallbacks
100- def setUp(self):
101- yield super(TimestampCheckerTestCase, self).setUp()
102- self.ws = MockWebServer()
103- self.addCleanup(self.ws.stop)
104- self.patch(TwistedTimestampChecker, "SERVER_URL", self.ws.get_url())
105-
106- @inlineCallbacks
107- def test_returned_value_is_int(self):
108- """The returned value is an integer."""
109- checker = TwistedTimestampChecker()
110- t = yield checker.get_faithful_time()
111- self.assertEqual(type(t), int)
112-
113- @inlineCallbacks
114- def test_first_call_does_head(self):
115- """The first call gets the clock from our web."""
116- checker = TwistedTimestampChecker()
117- yield checker.get_faithful_time()
118- self.assertEqual(self.ws.root.count, 1)
119-
120- @inlineCallbacks
121- def test_second_call_is_cached(self):
122- """For the second call, the time is cached."""
123- checker = TwistedTimestampChecker()
124- yield checker.get_faithful_time()
125- yield checker.get_faithful_time()
126- self.assertEqual(self.ws.root.count, 1)
127-
128- @inlineCallbacks
129- def test_after_timeout_cache_expires(self):
130- """After some time, the cache expires."""
131- fake_timestamp = 1
132- self.patch(time, "time", lambda: fake_timestamp)
133- checker = TwistedTimestampChecker()
134- yield checker.get_faithful_time()
135- fake_timestamp += TwistedTimestampChecker.CHECKING_INTERVAL
136- yield checker.get_faithful_time()
137- self.assertEqual(self.ws.root.count, 2)
138-
139- @inlineCallbacks
140- def test_server_error_means_skew_not_updated(self):
141- """When server can't be reached, the skew is not updated."""
142- fake_timestamp = 1
143- self.patch(time, "time", lambda: fake_timestamp)
144- checker = TwistedTimestampChecker()
145- failing_get_server_time = lambda: defer.fail(FakedError())
146- self.patch(checker, "get_server_time", failing_get_server_time)
147- yield checker.get_faithful_time()
148- self.assertEqual(checker.skew, 0)
149- self.assertEqual(checker.next_check,
150- fake_timestamp + TwistedTimestampChecker.ERROR_INTERVAL)
151-
152- @inlineCallbacks
153- def test_server_date_sends_nocache_headers(self):
154- """Getting the server date sends the no-cache headers."""
155- checker = TwistedTimestampChecker()
156- yield checker.get_server_date_header(self.ws.get_url())
157- assert len(self.ws.root.request_headers) == 1
158- headers = self.ws.root.request_headers[0]
159- result = headers.getRawHeaders("Cache-Control")
160- self.assertEqual(result, ["no-cache"])
161-
162-
163 class TestGenerationInRequests(RequestTestCase):
164 """Base class for testing that actions that change the volume will
165 have a new_generation attribute set."""
166@@ -992,8 +877,8 @@
167
168 def build_request(self):
169 """Creates the request object."""
170- return self.request_class(FakedProtocol(), None, None, None, None,
171- None, None, None, None)
172+ return self.request_class(
173+ FakedProtocol(), None, None, None, None, None, None, None, None)
174
175 def build_message(self):
176 """Creates the ending message for the request."""
177@@ -1040,8 +925,8 @@
178 def test_bytesmessageproducer_maxpayloadsize(self):
179 """The producer uses the payload size from the request."""
180 # set up the PutContent
181- pc = self.request_class(FakedProtocol(), None, None, None, None,
182- None, None, None, None)
183+ pc = self.request_class(
184+ FakedProtocol(), None, None, None, None, None, None, None, None)
185 assert 12345 != pc.max_payload_size
186 pc.max_payload_size = 12345
187
188
189=== modified file 'tests/test_context.py'
190--- tests/test_context.py 2014-04-14 22:21:04 +0000
191+++ tests/test_context.py 2015-05-18 17:24:40 +0000
192@@ -1,6 +1,6 @@
193 # tests.test_context - test ssl context creation
194 #
195-# Copyright 2012 Canonical Ltd.
196+# Copyright 2012-2015 Canonical Ltd.
197 #
198 # This program is free software: you can redistribute it and/or modify it
199 # under the terms of the GNU Affero General Public License version 3,
200@@ -35,7 +35,12 @@
201 from twisted.web import client, resource, server
202
203 from ubuntuone.storageprotocol import context
204-from ubuntu_sso.utils import get_cert_dir
205+
206+try:
207+ from ubuntu_sso.utils import get_cert_dir
208+except ImportError:
209+ # old system, let's support Precise
210+ get_cert_dir = lambda: '/etc/ssl/certs'
211
212
213 class FakeCerts(object):
214
215=== modified file 'ubuntuone/storageprotocol/client.py'
216--- ubuntuone/storageprotocol/client.py 2013-02-25 15:21:30 +0000
217+++ ubuntuone/storageprotocol/client.py 2015-05-18 17:24:40 +0000
218@@ -6,7 +6,7 @@
219 # Author: Facundo Batista <facundo@canonical.com>
220 # Author: Alejandro J. Cura <alecu@canonical.com>
221 #
222-# Copyright 2009-2012 Canonical Ltd.
223+# Copyright 2009-2015 Canonical Ltd.
224 #
225 # This program is free software: you can redistribute it and/or modify it
226 # under the terms of the GNU Affero General Public License version 3,
227@@ -39,42 +39,22 @@
228
229 from functools import partial
230 from itertools import chain
231-from urlparse import urlparse, parse_qsl
232-from oauthlib.oauth1 import Client, SIGNATURE_PLAINTEXT, SIGNATURE_TYPE_QUERY
233
234 from twisted.internet.protocol import ClientFactory
235 from twisted.internet import reactor, defer
236 from twisted.python import log
237-from twisted.web import client
238-from twisted.web.http_headers import Headers
239
240 from ubuntuone.storageprotocol import (
241 delta,
242 protocol_pb2,
243 request,
244 sharersp,
245- utils,
246 volumes,
247 )
248
249 log_debug = partial(log.msg, loglevel=logging.DEBUG)
250
251
252-class TwistedTimestampChecker(utils.BaseTimestampChecker):
253- """A timestamp that's regularly checked with a server."""
254-
255- @defer.inlineCallbacks
256- def get_server_date_header(self, server_url):
257- """Get the server date using twisted webclient."""
258- agent = client.Agent(reactor)
259- headers = Headers({'Cache-Control': ['no-cache']})
260- response = yield agent.request("HEAD", server_url, headers)
261- defer.returnValue(response.headers.getRawHeaders("Date")[0])
262-
263-
264-tx_timestamp_checker = TwistedTimestampChecker()
265-
266-
267 class StorageClient(request.RequestHandler):
268 """A Basic Storage Protocol client."""
269
270@@ -140,38 +120,15 @@
271 p.start()
272 return p.deferred
273
274- @defer.inlineCallbacks
275- def oauth_authenticate(self, consumer, token, metadata=None):
276- """Authenticate to a server using the OAuth provider.
277-
278- @param consumer: the OAuth consumer (a.k.a. client in RFC 5849) to
279- authenticate with.
280- @type consumer: object having both a `.key` and `.secret` attribute.
281- @param token: a previously acquired OAuth access token (a.k.a.
282- resource owner in RFC 5849).
283- @type token: object having both a `.key` and `.secret` attribute.
284- @param metadata: key/values to send as metadata
285-
286- Return a deferred that will get called with the request
287- object when completed.
288-
289- """
290- timestamp = yield tx_timestamp_checker.get_faithful_time()
291- client = Client(consumer.key, consumer.secret,
292- token.key, token.secret,
293- signature_method=SIGNATURE_PLAINTEXT,
294- signature_type=SIGNATURE_TYPE_QUERY,
295- timestamp=timestamp)
296- url, headers, body = client.sign('http://server')
297- # Parse out the authentication parameters from the query string.
298- auth_parameters = dict(
299- (name, value) for name, value in
300- parse_qsl(urlparse(url).query)
301- if name.startswith('oauth_'))
302+ def simple_authenticate(self, username, password, metadata=None):
303+ """Authenticate to a server using the username and password."""
304+ auth_parameters = {
305+ 'username': username,
306+ 'password': password,
307+ }
308 p = Authenticate(self, auth_parameters, metadata=metadata)
309 p.start()
310- result = yield p.deferred
311- defer.returnValue(result)
312+ return p.deferred
313
314 def handle_ROOT(self, message):
315 """Handle incoming ROOT message.
316@@ -1756,10 +1713,10 @@
317 def checkReadBandwidth(self):
318 """Check if we've passed bandwidth limits."""
319 limit_check = self.valid_limit(self.readLimit) and \
320- self.readLimit is not None and \
321- self.readThisSecond >= self.readLimit
322+ self.readLimit is not None and \
323+ self.readThisSecond >= self.readLimit
324 should_check = self.throttling_enabled and limit_check and \
325- self.unthrottleReadsID is None
326+ self.unthrottleReadsID is None
327 if should_check:
328 self.throttleReads()
329 throttle_time = self._get_throttle_time(self.readThisSecond,
330@@ -1771,14 +1728,14 @@
331 def checkWriteBandwidth(self):
332 """Check if we've passed bandwidth limits."""
333 limit_check = self.valid_limit(self.writeLimit) and \
334- self.writeLimit is not None and \
335- self.writtenThisSecond >= self.writeLimit
336+ self.writeLimit is not None and \
337+ self.writtenThisSecond >= self.writeLimit
338 should_check = self.throttling_enabled and limit_check and \
339- self.unthrottleWritesID is None
340+ self.unthrottleWritesID is None
341 if should_check:
342 self.throttleWrites()
343 throttle_time = self._get_throttle_time(self.writtenThisSecond,
344- self.writeLimit)
345+ self.writeLimit)
346 log_debug("pause writes for: %s", str(throttle_time))
347 self.unthrottleWritesID = self.maybeCallLater(
348 'unthrottleWritesID', throttle_time, self.unthrottleWrites)
349@@ -1787,8 +1744,8 @@
350 """Reset the counter named with 'name' every 1 second."""
351 # check the bandwidth limits
352 self.readThisSecond = 0
353- self.resetReadThisSecondID = self.callLater(1,
354- self._resetReadThisSecond)
355+ self.resetReadThisSecondID = self.callLater(
356+ 1, self._resetReadThisSecond)
357
358 def _resetWrittenThisSecond(self):
359 """Reset the counter named with 'name' every 1 second."""
360
361=== modified file 'ubuntuone/storageprotocol/context.py'
362--- ubuntuone/storageprotocol/context.py 2014-04-14 22:21:04 +0000
363+++ ubuntuone/storageprotocol/context.py 2015-05-18 17:24:40 +0000
364@@ -1,6 +1,6 @@
365 # ubuntuone.storageprotocol.context - ssl context creation
366 #
367-# Copyright 2009-2012 Canonical Ltd.
368+# Copyright 2009-2015 Canonical Ltd.
369 #
370 # This program is free software: you can redistribute it and/or modify it
371 # under the terms of the GNU Affero General Public License version 3,
372@@ -35,7 +35,11 @@
373 from twisted.internet import error, ssl
374 from twisted.python import log
375
376-from ubuntu_sso.utils import get_cert_dir
377+try:
378+ from ubuntu_sso.utils import get_cert_dir
379+except ImportError:
380+ # old system, let's support Precise
381+ get_cert_dir = lambda: '/etc/ssl/certs'
382
383
384 class HostnameVerifyContextFactory(ssl.CertificateOptions):
385@@ -52,7 +56,8 @@
386 # No extra checks because U1 certs have the right commonName
387 if self.expected_hostname != cert.get_subject().commonName:
388 log.err("Host name does not match certificate. "
389- "Expected %s but got %s." % (self.expected_hostname,
390+ "Expected %s but got %s." % (
391+ self.expected_hostname,
392 cert.get_subject().commonName))
393 return False
394 return preverifyOK
395@@ -95,5 +100,6 @@
396 raise error.CertificateError(
397 'No hostname specified. Unable to verify SSL certificate.')
398 ctx = HostnameVerifyContextFactory(hostname, verify=True,
399- caCerts=get_certificates(), method=SSL.SSLv23_METHOD)
400+ caCerts=get_certificates(),
401+ method=SSL.SSLv23_METHOD)
402 return ctx

Subscribers

People subscribed via source and target branches