Merge lp:~jamestait/canonical-identity-provider/lp1167645 into lp:canonical-identity-provider/release

Proposed by James Tait
Status: Merged
Approved by: Ricardo Kirkner
Approved revision: no longer in the source branch.
Merged at revision: 793
Proposed branch: lp:~jamestait/canonical-identity-provider/lp1167645
Merge into: lp:canonical-identity-provider/release
Diff against target: 158 lines (+59/-14)
4 files modified
identityprovider/forms.py (+11/-12)
identityprovider/tests/test_forms.py (+21/-2)
identityprovider/tests/test_views_server.py (+25/-0)
identityprovider/views/server.py (+2/-0)
To merge this branch: bzr merge lp:~jamestait/canonical-identity-provider/lp1167645
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+158682@code.launchpad.net

Commit message

Ensure that for trusted, auto-authorize RPs, AX data is still returned.

Description of the change

Ensure that for trusted, auto-authorize RPs, AX data is still returned.

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

<pindonga> 1. you added the ax extension to the request in the server views... this means this wasn't handled at all previously?
<jayteeuk> It wasn't handled in a specific situation - namely, when the RP was trusted and configured to auto-authorize.
<pindonga> so this addition is only in that case?
<pindonga> (can't get that from the diff context)
<jayteeuk> Correct - it's not obvious from the code, but we end up in that block specifically under those circumstances.

<pindonga> 2. care to explain why the data_approved_for_request also needs to be returned on GET (I'm sure it's correct, just want the context)
<pindonga> jayteeuk, did you see question 2 up there?
<jayteeuk> I was about to answer 2) but I think what I was about to say might not be the reason I changed that - let me go get my notebook. :)
<pindonga> :)
<jayteeuk> pindonga, all I have scribbled is "This will never have data for GET", with no explanation. :-(
<pindonga> I recall you saying something about auto-authorize not posting but getting, but don't recall the specifics
<jayteeuk> I'll have another look and make sure, because I'm not totally convinced that's true either.

<pindonga> ok, let me ask other stuff
<pindonga> 3. test_fields_for_trusted_auto_authorize_site looks good, but triggers the question, do we have a test that shows that optional attrs are also returned for trusted/auto-authorized sites?
<jayteeuk> pindonga, lines 86-87 request an optional attribute - maybe that could use a couple of comments.
<pindonga> ah, no, I missed the required=False
<pindonga> sorry
<pindonga> why not using assertIn instead of assertTrue in that test?
<jayteeuk> pindonga, good point - I made the same mistake last time.
<pindonga> not a mistake, really :)
<jayteeuk> No, but good style.

<pindonga> 4. in test_handle_user_response_ax_openid_is_authorized_idselect, why do you only assert the email? what's the intent of this test?
<pindonga> that's all I have
<jayteeuk> 4) I should add the other two attributes to the tests - language isn't allowed in the rpconfig, so won't be returned.
<jayteeuk> The test was to ensure the AX attributes were being returned.

<jayteeuk> pindonga, I need to reboot quickly before the weekly call, could you drop those questions in the MP, and I'll address them afterwards?
<pindonga> kk
<jayteeuk> Thanks!
<jayteeuk> Good review!
<jayteeuk> brb!

review: Needs Information
Revision history for this message
James Tait (jamestait) wrote :

17:20 < jayteeuk> Indirect communication passes messages through the user-agent, can be initiated by either the RP or the OP, and is
                  used for authentication requests and authentication responses.
17:21 < jayteeuk> "There are two methods for indirect communication: HTTP redirects and HTML form submission. Both form submission and
                  redirection require that the sender know a recipient URL and that the recipient URL expect indirect messages, as
                  specified in Section 4.1.2. The initiator of the communication chooses which method of indirect communication is
                  appropriate depending on capabilities, message size, or other external factors."
17:22 < jayteeuk> So, pretty much, if we're going to interact with RPs outside of our control, they can decide whether to use GETs or
                  POSTs.
17:23 < jayteeuk> A GET request is perfectly valid.

Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'identityprovider/forms.py'
2--- identityprovider/forms.py 2013-04-09 14:24:53 +0000
3+++ identityprovider/forms.py 2013-04-15 16:55:33 +0000
4@@ -361,7 +361,7 @@
5 callback = forms.CharField(error_messages=default_errors)
6
7
8-def _get_data_for_user(request, fields, with_verified=False):
9+def _get_data_for_user(request, fields, for_ax=False):
10 """Get the data to ask about in the form based on the user's
11 account record.
12 """
13@@ -370,9 +370,6 @@
14 values['fullname'] = user.displayname
15 if user.preferredemail is not None:
16 values['email'] = user.preferredemail.email
17- if with_verified:
18- values['account_verified'] = (
19- 'token_via_email' if user.is_verified else 'no')
20 if user.person is not None:
21 values['nickname'] = user.person.name
22 if user.person.time_zone is not None:
23@@ -381,7 +378,11 @@
24 values['language'] = user.preferredlanguage
25 else:
26 values['language'] = translation.get_language_from_request(request)
27- logger.debug("values (sreg_fields) = " + str(values))
28+ if for_ax:
29+ values['account_verified'] = (
30+ 'token_via_email' if user.is_verified else 'no')
31+ logger.debug('values (%s_fields) = %s',
32+ 'ax' if for_ax else 'sreg', str(values))
33
34 return dict([(f, values[f]) for f in fields if f in values])
35
36@@ -419,7 +420,7 @@
37 fields = set()
38 else:
39 fields = sreg_fields
40- self.data = _get_data_for_user(request, fields, with_verified=False)
41+ self.data = _get_data_for_user(request, fields, for_ax=False)
42
43 super(SRegRequestForm, self).__init__(self.data)
44 self._init_fields(self.data)
45@@ -486,7 +487,7 @@
46 ax_fields = self._get_requested_field_aliases()
47 if rpconfig is not None:
48 ax_fields = self._filter_allowed_fields(ax_fields)
49- self.data = _get_data_for_user(request, ax_fields, with_verified=True)
50+ self.data = _get_data_for_user(request, ax_fields, for_ax=True)
51
52 super(AXFetchRequestForm, self).__init__(self.data)
53 self._init_fields(self.data)
54@@ -569,11 +570,9 @@
55 @property
56 def data_approved_for_request(self):
57 """Return the list of ax data approved for the request."""
58- if self.request_method == 'POST':
59- return dict(
60- [(f, self.data[f]) for f in self.data
61- if self.field_approved(AX_DATA_FIELDS.getNamespaceURI(f))])
62- return {}
63+ return dict(
64+ [(f, self.data[f]) for f in self.data
65+ if self.field_approved(AX_DATA_FIELDS.getNamespaceURI(f))])
66
67 @property
68 def has_data(self):
69
70=== modified file 'identityprovider/tests/test_forms.py'
71--- identityprovider/tests/test_forms.py 2013-04-02 09:26:22 +0000
72+++ identityprovider/tests/test_forms.py 2013-04-15 16:55:33 +0000
73@@ -435,8 +435,27 @@
74 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
75 form = AXFetchRequestForm(self._get_request_with_post_args(),
76 fetch_request, self.rpconfig)
77- self.assertTrue('fullname' in form.data_approved_for_request)
78- self.assertFalse('email' in form.data_approved_for_request)
79+ self.assertIn('fullname', form.data_approved_for_request)
80+ self.assertNotIn('email', form.data_approved_for_request)
81+
82+ def test_fields_for_trusted_auto_authorize_site(self):
83+ """The server should always return values for requested fields to
84+ trusted sites configured to auto-authorize.
85+ """
86+ self.rpconfig.allowed_ax = 'fullname,email'
87+ self.rpconfig.auto_authorize = True
88+ fetch_request = FetchRequest()
89+ # One required attribute
90+ fetch_request.add(
91+ AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
92+ # One optional attribute
93+ fetch_request.add(
94+ AttrInfo(AX_URI_EMAIL, alias='email', required=False))
95+ form = AXFetchRequestForm(self._get_request_with_post_args(),
96+ fetch_request, self.rpconfig)
97+ # Both attributes should be returned
98+ self.assertIn('fullname', form.data_approved_for_request)
99+ self.assertIn('email', form.data_approved_for_request)
100
101 def test_optional_fields_for_trusted_site(self):
102 """The server should return values for optional fields to trusted
103
104=== modified file 'identityprovider/tests/test_views_server.py'
105--- identityprovider/tests/test_views_server.py 2013-04-02 09:26:22 +0000
106+++ identityprovider/tests/test_views_server.py 2013-04-15 16:55:33 +0000
107@@ -167,6 +167,31 @@
108 self.assertEqual(r.status_code, 200)
109 self.assertTemplateUsed(r, 'server/invalid_identifier.html')
110
111+ def test_handle_user_response_ax_openid_is_authorized_idselect(self):
112+ # update rp to auto authorize
113+ self.rpconfig.auto_authorize = True
114+ self.rpconfig.allowed_ax = 'fullname,email,account_verified'
115+ self.rpconfig.save()
116+
117+ self.client.login(username=self.email, password=DEFAULT_USER_PASSWORD)
118+ self.params.update({
119+ 'openid.ns.ax': AXMessage.ns_uri,
120+ 'openid.ax.mode': FetchRequest.mode,
121+ 'openid.ax.type.fullname': AX_URI_FULL_NAME,
122+ 'openid.ax.type.email': AX_URI_EMAIL,
123+ 'openid.ax.type.account_verified': AX_URI_ACCOUNT_VERIFIED,
124+ 'openid.ax.type.language': AX_URI_LANGUAGE,
125+ 'openid.ax.required': 'fullname,email,account_verified,language',
126+ })
127+ r = self.client.get(self.url, self.params)
128+ query = self.get_query(r)
129+ self.assertEqual(query['openid.ax.value.email.1'],
130+ quote_plus(self.email))
131+ self.assertEqual(query['openid.ax.value.fullname.1'],
132+ quote_plus(self.account.get_full_name()))
133+ self.assertEqual(query['openid.ax.value.account_verified.1'],
134+ 'token_via_email')
135+
136 def test_handle_user_response_openid_is_authorized_idselect(self):
137 # update rp to auto authorize
138 self.rpconfig.auto_authorize = True
139
140=== modified file 'identityprovider/views/server.py'
141--- identityprovider/views/server.py 2013-04-02 23:01:33 +0000
142+++ identityprovider/views/server.py 2013-04-15 16:55:33 +0000
143@@ -176,6 +176,7 @@
144 else:
145 oresponse = orequest.answer(True)
146 _add_sreg(request, orequest, oresponse)
147+ _add_ax(request, orequest, oresponse)
148 _check_team_membership(request, orequest, oresponse,
149 immediate=True)
150 response = _django_response(request, oresponse, True)
151@@ -197,6 +198,7 @@
152 else:
153 oresponse = orequest.answer(True)
154 _add_sreg(request, orequest, oresponse)
155+ _add_ax(request, orequest, oresponse)
156 _check_team_membership(request, orequest, oresponse, immediate=True)
157 response = _django_response(request, oresponse, True)
158 elif (twofactor.is_authenticated(request) and not