Merge lp:~lukasz-czyzykowski/rnr-server/fix-duplicate-consumer into lp:rnr-server

Proposed by Łukasz Czyżykowski
Status: Merged
Approved by: Michael Nelson
Approved revision: 154
Merged at revision: 156
Proposed branch: lp:~lukasz-czyzykowski/rnr-server/fix-duplicate-consumer
Merge into: lp:rnr-server
Diff against target: 122 lines (+55/-6)
3 files modified
Makefile (+1/-1)
src/reviewsapp/models/oauthtoken.py (+29/-5)
src/reviewsapp/tests/test_auth.py (+25/-0)
To merge this branch: bzr merge lp:~lukasz-czyzykowski/rnr-server/fix-duplicate-consumer
Reviewer Review Type Date Requested Status
Michael Nelson (community) Approve
Review via email: mp+53750@code.launchpad.net

Commit message

[r=noodels][bug=706035] Workaround for Bug:706035 to enable access to api when there is more than one oauth consumer with the same consumer_key.

Description of the change

Overview
========
This branch works around the problem of having duplicate oauth consumers in the database with the same consumer_key.

Details
=======
In principle the situation when there is more than one oauth consumer with the same consumer_key should never happen. But because it had happened on our servers the workaround proposed here is to:

- not fail miserably, when trying to use api using oauth consumer key when such bad situation had happened,

- clear the situation, so future access will be quicker and saner.

Testing
=======
Follow `README` instructions to setup environment and then issue `make test`.

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

09:36 <@noodles> lukasz: with your branch, the code change looks fine, but I think there should be a comment in the code along the lines of "There should not be more than one, but so that we don't error..."...
09:36 < lukasz> k
09:36 <@noodles> and, I wonder if it would be worth logging the deletion when it occurs?
09:36 <@noodles> (so that we can eventually track it down?)
09:37 < lukasz> I wondered about that
09:37 < lukasz> or just leave alone
09:37 < lukasz> the code can cope with that
09:38 < lukasz> noodles: the deletion was achuni's idea
09:38 <@noodles> Hrm... are we certain that the duplicates are always for the same user? (based on the consumer_key, they have to be?)
09:38 < lukasz> noodles: no, they can't be fore the same user (that's a unique thing for Consumers)
09:39 * noodles tries to re-read that statement
09:39 < lukasz> but, same person can have two django users
09:39 < lukasz> in my opinion this can only happen if you switch open_id providers
09:40 <@noodles> Ah - between staging/production?
09:40 < lukasz> but I'm not sure
09:40 < lukasz> yes
09:40 <@noodles> Which would fit, time-wise too.
09:41 <@noodles> OK, lets put that knowledge in the comment with the code, so that if we come back to it in 6months, we can understand why we did it?
09:41 < lukasz> maybe just switch that to log such situation and leave it alone
09:41 <@noodles> (although, you've linked to the bug too)
09:41 < lukasz> ok
09:41 <@noodles> +1 for logging and leaving (so we can always investigate)
09:42 <@noodles> if our assumptions turn out to be false...
09:43 < lukasz> ok

153. By Łukasz Czyżykowski

Moved logic for finding Consumer object based on the consumer_key to
Consumer class itself, so that other parts of the code can also use it.

154. By Łukasz Czyżykowski

Changed buildout from depending on distribute to setuptools because of
current distribute not working too well.

Revision history for this message
Michael Nelson (michael.nelson) wrote :

Great, thanks lukasz.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2011-02-23 11:12:49 +0000
3+++ Makefile 2011-03-17 09:22:24 +0000
4@@ -6,7 +6,7 @@
5 bin/buildout
6
7 bootstrap:
8- $(PYTHON) bootstrap.py --distribute
9+ $(PYTHON) bootstrap.py
10
11 port=8000
12 run:
13
14=== modified file 'src/reviewsapp/models/oauthtoken.py'
15--- src/reviewsapp/models/oauthtoken.py 2010-12-02 18:45:33 +0000
16+++ src/reviewsapp/models/oauthtoken.py 2011-03-17 09:22:24 +0000
17@@ -31,6 +31,7 @@
18 import struct
19 import string
20 from functools import partial
21+from logging import getLogger
22
23 from oauth.oauth import OAuthToken, OAuthDataStore, OAuthConsumer
24
25@@ -97,6 +98,30 @@
26 created_at = models.DateTimeField(auto_now_add=True)
27 updated_at = models.DateTimeField(auto_now=True)
28
29+ @classmethod
30+ def lookup_consumer(self, consumer_key):
31+ # lukasz: To my best knowledge the query below can return more than one
32+ # entry only if the OpenID provider was switched for already deployed
33+ # code (between staging/production SSO). That would mean, that the same
34+ # user on SSO will get second Django user, and second Consumer object,
35+ # but with exactly the same consumer_key.
36+ consumers = Consumer.objects.filter(key=consumer_key)
37+ # If more than one Consumer object is found we choose the one created
38+ # as the last one keeping up with the assumption of switching the
39+ # OpenID providers.
40+ consumers = consumers.order_by('-created_at')
41+ consumers_count = consumers.count()
42+
43+ if consumers_count == 0:
44+ return None
45+ elif consumers_count > 1:
46+ getLogger(__name__).info(
47+ "consumer_key=%s has %d entries in the database",
48+ consumer_key, consumers_count
49+ )
50+ consumer = consumers[0]
51+ return consumer
52+
53 def __unicode__(self):
54 return self.key
55
56@@ -119,7 +144,7 @@
57 """
58 Create new nonce object linked to given consumer and token
59 """
60- consumer = Consumer.objects.get(key=consumer_key)
61+ consumer = Consumer.lookup_consumer(consumer_key)
62 token = consumer.token_set.get(token=token_value)
63 return consumer.nonce_set.create(token=token, nonce=nonce)
64
65@@ -157,11 +182,10 @@
66
67 :returns: OAuthConsumer object
68 """
69- try:
70- consumer = Consumer.objects.get(key=consumer_key)
71+ consumer = Consumer.lookup_consumer(consumer_key)
72+ if consumer is not None:
73 return consumer.oauth_consumer()
74- except Consumer.DoesNotExist:
75- return None
76+
77
78 def lookup_nonce(self, consumer, token, nonce):
79 """
80
81=== modified file 'src/reviewsapp/tests/test_auth.py'
82--- src/reviewsapp/tests/test_auth.py 2011-02-23 10:20:41 +0000
83+++ src/reviewsapp/tests/test_auth.py 2011-03-17 09:22:24 +0000
84@@ -38,6 +38,7 @@
85 from reviewsapp.tests.factory import TestCaseWithFactory
86 from reviewsapp.tests.helpers import mock_data_for_account
87
88+
89 class MockRequest:
90 def __init__(self, method='GET'):
91 self.META = {'QUERY_STRING': ''}
92@@ -253,6 +254,30 @@
93 user = User.objects.get(id=user.id)
94 self.assertEqual('Hazel Jennet', user.get_full_name())
95
96+ @patch('reviewsapp.utilities.WebServices.get_data_for_account')
97+ def test_auth_succeedes_when_more_than_one_consumer(self, mock_get_data):
98+ """
99+ Check that auth works even when there are two consumers for one token
100+
101+ Bug: #706035
102+
103+ """
104+ user1 = self.factory.makeUser()
105+ user2 = self.factory.makeUser()
106+
107+ token1, consumer1 = self.factory.makeOAuthTokenAndConsumer(user=user1)
108+ token2, consumer2 = self.factory.makeOAuthTokenAndConsumer(user=user2)
109+ consumer2.key = consumer1.key
110+ consumer2.save()
111+
112+ request = MockRequest()
113+ request.META.update(
114+ self.header_from_token(request, token2, consumer2))
115+ mock_get_data.return_value = mock_data_for_account(consumer2, token2)
116+
117+ response = self.get_resource(request)
118+ self.assertEquals(response.status_code, 200)
119+
120 def make_request_with_token(self, save=True, method='GET'):
121 token, consumer = self.factory.makeOAuthTokenAndConsumer(save=save)
122 request = MockRequest(method=method)

Subscribers

People subscribed via source and target branches