Merge lp:~jamestait/canonical-identity-provider/unify-sreg-ax-forms 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: 828
Proposed branch: lp:~jamestait/canonical-identity-provider/unify-sreg-ax-forms
Merge into: lp:canonical-identity-provider/release
Diff against target: 2360 lines (+1164/-594)
16 files modified
identityprovider/admin.py (+1/-7)
identityprovider/fixtures/2f.json (+1/-1)
identityprovider/forms.py (+91/-170)
identityprovider/management/commands/add_openid_rp_config.py (+4/-5)
identityprovider/management/commands/create_test_team.py (+2/-2)
identityprovider/migrations/0008_add_allowed_user_attribs.py (+213/-0)
identityprovider/migrations/0009_unify_allowed_sreg_ax.py (+292/-0)
identityprovider/migrations/0010_remove_allowed_sreg_ax.py (+219/-0)
identityprovider/models/openidmodels.py (+1/-2)
identityprovider/templates/server/decide.html (+4/-10)
identityprovider/tests/openid_server/per_version/test_restricted_sreg.py (+5/-4)
identityprovider/tests/test_admin.py (+7/-54)
identityprovider/tests/test_command_add_openid_rp_config.py (+10/-29)
identityprovider/tests/test_forms.py (+263/-235)
identityprovider/tests/test_views_server.py (+27/-33)
identityprovider/views/server.py (+24/-42)
To merge this branch: bzr merge lp:~jamestait/canonical-identity-provider/unify-sreg-ax-forms
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+161125@code.launchpad.net

Commit message

Unify the allowed_ax and allowed_sreg properties on the OpenIDRPConfig.

Description of the change

This branch unifies the allowed_ax and allowed_sreg properties on the
OpenIDRPConfig object, into an allowed_user_attribs property. There is a
migration script that will merge the allowed properties from original two
columns, so no additional configuration of RPs is required.

The SRegRequestForm and AXFetchRequestForm have been merged into a
UserAttribsRequestForm that takes the required and optional attributes from the
SReg and AX request and pulls them together such that if a requested attribute
is required in either request, it will be treated as a required attribute
over all. This really only affects the presentation of the form that is
displayed to the user - required attributes for untrusted RPs are still checked
by default, and also disabled for trusted RPs.

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

l. 102-103: this is not really needed unless allowed_user_attribs can be None, right?
l. 104-105: this can also be avoided by having allowed_attribs = known_attribus = set(...) in l. 97
l. 169: is it really necessary to shadow the 'value' attribute? can't we use name and value?
l. 180-181: is the comment right? because the code doesn't say the same.. also can we have an extra pair of () for the | expression? (just to be explict)
l. 197-202: wouldn't this mean you can approve a field by just posting it, disregarding whether it was checked? (maybe I'm missing some standard html subtlety here)
l. 573: shouldn't you skip the account_verified attrib here?
l. 1483,1621: why is it now necessary to set the allowed_attrs ?
l. 1645: how can this work? allowed_attrs don't match the already approved ones, so how were those possible to approve?
l. 1736, 1759: these two tests are virtually identical, except for the attribute being requested.. and that doesn't make the diff between a trusted/untrusted site. doesn't the untrusted test require that you remove the rpconfig entry? how are you sure the site is untrusted in the untrusted test?
l. 1866-1875: why was this change needed at all?

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

> l. 102-103: this is not really needed unless allowed_user_attribs can be None,
> right?

The empty string is also caught by this, so I think it's still needed.

> l. 104-105: this can also be avoided by having allowed_attribs =
> known_attribus = set(...) in l. 97

Yep, that would save a couple of lines.

> l. 169: is it really necessary to shadow the 'value' attribute? can't we use
> name and value?

That would improved readability, actually, yes.

> l. 180-181: is the comment right? because the code doesn't say the same.. also
> can we have an extra pair of () for the | expression? (just to be explict)

Erm, no, I didn't update the comment. Good catch!

> l. 197-202: wouldn't this mean you can approve a field by just posting it,
> disregarding whether it was checked? (maybe I'm missing some standard html
> subtlety here)

So, e.g. fullname=&email=email results in both being checked? Hm, good point, I'm not actually sure. I'll experiment with it and see.

> l. 573: shouldn't you skip the account_verified attrib here?

Indeed! For the backward migration it probably will be there!

> l. 1483,1621: why is it now necessary to set the allowed_attrs ?

l. 1483 is the equivalent of the removed l 1463; l. 1621 addresses what appears to me to be a flaw in the original test - for a trusted, non-auto-authorising RP, a required attribute should be presented to the user as a checked, disabled checkbox, but only if we are configured to allow that attribute.

> l. 1645: how can this work? allowed_attrs don't match the already approved
> ones, so how were those possible to approve?

Ah, bugger, I made a real mess of that one. :-/

> l. 1736, 1759: these two tests are virtually identical, except for the
> attribute being requested.. and that doesn't make the diff between a
> trusted/untrusted site. doesn't the untrusted test require that you remove the
> rpconfig entry? how are you sure the site is untrusted in the untrusted test?

No, there is a difference - l. 1744 passes in the rpconfig, so the site is trusted; l. 1766 passes in no rpconfig, so the site is untrusted. Maybe this could be made clearer by using kwargs, though.

> l. 1866-1875: why was this change needed at all?

Not strictly needed, but the old test could match, e.g. nickname=assoc_handle, where the new test explicitly checks that openid.assoc_handle and openid.sig are form fields. It's just a bit more robust.

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

> > l. 102-103: this is not really needed unless allowed_user_attribs can be
> None,
> > right?
>
> The empty string is also caught by this, so I think it's still needed.
>

not really, bc ''.split(';') == [] so you get the same end result.

> > l. 104-105: this can also be avoided by having allowed_attribs =
> > known_attribus = set(...) in l. 97
>
> Yep, that would save a couple of lines.
>
> > l. 169: is it really necessary to shadow the 'value' attribute? can't we use
> > name and value?
>
> That would improved readability, actually, yes.
>
> > l. 180-181: is the comment right? because the code doesn't say the same..
> also
> > can we have an extra pair of () for the | expression? (just to be explict)
>
> Erm, no, I didn't update the comment. Good catch!
>
> > l. 197-202: wouldn't this mean you can approve a field by just posting it,
> > disregarding whether it was checked? (maybe I'm missing some standard html
> > subtlety here)
>
> So, e.g. fullname=&email=email results in both being checked? Hm, good point,
> I'm not actually sure. I'll experiment with it and see.
>
> > l. 573: shouldn't you skip the account_verified attrib here?
>
> Indeed! For the backward migration it probably will be there!
>
> > l. 1483,1621: why is it now necessary to set the allowed_attrs ?
>
> l. 1483 is the equivalent of the removed l 1463; l. 1621 addresses what
> appears to me to be a flaw in the original test - for a trusted, non-auto-
> authorising RP, a required attribute should be presented to the user as a
> checked, disabled checkbox, but only if we are configured to allow that
> attribute.
>
> > l. 1645: how can this work? allowed_attrs don't match the already approved
> > ones, so how were those possible to approve?
>
> Ah, bugger, I made a real mess of that one. :-/
>
> > l. 1736, 1759: these two tests are virtually identical, except for the
> > attribute being requested.. and that doesn't make the diff between a
> > trusted/untrusted site. doesn't the untrusted test require that you remove
> the
> > rpconfig entry? how are you sure the site is untrusted in the untrusted
> test?
>
> No, there is a difference - l. 1744 passes in the rpconfig, so the site is
> trusted; l. 1766 passes in no rpconfig, so the site is untrusted. Maybe this
> could be made clearer by using kwargs, though.

yes, that would help please

>
> > l. 1866-1875: why was this change needed at all?
>
> Not strictly needed, but the old test could match, e.g. nickname=assoc_handle,
> where the new test explicitly checks that openid.assoc_handle and openid.sig
> are form fields. It's just a bit more robust.

good, robust is good :)

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

> > > l. 102-103: this is not really needed unless allowed_user_attribs can be
> > None,
> > > right?
> >
> > The empty string is also caught by this, so I think it's still needed.
> >
>
> not really, bc ''.split(';') == [] so you get the same end result.

I don't think so:

>>> ''.split(';')
['']
>>> len(''.split(';'))
1
>>>

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

> > > > l. 102-103: this is not really needed unless allowed_user_attribs can be
> > > None,
> > > > right?
> > >
> > > The empty string is also caught by this, so I think it's still needed.
> > >
> >
> > not really, bc ''.split(';') == [] so you get the same end result.
>
> I don't think so:
>
> >>> ''.split(';')
> ['']
> >>> len(''.split(';'))
> 1
> >>>

sorry, I really meant

allowed_attribs = known_attribs.intersection(self.rpconfig.allowed_user_attribs.split(','))

would return set([]) too if allowed_user_attribs is ''

The if is really only necessary if we can't always ensure allowed_user_attribs is a string.

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

Just a few minor issues/questions

l. 209: small correction needed in comment (it's not just ax data, right?)
l. 1802: why is email checked if it was optional?
l. 1829: don't we want to assert fullname is still required? this test seems to contradict l.1802 unless I'm missing something (maybe the default value for the rpconfig in the test is to pre-approve the email field?)

Everything else looks really good, and seems much clearer now. Thanks for the hard work!

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

> sorry, I really meant
>
> allowed_attribs = known_attribs.intersection(self.rpconfig.allowed_user_attribs.split(','))
>
> would return set([]) too if allowed_user_attribs is ''
>
> The if is really only necessary if we can't always ensure allowed_user_attribs is a string.

Ah yes! I'm following you now - so the else is only really necessary if rpconfig.allowed_user_attribs can be None. I'll have to check if that's a possibility (and maybe fix that if it is).

> Just a few minor issues/questions
>
> l. 209: small correction needed in comment (it's not just ax data, right?)

Correct, nice catch!

> l. 1802: why is email checked if it was optional?

Checked by default, but not disabled - for a known trust root, we default to allowing all attributes, and disable the checkbox for required attributes so they can't be unchecked.

> l. 1829: don't we want to assert fullname is still required? this test seems
> to contradict l.1802 unless I'm missing something (maybe the default value for
> the rpconfig in the test is to pre-approve the email field?)

Yes, we should assert fullname is required as well, I'll add that. The email checkbox should be unchecked in this instance because the user has previously opted not to approve it (approved_data['approved'] = [] in l. 1822). I'll add a comment before l. 1829 to make this clear as I have done with similar tests elsewhere.

> Everything else looks really good, and seems much clearer now. Thanks for the
> hard work!

It's been a bit of an epic mission, but I think we've got it nailed this time! :)

Revision history for this message
James Tait (jamestait) wrote :
Download full text (4.7 KiB)

So with Simon's help I eventually managed to get the acceptance tests to run this morning. I got seven failures:

Three instances of this:
_StringException: Traceback (most recent call last):
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/runtests.py", line 571, in run_test_script
    exec self.code in self.context
  File "/home/jtait/src/canonical-identity-provider/identityprovider/tests/acceptance/consumer/return_private_teams.py", line 52, in <module>
    login_from_redirect()
  File "/home/jtait/src/canonical-identity-provider/identityprovider/tests/acceptance/shared/helpers.py", line 249, in login_from_redirect
    log_in = pages.LogInFromRedirect()
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/u1testutils/sst/__init__.py", line 74, in __init__
    self.assert_page_is_open()
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/u1testutils/sst/__init__.py", line 98, in assert_page_is_open
    self.assert_headings2()
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/u1testutils/sst/__init__.py", line 157, in assert_headings2
    self._assert_elements_text('h2', self.headings2)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/u1testutils/sst/__init__.py", line 148, in _assert_elements_text
    ', '.join(expected_texts), ', '.join(elements_text))
AssertionError: Expected elements texts: Log in, Are you new?
Actual elements texts: Log in to Ubuntu Single Sign On, Are you new?

Two instances of this:
_StringException: Traceback (most recent call last):
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/runtests.py", line 571, in run_test_script
    exec self.code in self.context
  File "/home/jtait/src/canonical-identity-provider/identityprovider/tests/acceptance/root/link_logo.py", line 25, in <module>
    wait_for(assert_title, 'Home | Ubuntu')
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 134, in wrapped
    return func(*args, **kwargs)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 750, in wait_for
    _wait_for(condition, False, _TIMEOUT, _POLL, *args, **kwargs)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 727, in _wait_for
    _raise(error)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 118, in _raise
    raise AssertionError(msg)
AssertionError: Timed out waiting for: assert_title
Error during wait: Title is: u"The world's most popular free OS | Ubuntu". Should be: 'Home | Ubuntu'

Those look to be out-of-date tests. Two were related to SReg opt-out and disabled fields, though:

_StringException: Traceback (most recent call last):
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/runtests.py", line 571, in run_test_script
    exec self.code in self.context
  File "/home/jta...

Read more...

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

I just re-ran the acceptance tests thus:

 - Stop any running instances
 - fab shutdown_postgresql_server
 - rm -r .env/db
 - fab setup_postgresql_server
 - edit scripts/local.cfg-dev
  - set db_host to the full path to .env/db
  - set basedir to the full path to the working tree
 - scripts/run-acceptance-tests dev

I got one failure:

FAIL [16.410s]: sst.runtests.SSTScriptTestCase.test_link_logo (sst.runtests.SSTScriptTestCase)
----------------------------------------------------------------------
_StringException: Traceback (most recent call last):
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/runtests.py", line 571, in run_test_script
    exec self.code in self.context
  File "/home/jtait/src/canonical-identity-provider/identityprovider/tests/acceptance/root/link_logo.py", line 25, in <module>
    wait_for(assert_title, 'Home | Ubuntu')
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 134, in wrapped
    return func(*args, **kwargs)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 750, in wait_for
    _wait_for(condition, False, _TIMEOUT, _POLL, *args, **kwargs)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 727, in _wait_for
    _raise(error)
  File "/home/jtait/src/canonical-identity-provider/.env/local/lib/python2.7/site-packages/sst/actions.py", line 118, in _raise
    raise AssertionError(msg)
AssertionError: Timed out waiting for: assert_title
Error during wait: Title is: u"The world's most popular free OS | Ubuntu". Should be: 'Home | Ubuntu'

----------------------------------------------------------------------
Ran 116 tests in 1288.091s

FAILED (failures=1)

This is because the test clicks on the link in the page footer to www.ubuntu.com, which has recently changed its title text. It strikes me that this might not be the most robust test, but I don't currently have an alternative - ideally it wouldn't depend on an external resource, but in the interim maybe we could either update the text in the test, or change the test to look for some other unique attribute of the page.

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

Since that acceptance test is a known issue (and being worked on), this looks very good to me. Approving.

Great work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'identityprovider/admin.py'
2--- identityprovider/admin.py 2013-03-23 01:27:58 +0000
3+++ identityprovider/admin.py 2013-05-01 15:39:26 +0000
4@@ -8,7 +8,6 @@
5
6 from identityprovider.const import (
7 AX_DATA_LABELS,
8- SREG_LABELS,
9 )
10 from identityprovider.fields import CommaSeparatedField
11 from identityprovider.models import (
12@@ -45,16 +44,11 @@
13 displayname = forms.CharField(label="Display name")
14 trust_root = forms.URLField(verify_exists=False)
15 logo = forms.CharField(required=False)
16- allowed_ax = CommaSeparatedField(
17+ allowed_user_attribs = CommaSeparatedField(
18 choices=AX_DATA_LABELS.items(),
19 required=False,
20 widget=CommaSeparatedWidget,
21 )
22- allowed_sreg = CommaSeparatedField(
23- choices=SREG_LABELS.items(),
24- required=False,
25- widget=CommaSeparatedWidget
26- )
27 can_query_any_team = forms.BooleanField(label="Can query private teams",
28 required=False)
29 auto_authorize = forms.BooleanField(label="Auto-authorize",
30
31=== modified file 'identityprovider/fixtures/2f.json'
32--- identityprovider/fixtures/2f.json 2013-03-12 19:14:34 +0000
33+++ identityprovider/fixtures/2f.json 2013-05-01 15:39:26 +0000
34@@ -32,7 +32,7 @@
35 "logo": "",
36 "require_two_factor": false,
37 "auto_authorize": false,
38- "allowed_sreg": "",
39+ "allowed_user_attribs": "",
40 "creation_rationale": 13}},
41 {"pk": 1, "model": "auth.user", "fields": {
42 "username": "KxHA3MH",
43
44=== modified file 'identityprovider/forms.py'
45--- identityprovider/forms.py 2013-04-24 07:08:03 +0000
46+++ identityprovider/forms.py 2013-05-01 15:39:26 +0000
47@@ -11,8 +11,6 @@
48 from identityprovider.const import (
49 AX_DATA_FIELDS,
50 AX_DATA_LABELS,
51- SREG_DATA_FIELDS_ORDER,
52- SREG_LABELS,
53 )
54 from identityprovider.models import (
55 Account,
56@@ -371,147 +369,74 @@
57 callback = forms.CharField(error_messages=default_errors)
58
59
60-def _get_data_for_user(request, fields, for_ax=False):
61- """Get the data to ask about in the form based on the user's
62- account record.
63- """
64- values = {}
65- user = request.user
66- values['fullname'] = user.displayname
67- if user.preferredemail is not None:
68- values['email'] = user.preferredemail.email
69- if user.person is not None:
70- values['nickname'] = user.person.name
71- if user.person.time_zone is not None:
72- values['timezone'] = user.person.time_zone
73- if user.preferredlanguage is not None:
74- values['language'] = user.preferredlanguage
75- else:
76- values['language'] = translation.get_language_from_request(request)
77- if for_ax:
78+class UserAttribsRequestForm(Form):
79+ """A form object for user control over OpenID Attribute Exchange."""
80+
81+ def __init__(self, request, sreg_request, ax_request, rpconfig,
82+ approved_data=None):
83+ self.request = request
84+ self.sreg_request = sreg_request
85+ self.ax_request = ax_request
86+ self.rpconfig = rpconfig
87+ self.approved_data = approved_data
88+ # generate initial form data
89+ self._split_and_filter_requested_attributes()
90+ self._get_data_for_user()
91+ super(UserAttribsRequestForm, self).__init__(self.data)
92+ self._init_fields(self.data)
93+
94+ def _split_and_filter_requested_attributes(self):
95+ # Merge the requested attributes from sreg_request and ax_request and
96+ # filter out any that we don't recognise or that aren't allowed by our
97+ # rpconfig.
98+ # The rule is that if at least one request lists it as required, it's
99+ # required, otherwise it's optional.
100+ known_attribs = set([a for a in AX_DATA_FIELDS.iterAliases()])
101+ if self.rpconfig is not None:
102+ allowed = self.rpconfig.allowed_user_attribs or ''
103+ allowed_attribs = known_attribs.intersection(allowed.split(','))
104+ else:
105+ allowed_attribs = known_attribs
106+
107+ required = set()
108+ optional = set()
109+ if self.sreg_request:
110+ required.update(self.sreg_request.required)
111+ optional.update(self.sreg_request.optional)
112+ if self.ax_request:
113+ for uri, attr in self.ax_request.requested_attributes.iteritems():
114+ if attr.required:
115+ required.add(AX_DATA_FIELDS.getAlias(uri))
116+ else:
117+ optional.add(AX_DATA_FIELDS.getAlias(uri))
118+ optional.difference_update(required)
119+ self.required = required.intersection(allowed_attribs)
120+ self.optional = optional.intersection(allowed_attribs)
121+
122+ def _get_data_for_user(self):
123+ """Get the data to ask about in the form based on the user's
124+ account record.
125+ """
126+ values = {}
127+ user = self.request.user
128+ values['fullname'] = user.displayname
129+ if user.preferredemail is not None:
130+ values['email'] = user.preferredemail.email
131+ if user.person is not None:
132+ values['nickname'] = user.person.name
133+ if user.person.time_zone is not None:
134+ values['timezone'] = user.person.time_zone
135+ if user.preferredlanguage is not None:
136+ values['language'] = user.preferredlanguage
137+ else:
138+ values['language'] = translation.get_language_from_request(
139+ self.request)
140 values['account_verified'] = (
141 'token_via_email' if user.is_verified else 'no')
142- logger.debug('values (%s_fields) = %s',
143- 'ax' if for_ax else 'sreg', str(values))
144-
145- return dict([(f, values[f]) for f in fields if f in values])
146-
147-
148-class SRegRequestForm(Form):
149- """A form object for user control over OpenID sreg data.
150- """
151- fields = {}
152-
153- @property
154- def data_approved_for_request(self):
155- """Return the list of sreg data approved for the request."""
156- return dict([(f, self.data[f]) for f in self.data
157- if self.field_approved(f)])
158-
159- @property
160- def has_data(self):
161- """Helper property to check if this form has any data."""
162- return len(self.data) > 0
163-
164- def __init__(self, request, sreg_request, rpconfig, approved_data=None):
165- self.request = request
166- self.request_method = request.META.get('REQUEST_METHOD')
167- self.sreg_request = sreg_request
168- self.rpconfig = rpconfig
169- self.approved_data = approved_data
170- # generate initial form data
171- sreg_fields = [f for f in SREG_DATA_FIELDS_ORDER if f in set(
172- self.sreg_request.required + self.sreg_request.optional)]
173- if rpconfig is not None:
174- if rpconfig.allowed_sreg:
175- fields = set(sreg_fields).intersection(
176- set(rpconfig.allowed_sreg.split(',')))
177- else:
178- fields = set()
179- else:
180- fields = sreg_fields
181- self.data = _get_data_for_user(request, fields, for_ax=False)
182-
183- super(SRegRequestForm, self).__init__(self.data)
184- self._init_fields(self.data)
185-
186- def _init_fields(self, data):
187- """Initialises form fields for the user's sreg data.
188- """
189- for key, val in data.items():
190- label = "%s: %s" % (SREG_LABELS.get(key, key), val)
191- attrs = {}
192- if key in self.sreg_request.required:
193- attrs['class'] = 'required'
194- if self.rpconfig is not None:
195- attrs['disabled'] = 'disabled'
196- self.fields[key] = fields.BooleanField(
197- label=label,
198- widget=forms.CheckboxInput(attrs=attrs,
199- check_test=self.check_test),
200- )
201-
202- def check_test(self, value):
203- """Determines if a checkbox should be pre-checked based on previously
204- approved user data, openid request and relying party type.
205- """
206- for k, v in self.data.items():
207- if value == v:
208- value = k
209- break
210-
211- if self.rpconfig and value in self.sreg_request.required:
212- return True
213- elif (self.approved_data and
214- value in self.approved_data.get('requested', [])):
215- return value in self.approved_data.get('approved', [])
216- elif self.rpconfig:
217- return True
218- else:
219- return value in self.sreg_request.required
220-
221- def field_approved(self, field):
222- """Check if the field should be returned in the response based on user
223- preferences and overridden for trusted relying parties.
224- """
225- approved = set(self.request.POST.keys())
226- if self.rpconfig is not None:
227- sreg_fields = set(self.sreg_request.required)
228- if self.rpconfig.auto_authorize:
229- # also include optional fields
230- sreg_fields.update(set(self.sreg_request.optional))
231- approved.update(sreg_fields)
232- return field in approved
233-
234-
235-class AXFetchRequestForm(Form):
236- """A form object for user control over OpenID Attribute Exchange."""
237-
238- def __init__(self, request, ax_request, rpconfig, approved_data=None):
239- self.request = request
240- self.request_method = request.META.get('REQUEST_METHOD')
241- self.ax_request = ax_request
242- self.rpconfig = rpconfig
243- self.approved_data = approved_data
244- # generate initial form data
245- ax_fields = self._get_requested_field_aliases()
246- if rpconfig is not None:
247- ax_fields = self._filter_allowed_fields(ax_fields)
248- self.data = _get_data_for_user(request, ax_fields, for_ax=True)
249-
250- super(AXFetchRequestForm, self).__init__(self.data)
251- self._init_fields(self.data)
252-
253- def _get_requested_field_aliases(self):
254- return [AX_DATA_FIELDS.getAlias(f)
255- for f in AX_DATA_FIELDS.iterNamespaceURIs()
256- if f in set(self.ax_request.requested_attributes.keys())]
257-
258- def _filter_allowed_fields(self, requested_fields):
259- if self.rpconfig.allowed_ax:
260- return set(requested_fields).intersection(
261- set(self.rpconfig.allowed_ax.split(',')))
262- return set()
263+ logger.debug('user attrib values = %s', str(values))
264+
265+ self.data = dict([(f, values[f]) for f in self.required | self.optional
266+ if f in values])
267
268 def _init_fields(self, data):
269 """Initialises form fields for the user's ax data.
270@@ -519,8 +444,7 @@
271 for key, val in data.items():
272 label = "%s: %s" % (AX_DATA_LABELS.get(key, key), val)
273 attrs = {}
274- if (AX_DATA_FIELDS.getNamespaceURI(key) in
275- self.ax_request.getRequiredAttrs()):
276+ if (key in self.required):
277 attrs['class'] = 'required'
278 if self.rpconfig is not None:
279 attrs['disabled'] = 'disabled'
280@@ -534,55 +458,52 @@
281 """Determines if a checkbox should be pre-checked based on previously
282 approved user data, openid request and relying party type.
283 """
284+ # We don't care about the value, so we first figure out the field name
285+ name = value
286 for k, v in self.data.items():
287 if value == v:
288- value = k
289+ name = k
290 break
291
292+ # Don't approve fields that weren't requested
293+ if name not in (self.required | self.optional):
294+ return False
295+
296 if self.rpconfig:
297 # Trusted site, check required fields
298- if (AX_DATA_FIELDS.getNamespaceURI(value) in
299- self.ax_request.getRequiredAttrs()):
300+ if (name in self.required):
301 return True
302- # If we have previous (dis)approval for this site, use it
303- if self.approved_data:
304- return (value in self.approved_data.get('requested', []) and
305- value in self.approved_data.get('approved', []))
306- # Otherwise, default to True
307- return (AX_DATA_FIELDS.getNamespaceURI(value) in
308- self.ax_request.requested_attributes)
309+ if self.approved_data and name in self.approved_data.get(
310+ 'requested', []):
311+ # The field was previously requested, use the same response
312+ return name in self.approved_data.get('approved', [])
313+ # We've never (dis)approved this field before, default to True
314+ return True
315 else:
316 # If we have previous (dis)approval for this site, use it
317- if self.approved_data:
318- return (value in self.approved_data.get('requested', []) and
319- value in self.approved_data.get('approved', []))
320+ if self.approved_data and name in self.approved_data.get(
321+ 'requested', []):
322+ return name in self.approved_data.get('approved', [])
323 # No previous (dis)approval, check required and leave the rest
324- if (AX_DATA_FIELDS.getNamespaceURI(value) in
325- self.ax_request.getRequiredAttrs()):
326- return True
327- # Otherwise default to False
328- return False
329+ return name in self.required
330
331 def field_approved(self, field):
332 """Check if the field should be returned in the response based on user
333 preferences and overridden for trusted relying parties.
334 """
335- approved = set([AX_DATA_FIELDS.getNamespaceURI(f)
336- for f in self.request.POST.keys()])
337+ post = self.request.POST
338+ approved = set([k for k in post.keys() if post[k]])
339 if self.rpconfig is not None:
340+ approved.update(self.required)
341 if self.rpconfig.auto_authorize:
342- ax_fields = set(self.ax_request.requested_attributes.keys())
343- else:
344- ax_fields = set(self.ax_request.getRequiredAttrs())
345- approved.update(ax_fields)
346+ approved.update(self.optional)
347 return field in approved
348
349 @property
350 def data_approved_for_request(self):
351- """Return the list of ax data approved for the request."""
352+ """Return the list of user attributes approved for the request."""
353 return dict(
354- [(f, self.data[f]) for f in self.data
355- if self.field_approved(AX_DATA_FIELDS.getNamespaceURI(f))])
356+ [(f, self.data[f]) for f in self.data if self.field_approved(f)])
357
358 @property
359 def has_data(self):
360
361=== modified file 'identityprovider/management/commands/add_openid_rp_config.py'
362--- identityprovider/management/commands/add_openid_rp_config.py 2013-04-15 20:31:04 +0000
363+++ identityprovider/management/commands/add_openid_rp_config.py 2013-05-01 15:39:26 +0000
364@@ -10,8 +10,8 @@
365 option_list = BaseCommand.option_list + (
366 make_option('--allow-unverified', action="store_true",
367 default=False, dest='allow_unverified'),
368- make_option('--allowed-ax', action="store", dest='allowed_ax'),
369- make_option('--allowed-sreg', action="store", dest='allowed_sreg'))
370+ make_option('--allowed-user-attribs', action="store",
371+ dest='allowed_user_attribs'))
372 help = "Create OpenID RP config entry."
373
374 def handle(self, *args, **kwargs):
375@@ -20,8 +20,7 @@
376
377 trust_root = args[0]
378 allow_unverified = kwargs.get('allow_unverified', False)
379- allowed_sreg = kwargs.get('allowed_sreg', '')
380- allowed_ax = kwargs.get('allowed_ax', '')
381+ allowed_user_attribs = kwargs.get('allowed_user_attribs', '')
382 OpenIDRPConfig.objects.create(
383 trust_root=trust_root, allow_unverified=allow_unverified,
384- allowed_sreg=allowed_sreg, allowed_ax=allowed_ax)
385+ allowed_user_attribs=allowed_user_attribs)
386
387=== modified file 'identityprovider/management/commands/create_test_team.py'
388--- identityprovider/management/commands/create_test_team.py 2013-04-22 20:35:58 +0000
389+++ identityprovider/management/commands/create_test_team.py 2013-05-01 15:39:26 +0000
390@@ -23,7 +23,7 @@
391 name = 'isdtest'
392 fullname = 'ISD Test'
393 teams = ['canonical-voices', 'canonical-isd-hackers']
394- allowed_sreg = ','.join([
395+ allowed_user_attribs = ','.join([
396 'fullname', 'nickname', 'email', 'timezone',
397 'language'
398 ])
399@@ -47,7 +47,7 @@
400 defaults={
401 'displayname': 'test',
402 'auto_authorize': False,
403- 'allowed_sreg': allowed_sreg,
404+ 'allowed_user_attribs': allowed_user_attribs,
405 'allow_unverified': True,
406 }
407 )
408
409=== added file 'identityprovider/migrations/0008_add_allowed_user_attribs.py'
410--- identityprovider/migrations/0008_add_allowed_user_attribs.py 1970-01-01 00:00:00 +0000
411+++ identityprovider/migrations/0008_add_allowed_user_attribs.py 2013-05-01 15:39:26 +0000
412@@ -0,0 +1,213 @@
413+# -*- coding: utf-8 -*-
414+from south.db import db
415+from south.v2 import SchemaMigration
416+
417+
418+class Migration(SchemaMigration):
419+
420+ def forwards(self, orm):
421+ # Adding field 'OpenIDRPConfig.allowed_user_attribs'
422+ db.add_column('ssoopenidrpconfig', 'allowed_user_attribs',
423+ self.gf('django.db.models.fields.TextField')(null=True, blank=True),
424+ keep_default=False)
425+
426+
427+ def backwards(self, orm):
428+ # Deleting field 'OpenIDRPConfig.allowed_user_attribs'
429+ db.delete_column('ssoopenidrpconfig', 'allowed_user_attribs')
430+
431+
432+ models = {
433+ 'identityprovider.account': {
434+ 'Meta': {'object_name': 'Account', 'db_table': "u'account'"},
435+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {}),
436+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
437+ 'date_status_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
438+ 'displayname': ('identityprovider.models.account.DisplaynameField', [], {}),
439+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
440+ 'old_openid_identifier': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
441+ 'openid_identifier': ('django.db.models.fields.TextField', [], {'default': "u'B4xBChz'", 'unique': 'True'}),
442+ 'preferredlanguage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
443+ 'status': ('django.db.models.fields.IntegerField', [], {}),
444+ 'status_comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
445+ 'twofactor_attempts': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'null': 'True'}),
446+ 'twofactor_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
447+ 'warn_about_backup_device': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
448+ },
449+ 'identityprovider.accountpassword': {
450+ 'Meta': {'object_name': 'AccountPassword', 'db_table': "u'accountpassword'"},
451+ 'account': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['identityprovider.Account']", 'unique': 'True', 'db_column': "'account'"}),
452+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
453+ 'password': ('identityprovider.models.account.PasswordField', [], {})
454+ },
455+ 'identityprovider.apiuser': {
456+ 'Meta': {'object_name': 'APIUser', 'db_table': "'api_user'"},
457+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
458+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
459+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
460+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
461+ 'username': ('django.db.models.fields.CharField', [], {'max_length': '256'})
462+ },
463+ 'identityprovider.authenticationdevice': {
464+ 'Meta': {'ordering': "('id',)", 'object_name': 'AuthenticationDevice'},
465+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'devices'", 'to': "orm['identityprovider.Account']"}),
466+ 'counter': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
467+ 'device_type': ('django.db.models.fields.TextField', [], {'null': 'True'}),
468+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
469+ 'key': ('django.db.models.fields.TextField', [], {}),
470+ 'name': ('django.db.models.fields.TextField', [], {})
471+ },
472+ 'identityprovider.authtoken': {
473+ 'Meta': {'object_name': 'AuthToken', 'db_table': "u'authtoken'"},
474+ 'date_consumed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
475+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'db_index': 'True', 'blank': 'True'}),
476+ 'displayname': ('identityprovider.models.account.DisplaynameField', [], {'null': 'True', 'blank': 'True'}),
477+ 'email': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
478+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
479+ 'password': ('identityprovider.models.account.PasswordField', [], {'null': 'True', 'blank': 'True'}),
480+ 'redirection_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
481+ 'requester': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'requester'", 'blank': 'True'}),
482+ 'requester_email': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
483+ 'token': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
484+ 'token_type': ('django.db.models.fields.IntegerField', [], {})
485+ },
486+ 'identityprovider.emailaddress': {
487+ 'Meta': {'object_name': 'EmailAddress', 'db_table': "u'emailaddress'"},
488+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'account'", 'blank': 'True'}),
489+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
490+ 'email': ('django.db.models.fields.TextField', [], {}),
491+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
492+ 'lp_person': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'person'", 'blank': 'True'}),
493+ 'status': ('django.db.models.fields.IntegerField', [], {})
494+ },
495+ 'identityprovider.invalidatedemailaddress': {
496+ 'Meta': {'object_name': 'InvalidatedEmailAddress', 'db_table': "u'invalidated_emailaddress'"},
497+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'account'", 'blank': 'True'}),
498+ 'account_notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
499+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}),
500+ 'date_invalidated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'null': 'True', 'blank': 'True'}),
501+ 'email': ('django.db.models.fields.TextField', [], {}),
502+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
503+ },
504+ 'identityprovider.lpopenididentifier': {
505+ 'Meta': {'object_name': 'LPOpenIdIdentifier', 'db_table': "u'lp_openididentifier'"},
506+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.date.today'}),
507+ 'identifier': ('django.db.models.fields.TextField', [], {'unique': 'True', 'primary_key': 'True'}),
508+ 'lp_account': ('django.db.models.fields.IntegerField', [], {'db_column': "'account'", 'db_index': 'True'})
509+ },
510+ 'identityprovider.openidassociation': {
511+ 'Meta': {'unique_together': "(('server_url', 'handle'),)", 'object_name': 'OpenIDAssociation', 'db_table': "u'openidassociation'"},
512+ 'assoc_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
513+ 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
514+ 'issued': ('django.db.models.fields.IntegerField', [], {}),
515+ 'lifetime': ('django.db.models.fields.IntegerField', [], {}),
516+ 'secret': ('django.db.models.fields.TextField', [], {}),
517+ 'server_url': ('django.db.models.fields.CharField', [], {'max_length': '2047'})
518+ },
519+ 'identityprovider.openidauthorization': {
520+ 'Meta': {'object_name': 'OpenIDAuthorization', 'db_table': "u'openidauthorization'"},
521+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'db_column': "'account'"}),
522+ 'client_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
523+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
524+ 'date_expires': ('django.db.models.fields.DateTimeField', [], {}),
525+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
526+ 'trust_root': ('django.db.models.fields.TextField', [], {})
527+ },
528+ 'identityprovider.openidnonce': {
529+ 'Meta': {'unique_together': "(('server_url', 'timestamp', 'salt'),)", 'object_name': 'OpenIDNonce', 'db_table': "'openidnonce'"},
530+ 'salt': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
531+ 'server_url': ('django.db.models.fields.CharField', [], {'max_length': '2047', 'primary_key': 'True'}),
532+ 'timestamp': ('django.db.models.fields.IntegerField', [], {})
533+ },
534+ 'identityprovider.openidrpconfig': {
535+ 'Meta': {'object_name': 'OpenIDRPConfig', 'db_table': "'ssoopenidrpconfig'"},
536+ 'allow_unverified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
537+ 'allowed_ax': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
538+ 'allowed_sreg': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
539+ 'allowed_user_attribs': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
540+ 'auto_authorize': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
541+ 'can_query_any_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
542+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {'default': '13'}),
543+ 'description': ('django.db.models.fields.TextField', [], {}),
544+ 'displayname': ('django.db.models.fields.TextField', [], {}),
545+ 'flag_twofactor': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
546+ 'ga_snippet': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
547+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
548+ 'logo': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
549+ 'prefer_canonical_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
550+ 'require_two_factor': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
551+ 'trust_root': ('django.db.models.fields.TextField', [], {'unique': 'True'})
552+ },
553+ 'identityprovider.openidrpsummary': {
554+ 'Meta': {'unique_together': "(('account', 'trust_root', 'openid_identifier'),)", 'object_name': 'OpenIDRPSummary', 'db_table': "u'openidrpsummary'"},
555+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'db_column': "'account'"}),
556+ 'approved_data': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
557+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
558+ 'date_last_used': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
559+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
560+ 'openid_identifier': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
561+ 'total_logins': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
562+ 'trust_root': ('django.db.models.fields.TextField', [], {'db_index': 'True'})
563+ },
564+ 'identityprovider.person': {
565+ 'Meta': {'object_name': 'Person', 'db_table': "u'lp_person'"},
566+ 'addressline1': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
567+ 'addressline2': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
568+ 'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
569+ 'country': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'country'", 'blank': 'True'}),
570+ 'creation_comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
571+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
572+ 'datecreated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
573+ 'defaultmembershipperiod': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
574+ 'defaultrenewalperiod': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
575+ 'displayname': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
576+ 'fti': ('django.db.models.fields.TextField', [], {'null': 'True'}),
577+ 'hide_email_addresses': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
578+ 'homepage_content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
579+ 'icon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'icon'", 'blank': 'True'}),
580+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
581+ 'language': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'language'", 'blank': 'True'}),
582+ 'logo': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'logo'", 'blank': 'True'}),
583+ 'lp_account': ('django.db.models.fields.IntegerField', [], {'unique': 'True', 'null': 'True', 'db_column': "'account'"}),
584+ 'mail_resumption_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
585+ 'mailing_list_auto_subscribe_policy': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
586+ 'mailing_list_receive_duplicates': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
587+ 'merged': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'merged'", 'blank': 'True'}),
588+ 'mugshot': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'mugshot'", 'blank': 'True'}),
589+ 'name': ('django.db.models.fields.TextField', [], {'unique': 'True', 'null': 'True'}),
590+ 'organization': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
591+ 'personal_standing': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
592+ 'personal_standing_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
593+ 'phone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
594+ 'postcode': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
595+ 'province': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
596+ 'registrant': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'registrant'", 'blank': 'True'}),
597+ 'renewal_policy': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True'}),
598+ 'subscriptionpolicy': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
599+ 'teamdescription': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
600+ 'teamowner': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'teamowner'", 'blank': 'True'}),
601+ 'verbose_bugnotifications': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
602+ 'visibility': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'})
603+ },
604+ 'identityprovider.personlocation': {
605+ 'Meta': {'object_name': 'PersonLocation', 'db_table': "u'lp_personlocation'"},
606+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
607+ 'date_last_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
608+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
609+ 'last_modified_by': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'last_modified_by'"}),
610+ 'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
611+ 'locked': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
612+ 'longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
613+ 'person': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['identityprovider.Person']", 'unique': 'True', 'null': 'True', 'db_column': "'person'"}),
614+ 'time_zone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
615+ 'visible': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'})
616+ },
617+ 'identityprovider.teamparticipation': {
618+ 'Meta': {'unique_together': "(('team', 'person'),)", 'object_name': 'TeamParticipation', 'db_table': "u'lp_teamparticipation'"},
619+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
620+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Person']", 'null': 'True', 'db_column': "'person'"}),
621+ 'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'team_participations'", 'null': 'True', 'db_column': "'team'", 'to': "orm['identityprovider.Person']"})
622+ }
623+ }
624+
625+ complete_apps = ['identityprovider']
626
627=== added file 'identityprovider/migrations/0009_unify_allowed_sreg_ax.py'
628--- identityprovider/migrations/0009_unify_allowed_sreg_ax.py 1970-01-01 00:00:00 +0000
629+++ identityprovider/migrations/0009_unify_allowed_sreg_ax.py 2013-05-01 15:39:26 +0000
630@@ -0,0 +1,292 @@
631+# -*- coding: utf-8 -*-
632+import json
633+import logging
634+
635+from django.db.models import Q
636+
637+from south.v2 import DataMigration
638+
639+class Migration(DataMigration):
640+
641+ def forwards(self, orm):
642+ "Write your forwards methods here."
643+ # Merge OpenIDRPConfig.allowed_ax and OpenIDRPConfg.allowed_sreg
644+ # So we take the value of both columns, create a set and populate the
645+ # new column.
646+ rpconfig_model = orm['identityprovider.OpenIDRPConfig']
647+ rpconfigs = rpconfig_model.objects.filter(
648+ Q(allowed_ax__isnull=False) | Q(allowed_sreg__isnull=False))
649+ for rpconfig in rpconfigs:
650+ allowed_ax = rpconfig.allowed_ax
651+ allowed_ax = allowed_ax.split(',') if allowed_ax else []
652+ allowed_sreg = rpconfig.allowed_sreg
653+ allowed_sreg = allowed_sreg.split(',') if allowed_sreg else []
654+ allowed_user_attribs = set(allowed_ax + allowed_sreg)
655+ rpconfig.allowed_user_attribs = ','.join(allowed_user_attribs)
656+ rpconfig.save()
657+ # And now deal with users' previous (dis)approvals
658+ summary_model = orm['identityprovider.OpenIDRPSummary']
659+ summaries = summary_model.objects.filter(approved_data__isnull=False)
660+ for summary in summaries:
661+ try:
662+ approved_data = json.loads(summary.approved_data)
663+ ax_data = approved_data.get('ax', {})
664+ requested_ax = set(ax_data.get('requested', []))
665+ approved_ax = set(ax_data.get('approved', []))
666+ sreg_data = approved_data.get('sreg', {})
667+ requested_sreg = set(sreg_data.get('requested', []))
668+ approved_sreg = set(sreg_data.get('approved', []))
669+ approved_data.pop('ax', None)
670+ approved_data.pop('sreg', None)
671+ approved_data['user_attribs'] = {
672+ 'requested': list(requested_ax | requested_sreg),
673+ 'approved': list(approved_ax | approved_sreg),
674+ }
675+ summary.approved_data = json.dumps(approved_data)
676+ summary.save()
677+ except ValueError:
678+ logging.warning(
679+ '[OpenIDRPSummary Migration] '
680+ 'Could not parse approved_data for OpenIDRPSummary id %d, '
681+ 'summary not migrated', summary.id)
682+
683+ def backwards(self, orm):
684+ "Write your backwards methods here."
685+ # So we take the value of the old column and copy it into the two new
686+ # columns, taking care to filter account_verified from allowed_sreg.
687+ rpconfig_model = orm['identityprovider.OpenIDRPConfig']
688+ rpconfigs = rpconfig_model.objects.filter(
689+ allowed_user_attribs__isnull=False)
690+ for rpconfig in rpconfigs:
691+ allowed_user_attribs = rpconfig.allowed_user_attribs
692+ allowed_user_attribs = (
693+ allowed_user_attribs.split(',') if allowed_user_attribs
694+ else [])
695+ allowed_sreg = [i for i in allowed_user_attribs
696+ if i != 'account_verified']
697+ rpconfig.allowed_sreg = ','.join(allowed_sreg)
698+ rpconfig.allowed_ax = ','.join(allowed_user_attribs)
699+ rpconfig.save()
700+ # And new the user's previous (dis)approvals
701+ summary_model = orm['identityprovider.OpenIDRPSummary']
702+ summaries = summary_model.objects.filter(approved_data__isnull=False)
703+ for summary in summaries:
704+ try:
705+ approved_data = json.loads(summary.approved_data)
706+ user_attribs_data = approved_data.get('user_attribs', {})
707+ requested_user_attribs = user_attribs_data.get('requested', [])
708+ approved_user_attribs = user_attribs_data.get('approved', [])
709+ approved_data.pop('user_attribs', None)
710+ approved_data['ax'] = {
711+ 'requested': requested_user_attribs,
712+ 'approved': approved_user_attribs,
713+ }
714+ approved_data['sreg'] = {
715+ 'requested': [a for a in requested_user_attribs
716+ if a != 'account_verified'],
717+ 'approved': [a for a in approved_user_attribs
718+ if a != 'account_verified'],
719+ }
720+ summary.approved_data = json.dumps(approved_data)
721+ summary.save()
722+ except ValueError:
723+ logging.warning(
724+ '[OpenIDRPSummary Migration] '
725+ 'Could not parse approved_data for OpenIDRPSummary id %d, '
726+ 'summary not migrated', summary.id)
727+
728+ models = {
729+ 'identityprovider.account': {
730+ 'Meta': {'object_name': 'Account', 'db_table': "u'account'"},
731+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {}),
732+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
733+ 'date_status_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
734+ 'displayname': ('identityprovider.models.account.DisplaynameField', [], {}),
735+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
736+ 'old_openid_identifier': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
737+ 'openid_identifier': ('django.db.models.fields.TextField', [], {'default': "u'PNywzmm'", 'unique': 'True'}),
738+ 'preferredlanguage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
739+ 'status': ('django.db.models.fields.IntegerField', [], {}),
740+ 'status_comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
741+ 'twofactor_attempts': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'null': 'True'}),
742+ 'twofactor_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
743+ 'warn_about_backup_device': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
744+ },
745+ 'identityprovider.accountpassword': {
746+ 'Meta': {'object_name': 'AccountPassword', 'db_table': "u'accountpassword'"},
747+ 'account': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['identityprovider.Account']", 'unique': 'True', 'db_column': "'account'"}),
748+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
749+ 'password': ('identityprovider.models.account.PasswordField', [], {})
750+ },
751+ 'identityprovider.apiuser': {
752+ 'Meta': {'object_name': 'APIUser', 'db_table': "'api_user'"},
753+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
754+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
755+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
756+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
757+ 'username': ('django.db.models.fields.CharField', [], {'max_length': '256'})
758+ },
759+ 'identityprovider.authenticationdevice': {
760+ 'Meta': {'ordering': "('id',)", 'object_name': 'AuthenticationDevice'},
761+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'devices'", 'to': "orm['identityprovider.Account']"}),
762+ 'counter': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
763+ 'device_type': ('django.db.models.fields.TextField', [], {'null': 'True'}),
764+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
765+ 'key': ('django.db.models.fields.TextField', [], {}),
766+ 'name': ('django.db.models.fields.TextField', [], {})
767+ },
768+ 'identityprovider.authtoken': {
769+ 'Meta': {'object_name': 'AuthToken', 'db_table': "u'authtoken'"},
770+ 'date_consumed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
771+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'db_index': 'True', 'blank': 'True'}),
772+ 'displayname': ('identityprovider.models.account.DisplaynameField', [], {'null': 'True', 'blank': 'True'}),
773+ 'email': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
774+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
775+ 'password': ('identityprovider.models.account.PasswordField', [], {'null': 'True', 'blank': 'True'}),
776+ 'redirection_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
777+ 'requester': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'requester'", 'blank': 'True'}),
778+ 'requester_email': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
779+ 'token': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
780+ 'token_type': ('django.db.models.fields.IntegerField', [], {})
781+ },
782+ 'identityprovider.emailaddress': {
783+ 'Meta': {'object_name': 'EmailAddress', 'db_table': "u'emailaddress'"},
784+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'account'", 'blank': 'True'}),
785+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
786+ 'email': ('django.db.models.fields.TextField', [], {}),
787+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
788+ 'lp_person': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'person'", 'blank': 'True'}),
789+ 'status': ('django.db.models.fields.IntegerField', [], {})
790+ },
791+ 'identityprovider.invalidatedemailaddress': {
792+ 'Meta': {'object_name': 'InvalidatedEmailAddress', 'db_table': "u'invalidated_emailaddress'"},
793+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'account'", 'blank': 'True'}),
794+ 'account_notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
795+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}),
796+ 'date_invalidated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'null': 'True', 'blank': 'True'}),
797+ 'email': ('django.db.models.fields.TextField', [], {}),
798+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
799+ },
800+ 'identityprovider.lpopenididentifier': {
801+ 'Meta': {'object_name': 'LPOpenIdIdentifier', 'db_table': "u'lp_openididentifier'"},
802+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.date.today'}),
803+ 'identifier': ('django.db.models.fields.TextField', [], {'unique': 'True', 'primary_key': 'True'}),
804+ 'lp_account': ('django.db.models.fields.IntegerField', [], {'db_column': "'account'", 'db_index': 'True'})
805+ },
806+ 'identityprovider.openidassociation': {
807+ 'Meta': {'unique_together': "(('server_url', 'handle'),)", 'object_name': 'OpenIDAssociation', 'db_table': "u'openidassociation'"},
808+ 'assoc_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
809+ 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
810+ 'issued': ('django.db.models.fields.IntegerField', [], {}),
811+ 'lifetime': ('django.db.models.fields.IntegerField', [], {}),
812+ 'secret': ('django.db.models.fields.TextField', [], {}),
813+ 'server_url': ('django.db.models.fields.CharField', [], {'max_length': '2047'})
814+ },
815+ 'identityprovider.openidauthorization': {
816+ 'Meta': {'object_name': 'OpenIDAuthorization', 'db_table': "u'openidauthorization'"},
817+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'db_column': "'account'"}),
818+ 'client_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
819+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
820+ 'date_expires': ('django.db.models.fields.DateTimeField', [], {}),
821+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
822+ 'trust_root': ('django.db.models.fields.TextField', [], {})
823+ },
824+ 'identityprovider.openidnonce': {
825+ 'Meta': {'unique_together': "(('server_url', 'timestamp', 'salt'),)", 'object_name': 'OpenIDNonce', 'db_table': "'openidnonce'"},
826+ 'salt': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
827+ 'server_url': ('django.db.models.fields.CharField', [], {'max_length': '2047', 'primary_key': 'True'}),
828+ 'timestamp': ('django.db.models.fields.IntegerField', [], {})
829+ },
830+ 'identityprovider.openidrpconfig': {
831+ 'Meta': {'object_name': 'OpenIDRPConfig', 'db_table': "'ssoopenidrpconfig'"},
832+ 'allow_unverified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
833+ 'allowed_ax': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
834+ 'allowed_sreg': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
835+ 'allowed_user_attribs': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
836+ 'auto_authorize': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
837+ 'can_query_any_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
838+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {'default': '13'}),
839+ 'description': ('django.db.models.fields.TextField', [], {}),
840+ 'displayname': ('django.db.models.fields.TextField', [], {}),
841+ 'flag_twofactor': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
842+ 'ga_snippet': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
843+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
844+ 'logo': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
845+ 'prefer_canonical_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
846+ 'require_two_factor': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
847+ 'trust_root': ('django.db.models.fields.TextField', [], {'unique': 'True'})
848+ },
849+ 'identityprovider.openidrpsummary': {
850+ 'Meta': {'unique_together': "(('account', 'trust_root', 'openid_identifier'),)", 'object_name': 'OpenIDRPSummary', 'db_table': "u'openidrpsummary'"},
851+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'db_column': "'account'"}),
852+ 'approved_data': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
853+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
854+ 'date_last_used': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
855+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
856+ 'openid_identifier': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
857+ 'total_logins': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
858+ 'trust_root': ('django.db.models.fields.TextField', [], {'db_index': 'True'})
859+ },
860+ 'identityprovider.person': {
861+ 'Meta': {'object_name': 'Person', 'db_table': "u'lp_person'"},
862+ 'addressline1': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
863+ 'addressline2': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
864+ 'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
865+ 'country': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'country'", 'blank': 'True'}),
866+ 'creation_comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
867+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
868+ 'datecreated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
869+ 'defaultmembershipperiod': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
870+ 'defaultrenewalperiod': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
871+ 'displayname': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
872+ 'fti': ('django.db.models.fields.TextField', [], {'null': 'True'}),
873+ 'hide_email_addresses': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
874+ 'homepage_content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
875+ 'icon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'icon'", 'blank': 'True'}),
876+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
877+ 'language': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'language'", 'blank': 'True'}),
878+ 'logo': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'logo'", 'blank': 'True'}),
879+ 'lp_account': ('django.db.models.fields.IntegerField', [], {'unique': 'True', 'null': 'True', 'db_column': "'account'"}),
880+ 'mail_resumption_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
881+ 'mailing_list_auto_subscribe_policy': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
882+ 'mailing_list_receive_duplicates': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
883+ 'merged': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'merged'", 'blank': 'True'}),
884+ 'mugshot': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'mugshot'", 'blank': 'True'}),
885+ 'name': ('django.db.models.fields.TextField', [], {'unique': 'True', 'null': 'True'}),
886+ 'organization': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
887+ 'personal_standing': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
888+ 'personal_standing_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
889+ 'phone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
890+ 'postcode': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
891+ 'province': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
892+ 'registrant': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'registrant'", 'blank': 'True'}),
893+ 'renewal_policy': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True'}),
894+ 'subscriptionpolicy': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
895+ 'teamdescription': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
896+ 'teamowner': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'teamowner'", 'blank': 'True'}),
897+ 'verbose_bugnotifications': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
898+ 'visibility': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'})
899+ },
900+ 'identityprovider.personlocation': {
901+ 'Meta': {'object_name': 'PersonLocation', 'db_table': "u'lp_personlocation'"},
902+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
903+ 'date_last_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
904+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
905+ 'last_modified_by': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'last_modified_by'"}),
906+ 'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
907+ 'locked': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
908+ 'longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
909+ 'person': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['identityprovider.Person']", 'unique': 'True', 'null': 'True', 'db_column': "'person'"}),
910+ 'time_zone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
911+ 'visible': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'})
912+ },
913+ 'identityprovider.teamparticipation': {
914+ 'Meta': {'unique_together': "(('team', 'person'),)", 'object_name': 'TeamParticipation', 'db_table': "u'lp_teamparticipation'"},
915+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
916+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Person']", 'null': 'True', 'db_column': "'person'"}),
917+ 'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'team_participations'", 'null': 'True', 'db_column': "'team'", 'to': "orm['identityprovider.Person']"})
918+ }
919+ }
920+
921+ complete_apps = ['identityprovider']
922+ symmetrical = True
923
924=== added file 'identityprovider/migrations/0010_remove_allowed_sreg_ax.py'
925--- identityprovider/migrations/0010_remove_allowed_sreg_ax.py 1970-01-01 00:00:00 +0000
926+++ identityprovider/migrations/0010_remove_allowed_sreg_ax.py 2013-05-01 15:39:26 +0000
927@@ -0,0 +1,219 @@
928+# -*- coding: utf-8 -*-
929+from south.db import db
930+from south.v2 import SchemaMigration
931+
932+
933+class Migration(SchemaMigration):
934+
935+ def forwards(self, orm):
936+ # Deleting field 'OpenIDRPConfig.allowed_ax'
937+ db.delete_column('ssoopenidrpconfig', 'allowed_ax')
938+
939+ # Deleting field 'OpenIDRPConfig.allowed_sreg'
940+ db.delete_column('ssoopenidrpconfig', 'allowed_sreg')
941+
942+
943+ def backwards(self, orm):
944+ # Adding field 'OpenIDRPConfig.allowed_ax'
945+ db.add_column('ssoopenidrpconfig', 'allowed_ax',
946+ self.gf('django.db.models.fields.TextField')(null=True, blank=True),
947+ keep_default=False)
948+
949+ # Adding field 'OpenIDRPConfig.allowed_sreg'
950+ db.add_column('ssoopenidrpconfig', 'allowed_sreg',
951+ self.gf('django.db.models.fields.TextField')(null=True, blank=True),
952+ keep_default=False)
953+
954+
955+ models = {
956+ 'identityprovider.account': {
957+ 'Meta': {'object_name': 'Account', 'db_table': "u'account'"},
958+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {}),
959+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
960+ 'date_status_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}),
961+ 'displayname': ('identityprovider.models.account.DisplaynameField', [], {}),
962+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
963+ 'old_openid_identifier': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
964+ 'openid_identifier': ('django.db.models.fields.TextField', [], {'default': "u'BDreFt7'", 'unique': 'True'}),
965+ 'preferredlanguage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
966+ 'status': ('django.db.models.fields.IntegerField', [], {}),
967+ 'status_comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
968+ 'twofactor_attempts': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'null': 'True'}),
969+ 'twofactor_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
970+ 'warn_about_backup_device': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
971+ },
972+ 'identityprovider.accountpassword': {
973+ 'Meta': {'object_name': 'AccountPassword', 'db_table': "u'accountpassword'"},
974+ 'account': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['identityprovider.Account']", 'unique': 'True', 'db_column': "'account'"}),
975+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
976+ 'password': ('identityprovider.models.account.PasswordField', [], {})
977+ },
978+ 'identityprovider.apiuser': {
979+ 'Meta': {'object_name': 'APIUser', 'db_table': "'api_user'"},
980+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
981+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
982+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
983+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
984+ 'username': ('django.db.models.fields.CharField', [], {'max_length': '256'})
985+ },
986+ 'identityprovider.authenticationdevice': {
987+ 'Meta': {'ordering': "('id',)", 'object_name': 'AuthenticationDevice'},
988+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'devices'", 'to': "orm['identityprovider.Account']"}),
989+ 'counter': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
990+ 'device_type': ('django.db.models.fields.TextField', [], {'null': 'True'}),
991+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
992+ 'key': ('django.db.models.fields.TextField', [], {}),
993+ 'name': ('django.db.models.fields.TextField', [], {})
994+ },
995+ 'identityprovider.authtoken': {
996+ 'Meta': {'object_name': 'AuthToken', 'db_table': "u'authtoken'"},
997+ 'date_consumed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
998+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'db_index': 'True', 'blank': 'True'}),
999+ 'displayname': ('identityprovider.models.account.DisplaynameField', [], {'null': 'True', 'blank': 'True'}),
1000+ 'email': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
1001+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1002+ 'password': ('identityprovider.models.account.PasswordField', [], {'null': 'True', 'blank': 'True'}),
1003+ 'redirection_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1004+ 'requester': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'requester'", 'blank': 'True'}),
1005+ 'requester_email': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1006+ 'token': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
1007+ 'token_type': ('django.db.models.fields.IntegerField', [], {})
1008+ },
1009+ 'identityprovider.emailaddress': {
1010+ 'Meta': {'object_name': 'EmailAddress', 'db_table': "u'emailaddress'"},
1011+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'account'", 'blank': 'True'}),
1012+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
1013+ 'email': ('django.db.models.fields.TextField', [], {}),
1014+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1015+ 'lp_person': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'person'", 'blank': 'True'}),
1016+ 'status': ('django.db.models.fields.IntegerField', [], {})
1017+ },
1018+ 'identityprovider.invalidatedemailaddress': {
1019+ 'Meta': {'object_name': 'InvalidatedEmailAddress', 'db_table': "u'invalidated_emailaddress'"},
1020+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'null': 'True', 'db_column': "'account'", 'blank': 'True'}),
1021+ 'account_notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1022+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}),
1023+ 'date_invalidated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'null': 'True', 'blank': 'True'}),
1024+ 'email': ('django.db.models.fields.TextField', [], {}),
1025+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
1026+ },
1027+ 'identityprovider.lpopenididentifier': {
1028+ 'Meta': {'object_name': 'LPOpenIdIdentifier', 'db_table': "u'lp_openididentifier'"},
1029+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.date.today'}),
1030+ 'identifier': ('django.db.models.fields.TextField', [], {'unique': 'True', 'primary_key': 'True'}),
1031+ 'lp_account': ('django.db.models.fields.IntegerField', [], {'db_column': "'account'", 'db_index': 'True'})
1032+ },
1033+ 'identityprovider.openidassociation': {
1034+ 'Meta': {'unique_together': "(('server_url', 'handle'),)", 'object_name': 'OpenIDAssociation', 'db_table': "u'openidassociation'"},
1035+ 'assoc_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
1036+ 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
1037+ 'issued': ('django.db.models.fields.IntegerField', [], {}),
1038+ 'lifetime': ('django.db.models.fields.IntegerField', [], {}),
1039+ 'secret': ('django.db.models.fields.TextField', [], {}),
1040+ 'server_url': ('django.db.models.fields.CharField', [], {'max_length': '2047'})
1041+ },
1042+ 'identityprovider.openidauthorization': {
1043+ 'Meta': {'object_name': 'OpenIDAuthorization', 'db_table': "u'openidauthorization'"},
1044+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'db_column': "'account'"}),
1045+ 'client_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1046+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
1047+ 'date_expires': ('django.db.models.fields.DateTimeField', [], {}),
1048+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1049+ 'trust_root': ('django.db.models.fields.TextField', [], {})
1050+ },
1051+ 'identityprovider.openidnonce': {
1052+ 'Meta': {'unique_together': "(('server_url', 'timestamp', 'salt'),)", 'object_name': 'OpenIDNonce', 'db_table': "'openidnonce'"},
1053+ 'salt': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
1054+ 'server_url': ('django.db.models.fields.CharField', [], {'max_length': '2047', 'primary_key': 'True'}),
1055+ 'timestamp': ('django.db.models.fields.IntegerField', [], {})
1056+ },
1057+ 'identityprovider.openidrpconfig': {
1058+ 'Meta': {'object_name': 'OpenIDRPConfig', 'db_table': "'ssoopenidrpconfig'"},
1059+ 'allow_unverified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1060+ 'allowed_user_attribs': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1061+ 'auto_authorize': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1062+ 'can_query_any_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1063+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {'default': '13'}),
1064+ 'description': ('django.db.models.fields.TextField', [], {}),
1065+ 'displayname': ('django.db.models.fields.TextField', [], {}),
1066+ 'flag_twofactor': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
1067+ 'ga_snippet': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1068+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1069+ 'logo': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1070+ 'prefer_canonical_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1071+ 'require_two_factor': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1072+ 'trust_root': ('django.db.models.fields.TextField', [], {'unique': 'True'})
1073+ },
1074+ 'identityprovider.openidrpsummary': {
1075+ 'Meta': {'unique_together': "(('account', 'trust_root', 'openid_identifier'),)", 'object_name': 'OpenIDRPSummary', 'db_table': "u'openidrpsummary'"},
1076+ 'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Account']", 'db_column': "'account'"}),
1077+ 'approved_data': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
1078+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
1079+ 'date_last_used': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'blank': 'True'}),
1080+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1081+ 'openid_identifier': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
1082+ 'total_logins': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
1083+ 'trust_root': ('django.db.models.fields.TextField', [], {'db_index': 'True'})
1084+ },
1085+ 'identityprovider.person': {
1086+ 'Meta': {'object_name': 'Person', 'db_table': "u'lp_person'"},
1087+ 'addressline1': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1088+ 'addressline2': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1089+ 'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1090+ 'country': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'country'", 'blank': 'True'}),
1091+ 'creation_comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1092+ 'creation_rationale': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
1093+ 'datecreated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
1094+ 'defaultmembershipperiod': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
1095+ 'defaultrenewalperiod': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
1096+ 'displayname': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1097+ 'fti': ('django.db.models.fields.TextField', [], {'null': 'True'}),
1098+ 'hide_email_addresses': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
1099+ 'homepage_content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1100+ 'icon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'icon'", 'blank': 'True'}),
1101+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1102+ 'language': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'language'", 'blank': 'True'}),
1103+ 'logo': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'logo'", 'blank': 'True'}),
1104+ 'lp_account': ('django.db.models.fields.IntegerField', [], {'unique': 'True', 'null': 'True', 'db_column': "'account'"}),
1105+ 'mail_resumption_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
1106+ 'mailing_list_auto_subscribe_policy': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
1107+ 'mailing_list_receive_duplicates': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
1108+ 'merged': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'merged'", 'blank': 'True'}),
1109+ 'mugshot': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'mugshot'", 'blank': 'True'}),
1110+ 'name': ('django.db.models.fields.TextField', [], {'unique': 'True', 'null': 'True'}),
1111+ 'organization': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1112+ 'personal_standing': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
1113+ 'personal_standing_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1114+ 'phone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1115+ 'postcode': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1116+ 'province': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1117+ 'registrant': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'registrant'", 'blank': 'True'}),
1118+ 'renewal_policy': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True'}),
1119+ 'subscriptionpolicy': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
1120+ 'teamdescription': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1121+ 'teamowner': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'teamowner'", 'blank': 'True'}),
1122+ 'verbose_bugnotifications': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
1123+ 'visibility': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'})
1124+ },
1125+ 'identityprovider.personlocation': {
1126+ 'Meta': {'object_name': 'PersonLocation', 'db_table': "u'lp_personlocation'"},
1127+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
1128+ 'date_last_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
1129+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1130+ 'last_modified_by': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'last_modified_by'"}),
1131+ 'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
1132+ 'locked': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
1133+ 'longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
1134+ 'person': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['identityprovider.Person']", 'unique': 'True', 'null': 'True', 'db_column': "'person'"}),
1135+ 'time_zone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1136+ 'visible': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'})
1137+ },
1138+ 'identityprovider.teamparticipation': {
1139+ 'Meta': {'unique_together': "(('team', 'person'),)", 'object_name': 'TeamParticipation', 'db_table': "u'lp_teamparticipation'"},
1140+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1141+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['identityprovider.Person']", 'null': 'True', 'db_column': "'person'"}),
1142+ 'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'team_participations'", 'null': 'True', 'db_column': "'team'", 'to': "orm['identityprovider.Person']"})
1143+ }
1144+ }
1145+
1146+ complete_apps = ['identityprovider']
1147
1148=== modified file 'identityprovider/models/openidmodels.py'
1149--- identityprovider/models/openidmodels.py 2013-03-20 17:37:36 +0000
1150+++ identityprovider/models/openidmodels.py 2013-05-01 15:39:26 +0000
1151@@ -154,8 +154,7 @@
1152 displayname = models.TextField()
1153 description = models.TextField()
1154 logo = models.TextField(blank=True, null=True)
1155- allowed_ax = models.TextField(blank=True, null=True)
1156- allowed_sreg = models.TextField(blank=True, null=True)
1157+ allowed_user_attribs = models.TextField(blank=True, null=True)
1158 creation_rationale = models.IntegerField(
1159 default=13, choices=AccountCreationRationale._get_choices())
1160 can_query_any_team = models.BooleanField(default=False)
1161
1162=== modified file 'identityprovider/templates/server/decide.html'
1163--- identityprovider/templates/server/decide.html 2013-04-02 23:01:33 +0000
1164+++ identityprovider/templates/server/decide.html 2013-05-01 15:39:26 +0000
1165@@ -31,18 +31,12 @@
1166 <form action="{{ action }}" method="POST" name="decideform">
1167 {% csrf_token %}
1168 <div class="info-items">
1169- {% if sreg_form.has_data or teams_form.has_data or ax_form.has_data %}
1170+ {% if user_attribs_form.has_data or teams_form.has_data %}
1171 <ul class="list">
1172- {% if ax_form.has_data %}
1173- {% for field in ax_form %}
1174- <li class="ax">{{ field|safe }} {{ field.label_tag }}</li>
1175+ {% if user_attribs_form.has_data %}
1176+ {% for field in user_attribs_form %}
1177+ <li class="user_attribs">{{ field|safe }} {{ field.label_tag }}</li>
1178 {% endfor %}
1179- {% else %}
1180- {% if sreg_form.has_data %}
1181- {% for field in sreg_form %}
1182- <li class="sreg">{{ field|safe }} {{ field.label_tag }}</li>
1183- {% endfor %}
1184- {% endif %}
1185 {% endif %}
1186 {% if teams_form.has_data %}
1187 {% ifequal teams_form.fields|length 1 %}
1188
1189=== modified file 'identityprovider/tests/openid_server/per_version/test_restricted_sreg.py'
1190--- identityprovider/tests/openid_server/per_version/test_restricted_sreg.py 2013-04-01 15:24:48 +0000
1191+++ identityprovider/tests/openid_server/per_version/test_restricted_sreg.py 2013-05-01 15:39:26 +0000
1192@@ -89,10 +89,11 @@
1193 # If we create a Relying Party configuration for the trust root, things
1194 # play out a bit differently:
1195
1196- allowed_sreg = ','.join(['fullname', 'nickname', 'email', 'timezone'])
1197+ allowed_user_attribs = ','.join(['fullname', 'nickname',
1198+ 'email', 'timezone'])
1199 self.create_openid_rp_config(
1200 trust_root=self.consumer_url,
1201- allowed_sreg=allowed_sreg)
1202+ allowed_user_attribs=allowed_user_attribs)
1203
1204 # Now begin another identical OpenID authentication request:
1205 response = self.initial_dance()
1206@@ -125,10 +126,10 @@
1207 # == Behaviour for known trust roots with auto_authorize ==
1208
1209 # enable auto-authorize for the rpconfig
1210- allowed_sreg = ','.join(['fullname', 'email', 'timezone'])
1211+ allowed_user_attribs = ','.join(['fullname', 'email', 'timezone'])
1212 self.create_openid_rp_config(
1213 trust_root=self.consumer_url,
1214- allowed_sreg=allowed_sreg, auto_authorize=True)
1215+ allowed_user_attribs=allowed_user_attribs, auto_authorize=True)
1216 # Now begin another identical OpenID authentication request:
1217 response = self.initial_dance()
1218
1219
1220=== modified file 'identityprovider/tests/test_admin.py'
1221--- identityprovider/tests/test_admin.py 2013-04-02 16:24:29 +0000
1222+++ identityprovider/tests/test_admin.py 2013-05-01 15:39:26 +0000
1223@@ -52,17 +52,17 @@
1224 for model in (AccountPassword, Person, LPOpenIdIdentifier):
1225 self.assertRaises(NotRegistered, admin.site.unregister, model)
1226
1227- def test_openidrpconfig_allowed_sreg_checkboxes_postable(self):
1228+ def test_openidrpconfig_allowed_user_attribs_checkboxes_postable(self):
1229 trust_root = 'http://localhost/bla/'
1230 displayname = 'My Test RP'
1231 description = 'Bla'
1232- allowed_sreg = ['fullname', 'email']
1233+ allowed_user_attribs = ['fullname', 'email']
1234 creation_rationale = 13
1235
1236 data = {'trust_root': trust_root,
1237 'displayname': displayname,
1238 'description': description,
1239- 'allowed_sreg': allowed_sreg,
1240+ 'allowed_user_attribs': allowed_user_attribs,
1241 'creation_rationale': creation_rationale,
1242 }
1243 add_view = reverse('admin:identityprovider_openidrpconfig_add')
1244@@ -77,15 +77,15 @@
1245 self.assertEqual(rpconfig.trust_root, trust_root)
1246 self.assertEqual(rpconfig.displayname, displayname)
1247 self.assertEqual(rpconfig.description, description)
1248- self.assertEqual(sorted(rpconfig.allowed_sreg.split(',')),
1249- sorted(allowed_sreg))
1250+ self.assertEqual(sorted(rpconfig.allowed_user_attribs.split(',')),
1251+ sorted(allowed_user_attribs))
1252 self.assertEqual(rpconfig.creation_rationale, creation_rationale)
1253
1254- def test_openidrpconfig_allowed_sreg_checkboxes_getable(self):
1255+ def test_openidrpconfig_allowed_user_attribs_checkboxes_getable(self):
1256 data = {'trust_root': 'http://localhost/bla/',
1257 'displayname': 'My Test RP',
1258 'description': 'Bla',
1259- 'allowed_sreg': 'fullname',
1260+ 'allowed_user_attribs': 'fullname',
1261 'creation_rationale': '13',
1262 }
1263 rpconfig = OpenIDRPConfig.objects.create(**data)
1264@@ -98,53 +98,6 @@
1265 self.assertEqual(len(checked), 1)
1266 self.assertEqual(checked[0].value, 'fullname')
1267
1268- def test_openidrpconfig_allowed_ax_checkboxes_postable(self):
1269- trust_root = 'http://localhost/bla/'
1270- displayname = 'My Test RP'
1271- description = 'Bla'
1272- allowed_ax = ['fullname', 'email']
1273- creation_rationale = 13
1274-
1275- data = {'trust_root': trust_root,
1276- 'displayname': displayname,
1277- 'description': description,
1278- 'allowed_ax': allowed_ax,
1279- 'creation_rationale': creation_rationale,
1280- }
1281- add_view = reverse('admin:identityprovider_openidrpconfig_add')
1282- response = self.client.get(add_view)
1283- response = self.client.post(add_view, data)
1284- self.assertEqual(302, response.status_code)
1285- # We don't get the ID back, so ensure we only have one entity and
1286- # assume it's the correct one. This is racy, but the alternative is
1287- # another request to the list screen to scrape the ID from there.
1288- self.assertEqual(OpenIDRPConfig.objects.count(), 1)
1289- rpconfig = OpenIDRPConfig.objects.get()
1290- self.assertEqual(rpconfig.trust_root, trust_root)
1291- self.assertEqual(rpconfig.displayname, displayname)
1292- self.assertEqual(rpconfig.description, description)
1293- self.assertEqual(sorted(rpconfig.allowed_ax.split(',')),
1294- sorted(allowed_ax))
1295- self.assertEqual(rpconfig.creation_rationale, creation_rationale)
1296-
1297- def test_openidrpconfig_allowed_ax_checkboxes_getable(self):
1298- data = {'trust_root': 'http://localhost/bla/',
1299- 'displayname': 'My Test RP',
1300- 'description': 'Bla',
1301- 'allowed_ax': 'email',
1302- 'creation_rationale': '13',
1303- }
1304- rpconfig = OpenIDRPConfig(**data)
1305- rpconfig.save()
1306- change_view = reverse(
1307- 'admin:identityprovider_openidrpconfig_change',
1308- args=(rpconfig.id,))
1309- response = self.client.get(change_view)
1310- dom = PyQuery(response.content)
1311- checked = dom.find('input[checked=checked]')
1312- self.assertEqual(len(checked), 1)
1313- self.assertEqual(checked[0].value, 'email')
1314-
1315 def test_inline_models(self):
1316 expected_inlines = [AccountPasswordInline, EmailAddressInline,
1317 AuthenticationDeviceInline]
1318
1319=== modified file 'identityprovider/tests/test_command_add_openid_rp_config.py'
1320--- identityprovider/tests/test_command_add_openid_rp_config.py 2013-04-15 20:43:43 +0000
1321+++ identityprovider/tests/test_command_add_openid_rp_config.py 2013-05-01 15:39:26 +0000
1322@@ -26,7 +26,7 @@
1323 config = get_object_or_none(OpenIDRPConfig, trust_root=root)
1324 self.assertIsNotNone(config)
1325 self.assertFalse(config.allow_unverified)
1326- self.assertEqual(config.allowed_sreg, '')
1327+ self.assertEqual(config.allowed_user_attribs, '')
1328
1329 def test_add_with_allow_unverified(self):
1330 root = 'http://localhost:8000'
1331@@ -35,31 +35,12 @@
1332 self.assertIsNotNone(config)
1333 self.assertTrue(config.allow_unverified)
1334
1335- def test_add_with_sreg_value(self):
1336- root = 'http://localhost:8000'
1337- call_command(
1338- 'add_openid_rp_config', root, allowed_sreg='nickname,email')
1339- config = get_object_or_none(OpenIDRPConfig, trust_root=root)
1340- self.assertIsNotNone(config)
1341- self.assertFalse(config.allow_unverified)
1342- self.assertEqual(config.allowed_sreg, 'nickname,email')
1343-
1344- def test_add_with_ax_value(self):
1345- root = 'http://localhost:8000'
1346- call_command(
1347- 'add_openid_rp_config', root, allowed_ax='nickname,email')
1348- config = get_object_or_none(OpenIDRPConfig, trust_root=root)
1349- self.assertIsNotNone(config)
1350- self.assertFalse(config.allow_unverified)
1351- self.assertEqual(config.allowed_ax, 'nickname,email')
1352-
1353- def test_add_with_ax_and_sreg_value(self):
1354- root = 'http://localhost:8000'
1355- call_command(
1356- 'add_openid_rp_config', root, allowed_sreg='nickname',
1357- allowed_ax='nickname,email')
1358- config = get_object_or_none(OpenIDRPConfig, trust_root=root)
1359- self.assertIsNotNone(config)
1360- self.assertFalse(config.allow_unverified)
1361- self.assertEqual(config.allowed_sreg, 'nickname')
1362- self.assertEqual(config.allowed_ax, 'nickname,email')
1363+ def test_add_with_allowed_user_attribs_value(self):
1364+ root = 'http://localhost:8000'
1365+ call_command(
1366+ 'add_openid_rp_config', root,
1367+ allowed_user_attribs='nickname,email')
1368+ config = get_object_or_none(OpenIDRPConfig, trust_root=root)
1369+ self.assertIsNotNone(config)
1370+ self.assertFalse(config.allow_unverified)
1371+ self.assertEqual(config.allowed_user_attribs, 'nickname,email')
1372
1373=== modified file 'identityprovider/tests/test_forms.py'
1374--- identityprovider/tests/test_forms.py 2013-04-15 16:40:19 +0000
1375+++ identityprovider/tests/test_forms.py 2013-05-01 15:39:26 +0000
1376@@ -15,18 +15,19 @@
1377 from identityprovider.models.account import Account
1378 from identityprovider.models.const import EmailStatus
1379 from identityprovider.const import (
1380+ AX_URI_ACCOUNT_VERIFIED,
1381+ AX_URI_EMAIL,
1382 AX_URI_FULL_NAME,
1383- AX_URI_EMAIL,
1384+ AX_URI_LANGUAGE,
1385 )
1386 from identityprovider.forms import (
1387- AXFetchRequestForm,
1388 DeviceRenameForm,
1389 EditAccountForm,
1390 LoginForm,
1391 ResetPasswordForm,
1392- SRegRequestForm,
1393 TeamsRequestForm,
1394 TokenForm,
1395+ UserAttribsRequestForm,
1396 )
1397 from identityprovider.models.openidmodels import OpenIDRPConfig
1398 from identityprovider.tests import DEFAULT_USER_PASSWORD
1399@@ -251,267 +252,180 @@
1400 'Invalid characters in password')
1401
1402
1403-class SRegRequestFormTest(SSOBaseTestCase):
1404-
1405- def _get_request_with_post_args(self, args={}):
1406- request = HttpRequest()
1407- request.user = self.test_user
1408- request.POST = args
1409- request.META = {'REQUEST_METHOD': 'POST'}
1410- return request
1411-
1412- def setUp(self):
1413- super(SRegRequestFormTest, self).setUp()
1414- self.test_user = Account.objects.create_account(
1415- 'My name', 'me@test.com', DEFAULT_USER_PASSWORD)
1416- self.rpconfig = OpenIDRPConfig.objects.create(
1417- trust_root='http://localhost/', description="Some description")
1418-
1419- def test_no_approved_fields_without_post_request(self):
1420- """The server should not generate a list of approved fields when the
1421- request is not a POST request.
1422- """
1423- request = self._get_request_with_post_args()
1424- request.META['REQUEST_METHOD'] = 'GET'
1425- form = SRegRequestForm(
1426- request,
1427- SRegRequest(required=['fullname', 'email']),
1428- self.rpconfig)
1429- self.assertEqual(len(form.data_approved_for_request), 0)
1430-
1431- def test_required_fields_for_trusted_site(self):
1432- """The server should always return values for required fields to
1433- trusted sites, regardless of the state of the checkbox in the UI.
1434- Optional fields should not be returned if the user has unchecked them.
1435- """
1436- self.rpconfig.allowed_sreg = 'fullname,email'
1437- form = SRegRequestForm(
1438- self._get_request_with_post_args(),
1439- SRegRequest(required=['fullname'], optional=['email']),
1440- self.rpconfig)
1441- self.assertIn('fullname', form.data_approved_for_request)
1442- self.assertNotIn('email', form.data_approved_for_request)
1443-
1444- def test_optional_fields_for_trusted_site(self):
1445- """The server should return values for optional fields to trusted
1446- sites only when the user checks the checkbox in the UI.
1447- """
1448- self.rpconfig.allowed_sreg = 'fullname,email'
1449- post_args = {'email': 'email'}
1450- form = SRegRequestForm(
1451- self._get_request_with_post_args(post_args),
1452- SRegRequest(optional=['fullname', 'email']),
1453- self.rpconfig)
1454- self.assertFalse('fullname' in form.data_approved_for_request)
1455- self.assertTrue('email' in form.data_approved_for_request)
1456-
1457- def test_required_fields_for_untrusted_site(self):
1458- """The server should return values for required fields to untrusted
1459- sites only when the user checks the checkbox in the UI.
1460- """
1461- post_args = {'email': 'email'}
1462- form = SRegRequestForm(
1463- self._get_request_with_post_args(post_args),
1464- SRegRequest(required=['fullname', 'email']),
1465- None)
1466- self.assertFalse('fullname' in form.data_approved_for_request)
1467- self.assertTrue('email' in form.data_approved_for_request)
1468-
1469- def test_optional_fields_for_untrusted_site(self):
1470- """The server should return values for optional fields to untrusted
1471- sites only when the user checks the checkbox in the UI.
1472- """
1473- post_args = {'fullname': 'fullname'}
1474- form = SRegRequestForm(
1475- self._get_request_with_post_args(post_args),
1476- SRegRequest(optional=['fullname', 'email']),
1477- None)
1478- self.assertTrue('fullname' in form.data_approved_for_request)
1479- self.assertFalse('email' in form.data_approved_for_request)
1480-
1481- def test_checkbox_status_for_trusted_site(self):
1482- """Checkboxes are always checked if the site is trusted
1483- """
1484- form = SRegRequestForm(
1485- self._get_request_with_post_args(),
1486- SRegRequest(required=['fullname'], optional=['email']),
1487- self.rpconfig)
1488- self.assertTrue(form.check_test('fullname'))
1489- self.assertTrue(form.check_test('email'))
1490-
1491- def test_checkbox_status_for_trusted_site_with_approved_data(self):
1492- """If the user has previously approved sending data to a trusted site
1493- the same checkbox settings should be returned on the next request
1494- unless those conflict with the required fields.
1495- """
1496- approved_data = {
1497- 'requested': ['fullname', 'email'],
1498- 'approved': ['email']}
1499- form1 = SRegRequestForm(
1500- self._get_request_with_post_args(),
1501- SRegRequest(required=['fullname'], optional=['email']),
1502- self.rpconfig, approved_data=approved_data)
1503- self.assertTrue(form1.check_test('fullname'))
1504- self.assertTrue(form1.check_test('email'))
1505-
1506- approved_data['approved'] = []
1507- form2 = SRegRequestForm(
1508- self._get_request_with_post_args(),
1509- SRegRequest(required=['fullname'], optional=['email']),
1510- self.rpconfig, approved_data=approved_data)
1511- self.assertFalse(form2.check_test('email'))
1512-
1513- def test_checkbox_status_for_untrusted_site(self):
1514- """Checkboxes are only checked on untrusted site requests if the field
1515- is required
1516- """
1517- form = SRegRequestForm(
1518- self._get_request_with_post_args(),
1519- SRegRequest(required=['fullname'], optional=['email']),
1520- None)
1521- self.assertTrue(form.check_test('fullname'))
1522- self.assertFalse(form.check_test('email'))
1523-
1524- def test_checkbox_status_for_untrusted_site_with_approved_data(self):
1525- """If the user has previously approved sending data to an untrusted
1526- site the same checkbox settings should be returned on the next request.
1527- """
1528- approved_data = {
1529- 'requested': ['fullname', 'email'],
1530- 'approved': ['email']}
1531- form = SRegRequestForm(
1532- self._get_request_with_post_args(),
1533- SRegRequest(required=['fullname'], optional=['email']),
1534- None, approved_data=approved_data)
1535- self.assertFalse(form.check_test('fullname'))
1536- self.assertTrue(form.check_test('email'))
1537-
1538-
1539-class AXFetchRequestFormTestCase(SSOBaseTestCase):
1540- """AX Fetch Request form tests.
1541-
1542- This is all functionally very similar to Simple Registration, except that
1543- the extensibility of AX adds an extra layer of complexity.
1544- """
1545-
1546- def _get_request_with_post_args(self, args={}):
1547- request = HttpRequest()
1548- request.user = self.test_user
1549- request.POST = args
1550- request.META = {'REQUEST_METHOD': 'POST'}
1551- return request
1552-
1553- def setUp(self):
1554- super(AXFetchRequestFormTestCase, self).setUp()
1555- self.test_user = Account.objects.create_account(
1556- 'My name', 'me@test.com', DEFAULT_USER_PASSWORD)
1557- self.rpconfig = OpenIDRPConfig.objects.create(
1558- trust_root='http://localhost/', description="Some description")
1559-
1560- def test_no_approved_fields_without_post_request(self):
1561- """The server should not generate a list of approved fields when the
1562- request is not a POST request.
1563- """
1564- request = self._get_request_with_post_args()
1565- request.META['REQUEST_METHOD'] = 'GET'
1566- fetch_request = FetchRequest()
1567+class UserAttribsRequestFormTest(SSOBaseTestCase):
1568+
1569+ def _get_request_with_post_args(self, args={}):
1570+ request = HttpRequest()
1571+ request.user = self.test_user
1572+ request.POST = args
1573+ request.META = {'REQUEST_METHOD': 'POST'}
1574+ return request
1575+
1576+ def setUp(self):
1577+ super(UserAttribsRequestFormTest, self).setUp()
1578+ self.test_user = Account.objects.create_account(
1579+ 'My name', 'me@test.com', DEFAULT_USER_PASSWORD)
1580+ self.rpconfig = OpenIDRPConfig.objects.create(
1581+ trust_root='http://localhost/', description="Some description")
1582+
1583+ def test_no_approved_fields_without_post_request(self):
1584+ """The server should not generate a list of approved fields when the
1585+ request is not a POST request.
1586+ """
1587+ sreg_request = SRegRequest(required=['fullname', 'email'])
1588+ ax_request = FetchRequest()
1589 for (attr, alias) in [
1590 (AX_URI_FULL_NAME, 'fullname'),
1591 (AX_URI_EMAIL, 'email')]:
1592- fetch_request.add(AttrInfo(attr, alias=alias, required=True))
1593- form = AXFetchRequestForm(request, fetch_request, self.rpconfig)
1594+ ax_request.add(AttrInfo(attr, alias=alias, required=True))
1595+ request = self._get_request_with_post_args()
1596+ request.META['REQUEST_METHOD'] = 'GET'
1597+ form = UserAttribsRequestForm(request=request,
1598+ sreg_request=sreg_request,
1599+ ax_request=ax_request,
1600+ rpconfig=self.rpconfig)
1601 self.assertEqual(len(form.data_approved_for_request), 0)
1602
1603- def test_required_fields_for_trusted_site(self):
1604- """The server should always return values for required fields to
1605- trusted sites, regardless of the state of the checkbox in the UI.
1606- Optional fields should not be returned if the user has unchecked them.
1607- """
1608- self.rpconfig.allowed_ax = 'fullname,email'
1609- fetch_request = FetchRequest()
1610- fetch_request.add(
1611+ def test_sreg_required_fields_for_trusted_site(self):
1612+ """The server should always return values for required fields to
1613+ trusted sites, regardless of the state of the checkbox in the UI.
1614+ Optional fields should not be returned if the user has unchecked them.
1615+ """
1616+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1617+ sreg_request = SRegRequest(required=['fullname'], optional=['email'])
1618+ form = UserAttribsRequestForm(
1619+ request=self._get_request_with_post_args(),
1620+ sreg_request=sreg_request, ax_request=None, rpconfig=self.rpconfig)
1621+ self.assertIn('fullname', form.data_approved_for_request)
1622+ self.assertNotIn('email', form.data_approved_for_request)
1623+
1624+ def test_ax_required_fields_for_trusted_site(self):
1625+ """The server should always return values for required fields to
1626+ trusted sites, regardless of the state of the checkbox in the UI.
1627+ Optional fields should not be returned if the user has unchecked them.
1628+ """
1629+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1630+ ax_request = FetchRequest()
1631+ ax_request.add(
1632 AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
1633- fetch_request.add(
1634+ ax_request.add(
1635 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
1636- form = AXFetchRequestForm(self._get_request_with_post_args(),
1637- fetch_request, self.rpconfig)
1638+ form = UserAttribsRequestForm(
1639+ request=self._get_request_with_post_args(), sreg_request=None,
1640+ ax_request=ax_request, rpconfig=self.rpconfig)
1641 self.assertIn('fullname', form.data_approved_for_request)
1642 self.assertNotIn('email', form.data_approved_for_request)
1643
1644- def test_fields_for_trusted_auto_authorize_site(self):
1645+ def test_ax_and_sreg_required_fields_for_trusted_site(self):
1646+ """The server should always return values for required fields to
1647+ trusted sites, regardless of the state of the checkbox in the UI.
1648+ Optional fields should not be returned if the user has unchecked them.
1649+ Fields are required if at least one of the SReg and AX request lists
1650+ them as such.
1651+ """
1652+ self.rpconfig.allowed_user_attribs = (
1653+ 'fullname,email,language,account_verified')
1654+ sreg_request = SRegRequest(
1655+ required=['language', 'email'], optional=['fullname'])
1656+ ax_request = FetchRequest()
1657+ ax_request.add(
1658+ AttrInfo(AX_URI_EMAIL, alias='email', required=True))
1659+ ax_request.add(
1660+ AttrInfo(AX_URI_ACCOUNT_VERIFIED, alias='account_verified',
1661+ required=True))
1662+ ax_request.add(
1663+ AttrInfo(AX_URI_LANGUAGE, alias='language', required=False))
1664+ ax_request.add(
1665+ AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=False))
1666+ form = UserAttribsRequestForm(
1667+ request=self._get_request_with_post_args(),
1668+ sreg_request=sreg_request, ax_request=ax_request,
1669+ rpconfig=self.rpconfig)
1670+ self.assertIn('email', form.data_approved_for_request)
1671+ self.assertIn('account_verified', form.data_approved_for_request)
1672+ self.assertIn('language', form.data_approved_for_request)
1673+ self.assertNotIn('fullname', form.data_approved_for_request)
1674+
1675+ def test_ax_fields_for_trusted_auto_authorize_site(self):
1676 """The server should always return values for requested fields to
1677 trusted sites configured to auto-authorize.
1678 """
1679- self.rpconfig.allowed_ax = 'fullname,email'
1680+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1681 self.rpconfig.auto_authorize = True
1682- fetch_request = FetchRequest()
1683+ ax_request = FetchRequest()
1684 # One required attribute
1685- fetch_request.add(
1686+ ax_request.add(
1687 AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
1688 # One optional attribute
1689- fetch_request.add(
1690+ ax_request.add(
1691 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
1692- form = AXFetchRequestForm(self._get_request_with_post_args(),
1693- fetch_request, self.rpconfig)
1694+ form = UserAttribsRequestForm(
1695+ request=self._get_request_with_post_args(), sreg_request=None,
1696+ ax_request=ax_request, rpconfig=self.rpconfig)
1697 # Both attributes should be returned
1698 self.assertIn('fullname', form.data_approved_for_request)
1699 self.assertIn('email', form.data_approved_for_request)
1700
1701- def test_optional_fields_for_trusted_site(self):
1702+ def test_ax_optional_fields_for_trusted_site(self):
1703 """The server should return values for optional fields to trusted
1704 sites only when the user checks the checkbox in the UI.
1705 """
1706- self.rpconfig.allowed_ax = 'fullname,email'
1707- post_args = {'email': 'email'}
1708- fetch_request = FetchRequest()
1709+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1710+ post_args = {'email': 'email', 'fullname': None}
1711+ ax_request = FetchRequest()
1712 for (attr, alias) in [
1713 (AX_URI_FULL_NAME, 'fullname'),
1714 (AX_URI_EMAIL, 'email')]:
1715- fetch_request.add(AttrInfo(attr, alias=alias, required=False))
1716- form = AXFetchRequestForm(self._get_request_with_post_args(post_args),
1717- fetch_request, self.rpconfig)
1718+ ax_request.add(AttrInfo(attr, alias=alias, required=False))
1719+ form = UserAttribsRequestForm(
1720+ request=self._get_request_with_post_args(post_args),
1721+ sreg_request=None, ax_request=ax_request, rpconfig=self.rpconfig)
1722 self.assertFalse('fullname' in form.data_approved_for_request)
1723 self.assertTrue('email' in form.data_approved_for_request)
1724
1725- def test_required_fields_for_untrusted_site(self):
1726+ def test_ax_required_fields_for_untrusted_site(self):
1727 """The server should return values for required fields to untrusted
1728 sites only when the user checks the checkbox in the UI.
1729 """
1730 post_args = {'email': 'email'}
1731- fetch_request = FetchRequest()
1732+ ax_request = FetchRequest()
1733 for (attr, alias) in [
1734 (AX_URI_FULL_NAME, 'fullname'),
1735 (AX_URI_EMAIL, 'email')]:
1736- fetch_request.add(AttrInfo(attr, alias=alias, required=True))
1737- form = AXFetchRequestForm(self._get_request_with_post_args(post_args),
1738- fetch_request, None)
1739+ ax_request.add(AttrInfo(attr, alias=alias, required=True))
1740+ form = UserAttribsRequestForm(
1741+ request=self._get_request_with_post_args(post_args),
1742+ sreg_request=None, ax_request=ax_request, rpconfig=None)
1743 self.assertFalse('fullname' in form.data_approved_for_request)
1744 self.assertTrue('email' in form.data_approved_for_request)
1745
1746- def test_optional_fields_for_untrusted_site(self):
1747+ def test_ax_optional_fields_for_untrusted_site(self):
1748 """The server should return values for optional fields to untrusted
1749 sites only when the user checks the checkbox in the UI.
1750 """
1751 post_args = {'fullname': 'fullname'}
1752- fetch_request = FetchRequest()
1753+ ax_request = FetchRequest()
1754 for (attr, alias) in [
1755 (AX_URI_FULL_NAME, 'fullname'),
1756 (AX_URI_EMAIL, 'email')]:
1757- fetch_request.add(AttrInfo(attr, alias=alias, required=False))
1758- form = AXFetchRequestForm(self._get_request_with_post_args(post_args),
1759- fetch_request, None)
1760+ ax_request.add(AttrInfo(attr, alias=alias, required=False))
1761+ form = UserAttribsRequestForm(
1762+ request=self._get_request_with_post_args(post_args),
1763+ sreg_request=None, ax_request=ax_request, rpconfig=None)
1764 self.assertTrue('fullname' in form.data_approved_for_request)
1765 self.assertFalse('email' in form.data_approved_for_request)
1766
1767- def test_checkbox_status_for_trusted_site(self):
1768+ def test_ax_checkbox_status_for_trusted_site(self):
1769 """Checkboxes are always checked if the site is trusted"""
1770- fetch_request = FetchRequest()
1771- fetch_request.add(
1772+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1773+ ax_request = FetchRequest()
1774+ ax_request.add(
1775 AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
1776- fetch_request.add(
1777+ ax_request.add(
1778 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
1779- form = AXFetchRequestForm(self._get_request_with_post_args(),
1780- fetch_request, self.rpconfig)
1781+ form = UserAttribsRequestForm(
1782+ request=self._get_request_with_post_args(),
1783+ sreg_request=None, ax_request=ax_request, rpconfig=self.rpconfig)
1784 # True because fullname required
1785 self.assertTrue(form.check_test('fullname'))
1786 # True because trusted site and no previous disapproval
1787@@ -519,22 +433,24 @@
1788 # Throw in an unrequested field for good measure
1789 self.assertFalse(form.check_test('language'))
1790
1791- def test_checkbox_status_for_trusted_site_with_approved_data(self):
1792+ def test_ax_checkbox_status_for_trusted_site_with_approved_data(self):
1793 """If the user has previously approved sending data to a trusted site
1794 the same checkbox settings should be returned on the next request
1795 unless those conflict with the required fields.
1796 """
1797+ self.rpconfig.allowed_user_attribs = 'fullname,email,language'
1798 approved_data = {
1799- 'requested': ['fullname', 'email'],
1800+ 'requested': ['fullname', 'email', 'language'],
1801 'approved': ['email', 'language']}
1802- fetch_request = FetchRequest()
1803- fetch_request.add(
1804+ ax_request = FetchRequest()
1805+ ax_request.add(
1806 AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
1807- fetch_request.add(
1808+ ax_request.add(
1809 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
1810- form1 = AXFetchRequestForm(self._get_request_with_post_args(),
1811- fetch_request, self.rpconfig,
1812- approved_data=approved_data)
1813+ form1 = UserAttribsRequestForm(
1814+ request=self._get_request_with_post_args(),
1815+ sreg_request=None, ax_request=ax_request, rpconfig=self.rpconfig,
1816+ approved_data=approved_data)
1817 # True because fullname required
1818 self.assertTrue(form1.check_test('fullname'))
1819 # True because email previously approved
1820@@ -543,9 +459,10 @@
1821 self.assertFalse(form1.check_test('language'))
1822
1823 approved_data['approved'] = []
1824- form2 = AXFetchRequestForm(self._get_request_with_post_args(),
1825- fetch_request, self.rpconfig,
1826- approved_data=approved_data)
1827+ form2 = UserAttribsRequestForm(
1828+ request=self._get_request_with_post_args(),
1829+ sreg_request=None, ax_request=ax_request, rpconfig=self.rpconfig,
1830+ approved_data=approved_data)
1831 # True because fullname required
1832 self.assertTrue(form1.check_test('fullname'))
1833 # False because email previously disapproved
1834@@ -553,17 +470,18 @@
1835 # Throw in an unrequested field for good measure
1836 self.assertFalse(form2.check_test('language'))
1837
1838- def test_checkbox_status_for_untrusted_site(self):
1839+ def test_ax_checkbox_status_for_untrusted_site(self):
1840 """Checkboxes are only checked on untrusted site requests if the field
1841 is required
1842 """
1843- fetch_request = FetchRequest()
1844- fetch_request.add(
1845+ ax_request = FetchRequest()
1846+ ax_request.add(
1847 AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
1848- fetch_request.add(
1849+ ax_request.add(
1850 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
1851- form = AXFetchRequestForm(self._get_request_with_post_args(),
1852- fetch_request, None)
1853+ form = UserAttribsRequestForm(
1854+ request=self._get_request_with_post_args(),
1855+ sreg_request=None, ax_request=ax_request, rpconfig=None)
1856 # True because fullname required
1857 self.assertTrue(form.check_test('fullname'))
1858 # False because untrusted site and no previous approval
1859@@ -571,21 +489,22 @@
1860 # Throw in an unrequested field for good measure
1861 self.assertFalse(form.check_test('language'))
1862
1863- def test_checkbox_status_for_untrusted_site_with_approved_data(self):
1864+ def test_ax_checkbox_status_for_untrusted_site_with_approved_data(self):
1865 """If the user has previously approved sending data to an untrusted
1866 site the same checkbox settings should be returned on the next request.
1867 """
1868 approved_data = {
1869 'requested': ['fullname', 'email'],
1870 'approved': ['email', 'language']}
1871- fetch_request = FetchRequest()
1872- fetch_request.add(
1873+ ax_request = FetchRequest()
1874+ ax_request.add(
1875 AttrInfo(AX_URI_FULL_NAME, alias='fullname', required=True))
1876- fetch_request.add(
1877+ ax_request.add(
1878 AttrInfo(AX_URI_EMAIL, alias='email', required=False))
1879- form = AXFetchRequestForm(self._get_request_with_post_args(),
1880- fetch_request, None,
1881- approved_data=approved_data)
1882+ form = UserAttribsRequestForm(
1883+ request=self._get_request_with_post_args(),
1884+ sreg_request=None, ax_request=ax_request, rpconfig=None,
1885+ approved_data=approved_data)
1886 # False because untrusted site and previously disapproved
1887 self.assertFalse(form.check_test('fullname'))
1888 # True because previously approved
1889@@ -593,6 +512,115 @@
1890 # Throw in an unrequested, previously-approved field for good measure
1891 self.assertFalse(form.check_test('language'))
1892
1893+ def test_sreg_optional_fields_for_trusted_site(self):
1894+ """The server should return values for optional fields to trusted
1895+ sites only when the user checks the checkbox in the UI.
1896+ """
1897+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1898+ post_args = {'email': 'email'}
1899+ form = UserAttribsRequestForm(
1900+ request=self._get_request_with_post_args(post_args),
1901+ sreg_request=SRegRequest(optional=['fullname', 'email']),
1902+ ax_request=None, rpconfig=self.rpconfig)
1903+ self.assertFalse('fullname' in form.data_approved_for_request)
1904+ self.assertTrue('email' in form.data_approved_for_request)
1905+
1906+ def test_sreg_required_fields_for_untrusted_site(self):
1907+ """The server should return values for required fields to untrusted
1908+ sites only when the user checks the checkbox in the UI.
1909+ """
1910+ post_args = {'email': 'email'}
1911+ form = UserAttribsRequestForm(
1912+ request=self._get_request_with_post_args(post_args),
1913+ sreg_request=SRegRequest(required=['fullname', 'email']),
1914+ ax_request=None, rpconfig=None)
1915+ self.assertFalse('fullname' in form.data_approved_for_request)
1916+ self.assertTrue('email' in form.data_approved_for_request)
1917+
1918+ def test_sreg_optional_fields_for_untrusted_site(self):
1919+ """The server should return values for optional fields to untrusted
1920+ sites only when the user checks the checkbox in the UI.
1921+ """
1922+ post_args = {'fullname': 'fullname'}
1923+ form = UserAttribsRequestForm(
1924+ request=self._get_request_with_post_args(post_args),
1925+ sreg_request=SRegRequest(optional=['fullname', 'email']),
1926+ ax_request=None, rpconfig=None)
1927+ self.assertTrue('fullname' in form.data_approved_for_request)
1928+ self.assertFalse('email' in form.data_approved_for_request)
1929+
1930+ def test_sreg_checkbox_status_for_trusted_site(self):
1931+ """Checkboxes are always checked if the site is trusted
1932+ """
1933+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1934+ form = UserAttribsRequestForm(
1935+ request=self._get_request_with_post_args(),
1936+ sreg_request=SRegRequest(
1937+ required=['fullname'], optional=['email']),
1938+ ax_request=None, rpconfig=self.rpconfig)
1939+ # Checked (and disabled) because fullname is required
1940+ self.assertTrue(form.check_test('fullname'))
1941+ # Checked (and togglable) because this is a known trust root
1942+ self.assertTrue(form.check_test('email'))
1943+
1944+ def test_sreg_checkbox_status_for_trusted_site_with_approved_data(self):
1945+ """If the user has previously approved sending data to a trusted site
1946+ the same checkbox settings should be returned on the next request
1947+ unless those conflict with the required fields.
1948+ """
1949+ self.rpconfig.allowed_user_attribs = 'fullname,email'
1950+ approved_data = {
1951+ 'requested': ['fullname', 'email'],
1952+ 'approved': ['email']}
1953+ form1 = UserAttribsRequestForm(
1954+ request=self._get_request_with_post_args(),
1955+ sreg_request=SRegRequest(
1956+ required=['fullname'], optional=['email']),
1957+ ax_request=None, rpconfig=self.rpconfig,
1958+ approved_data=approved_data)
1959+ self.assertTrue(form1.check_test('fullname'))
1960+ self.assertTrue(form1.check_test('email'))
1961+
1962+ approved_data['approved'] = []
1963+ form2 = UserAttribsRequestForm(
1964+ request=self._get_request_with_post_args(),
1965+ sreg_request=SRegRequest(
1966+ required=['fullname'], optional=['email']),
1967+ ax_request=None, rpconfig=self.rpconfig,
1968+ approved_data=approved_data)
1969+ # Checked (and disabled) because fullname is required
1970+ self.assertTrue(form2.check_test('fullname'))
1971+ # Unchecked (and togglable) because email is optional and the user
1972+ # previously disapproved it
1973+ self.assertFalse(form2.check_test('email'))
1974+
1975+ def test_sreg_checkbox_status_for_untrusted_site(self):
1976+ """Checkboxes are only checked on untrusted site requests if the field
1977+ is required
1978+ """
1979+ form = UserAttribsRequestForm(
1980+ request=self._get_request_with_post_args(),
1981+ sreg_request=SRegRequest(
1982+ required=['fullname'], optional=['email']),
1983+ ax_request=None, rpconfig=None)
1984+ self.assertTrue(form.check_test('fullname'))
1985+ self.assertFalse(form.check_test('email'))
1986+
1987+ def test_sreg_checkbox_status_for_untrusted_site_with_approved_data(self):
1988+ """If the user has previously approved sending data to an untrusted
1989+ site the same checkbox settings should be returned on the next request.
1990+ """
1991+ approved_data = {
1992+ 'requested': ['fullname', 'email'],
1993+ 'approved': ['email']}
1994+ form = UserAttribsRequestForm(
1995+ request=self._get_request_with_post_args(),
1996+ sreg_request=SRegRequest(
1997+ required=['fullname'], optional=['email']),
1998+ ax_request=None, rpconfig=None, approved_data=approved_data)
1999+ self.assertFalse(form.check_test('fullname'))
2000+ self.assertTrue(form.check_test('email'))
2001+
2002
2003 class TeamsRequestFormTestCase(SSOBaseTestCase):
2004
2005@@ -705,7 +733,7 @@
2006 self.assertFalse(form2.check_test('ubuntu-team'))
2007
2008
2009-class TokenForrmTest(SSOBaseTestCase):
2010+class TokenFormTest(SSOBaseTestCase):
2011
2012 def test_confirmation_code_error(self):
2013 data = {'confirmation_code': 'BOGUS', 'email': 'fake@example.com'}
2014
2015=== modified file 'identityprovider/tests/test_views_server.py'
2016--- identityprovider/tests/test_views_server.py 2013-04-17 15:44:15 +0000
2017+++ identityprovider/tests/test_views_server.py 2013-05-01 15:39:26 +0000
2018@@ -170,7 +170,7 @@
2019 def test_handle_user_response_ax_openid_is_authorized_idselect(self):
2020 # update rp to auto authorize
2021 self.rpconfig.auto_authorize = True
2022- self.rpconfig.allowed_ax = 'fullname,email,account_verified'
2023+ self.rpconfig.allowed_user_attribs = 'fullname,email,account_verified'
2024 self.rpconfig.save()
2025
2026 self.client.login(username=self.email, password=DEFAULT_USER_PASSWORD)
2027@@ -210,7 +210,7 @@
2028 def _test_auto_auth(self):
2029 # update rp to auto authorize
2030 self.rpconfig.auto_authorize = True
2031- self.rpconfig.allowed_ax = 'fullname,email,account_verified'
2032+ self.rpconfig.allowed_user_attribs = 'fullname,email,account_verified'
2033 self.rpconfig.save()
2034
2035 self.client.login(username=self.email, password=DEFAULT_USER_PASSWORD)
2036@@ -225,8 +225,6 @@
2037 })
2038 response = self.client.post(self.url, self.params)
2039 self.assertEqual('text/html', response['Content-type'].split(';')[0])
2040- self.assertContains(response, 'assoc_handle')
2041- self.assertContains(response, 'openid.sig')
2042 dom = PyQuery(response.content)
2043 root = dom.root.getroot()
2044 self.assertEqual('html', root.tag)
2045@@ -244,6 +242,8 @@
2046 )
2047 for k, v in expected_fields:
2048 self.assertEqual(v, forms[0].fields[k])
2049+ for k in ('openid.assoc_handle', 'openid.sig'):
2050+ self.assertIn(k, forms[0].fields)
2051
2052 def test_handle_user_response_openid_is_authorized_idselect(self):
2053 # update rp to auto authorize
2054@@ -598,9 +598,9 @@
2055 # no extra check is needed
2056 server._check_team_membership(request, self.orequest, oresponse)
2057
2058- def test_only_ax_or_sreg_form_is_displayed(self):
2059- """Even if both SReg and AX requests are present, only display the AX
2060- form."""
2061+ def test_ax_and_sreg_fields_are_merged(self):
2062+ """If both SReg and AX requests are present, only display one set of
2063+ fields."""
2064 team_name = 'ubuntu-team'
2065 team = self.factory.make_team(name=team_name)
2066 self.factory.add_account_to_team(self.account, team)
2067@@ -608,8 +608,7 @@
2068 # create a trusted rpconfig
2069 rpconfig = OpenIDRPConfig(
2070 trust_root='http://localhost/',
2071- allowed_sreg='fullname,email',
2072- allowed_ax='fullname,email,account_verified',
2073+ allowed_user_attribs='fullname,email,account_verified',
2074 can_query_any_team=True,
2075 description="Some description",
2076 )
2077@@ -628,8 +627,8 @@
2078 self._prepare_openid_token(param_overrides=param_overrides)
2079 response = self.client.get('/%s/+decide' % self.token)
2080 dom = PyQuery(response.content)
2081- self.assertEqual(len(dom.find('li.ax')), 3)
2082- self.assertEqual(len(dom.find('li.sreg')), 0)
2083+ # Only 3 fields, because language isn't allowed by the rpconfig
2084+ self.assertEqual(len(dom.find('li.user_attribs')), 3)
2085
2086 def test_list_of_details_is_complete_with_sreg(self):
2087 team_name = 'ubuntu-team'
2088@@ -639,7 +638,7 @@
2089 # create a trusted rpconfig
2090 OpenIDRPConfig.objects.create(
2091 trust_root='http://localhost/',
2092- allowed_sreg='fullname,email,language',
2093+ allowed_user_attribs='fullname,email,language',
2094 can_query_any_team=True,
2095 description="Some description",
2096 )
2097@@ -662,7 +661,7 @@
2098 # create a trusted rpconfig
2099 rpconfig = OpenIDRPConfig(
2100 trust_root='http://localhost/',
2101- allowed_ax='fullname,email,account_verified',
2102+ allowed_user_attribs='fullname,email,account_verified',
2103 can_query_any_team=True,
2104 description="Some description",
2105 )
2106@@ -747,7 +746,7 @@
2107 # create a trusted rpconfig
2108 OpenIDRPConfig.objects.create(
2109 trust_root='http://localhost/',
2110- allowed_sreg='nickname,email,language',
2111+ allowed_user_attribs='nickname,email,language',
2112 can_query_any_team=True,
2113 description="Some description",
2114 )
2115@@ -759,7 +758,7 @@
2116 self._prepare_openid_token(param_overrides=param_overrides)
2117 response = self.client.get(self.url)
2118 dom = PyQuery(response.content)
2119- self.assertEqual(len(dom.find('li.sreg')), 3)
2120+ self.assertEqual(len(dom.find('li.user_attribs')), 3)
2121
2122 nickname = self.account.person.name
2123 self._test_required_trusted_field(dom, field='nickname',
2124@@ -784,7 +783,7 @@
2125 # create a trusted rpconfig
2126 rpconfig = OpenIDRPConfig(
2127 trust_root='http://localhost/',
2128- allowed_ax='nickname,email,language,account_verified',
2129+ allowed_user_attribs='nickname,email,language,account_verified',
2130 can_query_any_team=True,
2131 description="Some description",
2132 )
2133@@ -803,7 +802,7 @@
2134 self._prepare_openid_token(param_overrides=param_overrides)
2135 response = self.client.get('/%s/+decide' % self.token)
2136 dom = PyQuery(response.content)
2137- self.assertEqual(len(dom.find('li.ax')), 3)
2138+ self.assertEqual(len(dom.find('li.user_attribs')), 3)
2139
2140 self._test_required_trusted_field(dom, field='email',
2141 label='Email address',
2142@@ -838,7 +837,7 @@
2143 self._prepare_openid_token(param_overrides=param_overrides)
2144 response = self.client.get(self.url)
2145 dom = PyQuery(response.content)
2146- self.assertEqual(len(dom.find('li.sreg')), 4)
2147+ self.assertEqual(len(dom.find('li.user_attribs')), 4)
2148
2149 nickname = self.account.person.name
2150 self._test_required_untrusted_field(dom, field='nickname',
2151@@ -885,7 +884,7 @@
2152 self._prepare_openid_token(param_overrides=param_overrides)
2153 response = self.client.get('/%s/+decide' % self.token)
2154 dom = PyQuery(response.content)
2155- self.assertEqual(len(dom.find('li.ax')), 4)
2156+ self.assertEqual(len(dom.find('li.user_attribs')), 4)
2157
2158 fullname = self.account.get_full_name()
2159 self._test_required_untrusted_field(dom, field='fullname',
2160@@ -1676,11 +1675,10 @@
2161 self._get_request_with_post_args(post_args),
2162 self._get_openid_request(with_sreg=True, with_ax=False,
2163 with_teams=False))
2164- self.assertEqual(sorted(result['sreg']['requested']),
2165+ self.assertEqual(sorted(result['user_attribs']['requested']),
2166 ['email', 'fullname'])
2167- self.assertEqual(result['sreg']['approved'], ['email'])
2168+ self.assertEqual(result['user_attribs']['approved'], ['email'])
2169 self.assertNotIn('teams', result)
2170- self.assertNotIn('ax', result)
2171
2172 def test_approved_data_for_ax_only(self):
2173 post_args = {'email': 'email', 'ubuntu-team': 'ubuntu-team'}
2174@@ -1688,10 +1686,9 @@
2175 self._get_request_with_post_args(post_args),
2176 self._get_openid_request(with_sreg=False, with_ax=True,
2177 with_teams=False))
2178- self.assertEqual(sorted(result['ax']['requested']),
2179+ self.assertEqual(sorted(result['user_attribs']['requested']),
2180 ['email', 'fullname'])
2181- self.assertEqual(result['ax']['approved'], ['email'])
2182- self.assertNotIn('sreg', result)
2183+ self.assertEqual(result['user_attribs']['approved'], ['email'])
2184 self.assertNotIn('teams', result)
2185
2186 def test_approved_data_for_teams_only(self):
2187@@ -1702,8 +1699,7 @@
2188 with_teams=True))
2189 self.assertEqual(result['teams']['requested'], ['ubuntu-team'])
2190 self.assertEqual(result['teams']['approved'], ['ubuntu-team'])
2191- self.assertNotIn('ax', result)
2192- self.assertNotIn('sreg', result)
2193+ self.assertNotIn('user_attribs', result)
2194
2195 def test_approved_data_for_sreg_and_teams(self):
2196 post_args = {'email': 'email', 'ubuntu-team': 'ubuntu-team'}
2197@@ -1711,12 +1707,11 @@
2198 self._get_request_with_post_args(post_args),
2199 self._get_openid_request(with_sreg=True, with_ax=False,
2200 with_teams=True))
2201- self.assertEqual(sorted(result['sreg']['requested']),
2202+ self.assertEqual(sorted(result['user_attribs']['requested']),
2203 ['email', 'fullname'])
2204- self.assertEqual(result['sreg']['approved'], ['email'])
2205+ self.assertEqual(result['user_attribs']['approved'], ['email'])
2206 self.assertEqual(result['teams']['requested'], ['ubuntu-team'])
2207 self.assertEqual(result['teams']['approved'], ['ubuntu-team'])
2208- self.assertNotIn('ax', result)
2209
2210 def test_approved_data_for_ax_and_teams(self):
2211 post_args = {'email': 'email', 'ubuntu-team': 'ubuntu-team'}
2212@@ -1724,12 +1719,11 @@
2213 self._get_request_with_post_args(post_args),
2214 self._get_openid_request(with_sreg=False, with_ax=True,
2215 with_teams=True))
2216- self.assertEqual(sorted(result['ax']['requested']),
2217+ self.assertEqual(sorted(result['user_attribs']['requested']),
2218 ['email', 'fullname'])
2219- self.assertEqual(result['ax']['approved'], ['email'])
2220+ self.assertEqual(result['user_attribs']['approved'], ['email'])
2221 self.assertEqual(result['teams']['requested'], ['ubuntu-team'])
2222 self.assertEqual(result['teams']['approved'], ['ubuntu-team'])
2223- self.assertNotIn('sreg', result)
2224
2225
2226 class TokenLoginTestCase(SSOBaseTestCase):
2227
2228=== modified file 'identityprovider/views/server.py'
2229--- identityprovider/views/server.py 2013-04-17 15:25:21 +0000
2230+++ identityprovider/views/server.py 2013-05-01 15:39:26 +0000
2231@@ -69,10 +69,9 @@
2232 LAUNCHPAD_TEAMS_NS,
2233 )
2234 from identityprovider.forms import (
2235- AXFetchRequestForm,
2236 PreAuthorizeForm,
2237- SRegRequestForm,
2238 TeamsRequestForm,
2239+ UserAttribsRequestForm,
2240 )
2241
2242 from identityprovider.middleware.xrds import XRDSMiddleware
2243@@ -177,8 +176,7 @@
2244 True, identity=request.user.openid_identity_url)
2245 else:
2246 oresponse = orequest.answer(True)
2247- _add_sreg(request, orequest, oresponse)
2248- _add_ax(request, orequest, oresponse)
2249+ _add_user_attribs(request, orequest, oresponse)
2250 _check_team_membership(request, orequest, oresponse,
2251 immediate=True)
2252 response = _django_response(request, oresponse, True)
2253@@ -199,8 +197,7 @@
2254 True, identity=request.user.openid_identity_url)
2255 else:
2256 oresponse = orequest.answer(True)
2257- _add_sreg(request, orequest, oresponse)
2258- _add_ax(request, orequest, oresponse)
2259+ _add_user_attribs(request, orequest, oresponse)
2260 _check_team_membership(request, orequest, oresponse, immediate=True)
2261 response = _django_response(request, oresponse, True)
2262 elif (twofactor.is_authenticated(request) and not
2263@@ -299,19 +296,16 @@
2264 except OpenIDRPSummary.DoesNotExist:
2265 approved_data = {}
2266
2267- ax_form = (AXFetchRequestForm(
2268- request, ax_request, rpconfig, approved_data=approved_data.get('ax'))
2269- if ax_request else None)
2270- sreg_form = SRegRequestForm(request, sreg_request, rpconfig,
2271- approved_data=approved_data.get('sreg'))
2272+ user_attribs_form = UserAttribsRequestForm(
2273+ request, sreg_request, ax_request, rpconfig,
2274+ approved_data=approved_data.get('user_attribs'))
2275 teams_form = TeamsRequestForm(request, teams_request, rpconfig,
2276 approved_data=approved_data.get('teams'))
2277 context = RequestContext(request, {
2278 'account': request.user,
2279 'trust_root': orequest.trust_root,
2280 'rpconfig': rpconfig,
2281- 'ax_form': ax_form,
2282- 'sreg_form': sreg_form,
2283+ 'user_attribs_form': user_attribs_form,
2284 'teams_form': teams_form,
2285 'token': token,
2286 'sane_trust_root': _request_has_sane_trust_root(orequest)
2287@@ -568,19 +562,14 @@
2288 rpconfig = utils.get_rpconfig(orequest.trust_root)
2289
2290 sreg_request = SRegRequest.fromOpenIDRequest(orequest)
2291- sreg_form = SRegRequestForm(request, sreg_request, rpconfig)
2292- if sreg_form.has_data:
2293- approved_data['sreg'] = {
2294- 'requested': sreg_form.data.keys(),
2295- 'approved': sreg_form.data_approved_for_request.keys()}
2296-
2297 ax_request = ax.FetchRequest.fromOpenIDRequest(orequest)
2298- if ax_request:
2299- ax_form = AXFetchRequestForm(request, ax_request, rpconfig)
2300- if ax_form.has_data:
2301- approved_data['ax'] = {
2302- 'requested': ax_form.data.keys(),
2303- 'approved': ax_form.data_approved_for_request.keys()}
2304+
2305+ user_attribs_form = UserAttribsRequestForm(
2306+ request, sreg_request, ax_request, rpconfig)
2307+ if user_attribs_form.has_data:
2308+ approved_data['user_attribs'] = {
2309+ 'requested': user_attribs_form.data.keys(),
2310+ 'approved': user_attribs_form.data_approved_for_request.keys()}
2311
2312 args = orequest.message.getArgs(LAUNCHPAD_TEAMS_NS)
2313 team_names = args.get('query_membership')
2314@@ -607,27 +596,21 @@
2315 return ret
2316
2317
2318-def _add_sreg(request, openid_request, openid_response):
2319- # Add sreg result data
2320+def _add_user_attribs(request, openid_request, openid_response):
2321+ # Add ax and sreg result data
2322 sreg_request = SRegRequest.fromOpenIDRequest(openid_request)
2323+ ax_request = ax.FetchRequest.fromOpenIDRequest(openid_request)
2324 rpconfig = utils.get_rpconfig(openid_request.trust_root)
2325- form = SRegRequestForm(request, sreg_request, rpconfig)
2326+ form = UserAttribsRequestForm(
2327+ request, sreg_request, ax_request, rpconfig)
2328 if form.data_approved_for_request:
2329 sreg_response = SRegResponse.extractResponse(
2330 sreg_request, form.data_approved_for_request)
2331 openid_response.addExtension(sreg_response)
2332-
2333-
2334-def _add_ax(request, openid_request, openid_response):
2335- ax_request = ax.FetchRequest.fromOpenIDRequest(openid_request)
2336- if ax_request:
2337- rpconfig = utils.get_rpconfig(openid_request.trust_root)
2338- form = AXFetchRequestForm(request, ax_request, rpconfig)
2339- if form.data_approved_for_request:
2340- ax_response = ax.FetchResponse(ax_request)
2341- for k, v in form.data_approved_for_request.iteritems():
2342- ax_response.addValue(AX_DATA_FIELDS.getNamespaceURI(k), v)
2343- openid_response.addExtension(ax_response)
2344+ ax_response = ax.FetchResponse(ax_request)
2345+ for k, v in form.data_approved_for_request.iteritems():
2346+ ax_response.addValue(AX_DATA_FIELDS.getNamespaceURI(k), v)
2347+ openid_response.addExtension(ax_response)
2348
2349
2350 def _process_decide(request, orequest, decision):
2351@@ -646,8 +629,7 @@
2352 request.user,
2353 orequest.trust_root,
2354 client_id=request.session.session_key)
2355- _add_sreg(request, orequest, oresponse)
2356- _add_ax(request, orequest, oresponse)
2357+ _add_user_attribs(request, orequest, oresponse)
2358 # if there's no submitted POST data, this is an auto-authorized
2359 # (immediate) request
2360 immediate = not request.POST