Merge lp:~canonical-isd-hackers/canonical-identity-provider/return_to_match into lp:~canonical-isd-hackers/canonical-identity-provider/stable

Proposed by David Owen
Status: Merged
Approved by: Łukasz Czyżykowski
Approved revision: no longer in the source branch.
Merged at revision: 74
Proposed branch: lp:~canonical-isd-hackers/canonical-identity-provider/return_to_match
Merge into: lp:~canonical-isd-hackers/canonical-identity-provider/stable
Diff against target: 110 lines (+68/-3)
3 files modified
identityprovider/models/openidmodels.py (+34/-0)
identityprovider/tests/test_views_ui_logout.py (+32/-0)
identityprovider/views/ui.py (+2/-3)
To merge this branch: bzr merge lp:~canonical-isd-hackers/canonical-identity-provider/return_to_match
Reviewer Review Type Date Requested Status
Łukasz Czyżykowski (community) Approve
Review via email: mp+36948@code.launchpad.net

Commit message

Changed the way in which trust_root is found by logout page.

Description of the change

Changed to match the trust_root as a prefix of return_to.

To post a comment you must log in.
Revision history for this message
Łukasz Czyżykowski (lukasz-czyzykowski) wrote :

I can't think of better way of writing that query. Approving.

review: Approve
Revision history for this message
Łukasz Czyżykowski (lukasz-czyzykowski) wrote :

ok

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'identityprovider/models/openidmodels.py'
2--- identityprovider/models/openidmodels.py 2010-07-28 14:25:25 +0000
3+++ identityprovider/models/openidmodels.py 2010-09-29 13:19:43 +0000
4@@ -109,6 +109,38 @@
5 unique_together = ('server_url', 'timestamp', 'salt')
6
7
8+class OpenIDRPConfigManager(models.Manager):
9+
10+ def for_url(self, url):
11+ if url.endswith('/'):
12+ url = url[:-1]
13+ # We need to encode the % character in URLs, so that an
14+ # attacker can't maliciously take advantage of it as a
15+ # wildcard. To do this, we select an escape character (~),
16+ # and then escape it as ~T and % as ~P.
17+ #
18+ # Although this hole only exists in the right-hand argument of
19+ # LIKE, this replacement happens in both the received URL and
20+ # the recorded trust_root, in case they have legitimate ~
21+ # characters that need to be matched.
22+ #
23+ # Python uses % for parameters in interpolated strings, and
24+ # PostgreSQL uses it as a wild card in LIKE-patterns; so
25+ # they're escaped here.
26+ condition = """
27+ replace(replace(%s, '~', '~T'), '%%', '~P')
28+LIKE (replace(replace(trust_root, '~', '~T'), '%%', '~P') || '%%')
29+"""
30+
31+ q = self.extra(
32+ select={'len': 'length(trust_root)'},
33+ where=[condition],
34+ params=(url,),
35+ order_by=['-len'] # Select the longest match
36+ )
37+ return q[0] if q else None
38+
39+
40 class OpenIDRPConfig(models.Model):
41 trust_root = models.TextField(unique=True)
42 displayname = models.TextField()
43@@ -127,6 +159,8 @@
44 verbose_name = _('OpenID RP Config')
45 verbose_name_plural = _('OpenID RP Configs')
46
47+ objects = OpenIDRPConfigManager()
48+
49 def __unicode__(self):
50 return self.displayname
51
52
53=== modified file 'identityprovider/tests/test_views_ui_logout.py'
54--- identityprovider/tests/test_views_ui_logout.py 2010-09-22 00:43:01 +0000
55+++ identityprovider/tests/test_views_ui_logout.py 2010-09-29 13:19:43 +0000
56@@ -60,6 +60,38 @@
57 return_to = self.view.get_return_to_url(trust_root, None)
58 self.assertEquals(return_to, trust_root)
59
60+ def test_get_return_to_url_when_extends_root(self):
61+ trust_root = 'http://example.com/r'
62+ logout = trust_root + '/a'
63+ self.create_openid_rp_config(trust_root)
64+ return_to = self.view.get_return_to_url(logout, None)
65+ self.assertEquals(return_to, logout)
66+
67+ def test_get_return_to_url_when_diminishes_root(self):
68+ logout = 'http://example.com/r'
69+ trust_root = logout + '/a'
70+ self.create_openid_rp_config(trust_root)
71+ return_to = self.view.get_return_to_url(logout, None)
72+ self.assertTrue(return_to is None)
73+
74+ def test_get_return_to_url_when_tricky_trust(self):
75+ trust_root = 'http://%.example.com/r'
76+ self.create_openid_rp_config(trust_root)
77+ return_to = self.view.get_return_to_url('http://launchpad.net/?sneaky=.example.com/r', None)
78+ self.assertTrue(return_to is None)
79+
80+ def test_get_return_to_url_when_tricky_return(self):
81+ trust_root = 'http://example.com/r'
82+ self.create_openid_rp_config(trust_root)
83+ return_to = self.view.get_return_to_url('http://%.com/r', None)
84+ self.assertTrue(return_to is None)
85+
86+ def test_get_return_to_url_when_tilde(self):
87+ trust_root = 'http://example.com/~id'
88+ self.create_openid_rp_config(trust_root)
89+ return_to = self.view.get_return_to_url(trust_root, None)
90+ self.assertEquals(return_to, trust_root)
91+
92 def test_get_return_to_url_when_referer_mismatches_known_url(self):
93 trust_root = 'http://example.com/r'
94 self.create_openid_rp_config(trust_root)
95
96=== modified file 'identityprovider/views/ui.py'
97--- identityprovider/views/ui.py 2010-09-22 08:04:03 +0000
98+++ identityprovider/views/ui.py 2010-09-29 13:19:43 +0000
99@@ -113,9 +113,8 @@
100 def get_return_to_url(self, return_to, http_referer):
101 if not return_to:
102 return None
103- known = OpenIDRPConfig.objects.filter(
104- trust_root=return_to).count() == 1
105- if known:
106+ rp_config = OpenIDRPConfig.objects.for_url(return_to)
107+ if rp_config is not None:
108 if http_referer is not None and http_referer != return_to:
109 return None
110 return return_to

Subscribers

People subscribed via source and target branches