Merge lp:~wgrant/launchpad/destroy-person-password into lp:launchpad
- destroy-person-password
- Merge into devel
Proposed by
William Grant
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 14679 |
Proposed branch: | lp:~wgrant/launchpad/destroy-person-password |
Merge into: | lp:launchpad |
Diff against target: |
467 lines (+14/-229) 11 files modified
lib/lp/app/doc/launchpadform.txt (+1/-3) lib/lp/app/widgets/password.py (+0/-129) lib/lp/app/widgets/templates/passwordchange.pt (+0/-35) lib/lp/app/widgets/tests/test_widget_doctests.py (+0/-1) lib/lp/registry/browser/person.py (+4/-9) lib/lp/registry/browser/tests/person-admin-views.txt (+4/-17) lib/lp/registry/doc/person-account.txt (+3/-3) lib/lp/registry/doc/person.txt (+1/-4) lib/lp/registry/interfaces/person.py (+0/-3) lib/lp/registry/model/person.py (+1/-22) lib/lp/testing/factory.py (+0/-3) |
To merge this branch: | bzr merge lp:~wgrant/launchpad/destroy-person-password |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code. | Approve | |
Review via email: mp+88685@code.launchpad.net |
Commit message
Remove admin password reset support. Launchpad doesn't manage passwords.
Description of the change
Launchpad hasn't managed passwords since April 2010, but the admin password reset form has remained until now. This branch kills it and some related stuff.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/app/doc/launchpadform.txt' | |||
2 | --- lib/lp/app/doc/launchpadform.txt 2012-01-06 11:08:30 +0000 | |||
3 | +++ lib/lp/app/doc/launchpadform.txt 2012-01-17 00:44:26 +0000 | |||
4 | @@ -132,11 +132,9 @@ | |||
5 | 132 | 132 | ||
6 | 133 | >>> from zope.app.form.browser import TextWidget | 133 | >>> from zope.app.form.browser import TextWidget |
7 | 134 | >>> from lp.app.browser.launchpadform import custom_widget | 134 | >>> from lp.app.browser.launchpadform import custom_widget |
8 | 135 | >>> from lp.app.widgets.password import PasswordChangeWidget | ||
9 | 136 | 135 | ||
10 | 137 | >>> class FormTestView3(LaunchpadFormView): | 136 | >>> class FormTestView3(LaunchpadFormView): |
11 | 138 | ... schema = IFormTest | 137 | ... schema = IFormTest |
12 | 139 | ... custom_widget('password', PasswordChangeWidget) | ||
13 | 140 | ... custom_widget('displayname', TextWidget, displayWidth=50) | 138 | ... custom_widget('displayname', TextWidget, displayWidth=50) |
14 | 141 | 139 | ||
15 | 142 | >>> context = FormTest() | 140 | >>> context = FormTest() |
16 | @@ -149,7 +147,7 @@ | |||
17 | 149 | >>> view.widgets['displayname'].displayWidth | 147 | >>> view.widgets['displayname'].displayWidth |
18 | 150 | 50 | 148 | 50 |
19 | 151 | >>> view.widgets['password'] | 149 | >>> view.widgets['password'] |
21 | 152 | <...PasswordChangeWidget object at ...> | 150 | <...TextWidget object at ...> |
22 | 153 | 151 | ||
23 | 154 | 152 | ||
24 | 155 | == Using Another Context == | 153 | == Using Another Context == |
25 | 156 | 154 | ||
26 | === removed file 'lib/lp/app/widgets/password.py' | |||
27 | --- lib/lp/app/widgets/password.py 2011-12-24 17:49:30 +0000 | |||
28 | +++ lib/lp/app/widgets/password.py 1970-01-01 00:00:00 +0000 | |||
29 | @@ -1,129 +0,0 @@ | |||
30 | 1 | # Copyright 2009-2011 Canonical Ltd. This software is licensed under the | ||
31 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
32 | 3 | |||
33 | 4 | """ | ||
34 | 5 | Custom Password widgets. | ||
35 | 6 | |||
36 | 7 | TODO: Consider folding this back into Zope3 -- StuartBishop 20050520 | ||
37 | 8 | """ | ||
38 | 9 | |||
39 | 10 | __metaclass__ = type | ||
40 | 11 | |||
41 | 12 | from z3c.ptcompat import ViewPageTemplateFile | ||
42 | 13 | from zope.app.form.browser import PasswordWidget | ||
43 | 14 | from zope.app.form.browser.interfaces import ITextBrowserWidget | ||
44 | 15 | from zope.app.form.interfaces import WidgetInputError | ||
45 | 16 | from zope.component import getUtility | ||
46 | 17 | from zope.interface import implements | ||
47 | 18 | from zope.schema.interfaces import ValidationError | ||
48 | 19 | |||
49 | 20 | from lp import _ | ||
50 | 21 | from lp.services.webapp.interfaces import ( | ||
51 | 22 | IMultiLineWidgetLayout, | ||
52 | 23 | IPasswordEncryptor, | ||
53 | 24 | ) | ||
54 | 25 | |||
55 | 26 | |||
56 | 27 | class PasswordMismatch(ValidationError): | ||
57 | 28 | __doc__ = _("Passwords do not match.") | ||
58 | 29 | |||
59 | 30 | def __repr__(self): | ||
60 | 31 | return repr(self.__doc__) | ||
61 | 32 | |||
62 | 33 | |||
63 | 34 | class RequiredPasswordMissing(ValidationError): | ||
64 | 35 | __doc__ = _("You must enter a password.") | ||
65 | 36 | |||
66 | 37 | def __repr__(self): | ||
67 | 38 | return repr(self.__doc__) | ||
68 | 39 | |||
69 | 40 | |||
70 | 41 | class PasswordChangeWidget(PasswordWidget): | ||
71 | 42 | """A password change widget. | ||
72 | 43 | |||
73 | 44 | Text is not echoed to the user, and two text boxes are used to ensure | ||
74 | 45 | the password is entered correctly. | ||
75 | 46 | """ | ||
76 | 47 | implements(ITextBrowserWidget, IMultiLineWidgetLayout) | ||
77 | 48 | type = 'password change' | ||
78 | 49 | display_label = False | ||
79 | 50 | |||
80 | 51 | __call__ = ViewPageTemplateFile('templates/passwordchange.pt') | ||
81 | 52 | |||
82 | 53 | def hasInput(self): | ||
83 | 54 | """We always have input if there is an existing value | ||
84 | 55 | |||
85 | 56 | No input indicates unchanged. | ||
86 | 57 | """ | ||
87 | 58 | if PasswordWidget.hasInput(self): | ||
88 | 59 | return True | ||
89 | 60 | |||
90 | 61 | # If we don't have input from the user, we lie because we will | ||
91 | 62 | # use the existing value. | ||
92 | 63 | return bool(self._getCurrentPassword()) | ||
93 | 64 | |||
94 | 65 | def _getCurrentPassword(self): | ||
95 | 66 | # Yesh... indirection up the wazoo to do something this simple. | ||
96 | 67 | # Returns the current password. | ||
97 | 68 | return self.context.get(self.context.context) or None | ||
98 | 69 | |||
99 | 70 | def getInputValue(self): | ||
100 | 71 | """Ensure both text boxes contain the same value and inherited checks | ||
101 | 72 | |||
102 | 73 | >>> from lp.services.webapp.servers import ( | ||
103 | 74 | ... LaunchpadTestRequest) | ||
104 | 75 | >>> from zope.schema import Field | ||
105 | 76 | >>> field = Field(__name__='foo', title=u'Foo') | ||
106 | 77 | |||
107 | 78 | The widget will only return a value if both of the text boxes | ||
108 | 79 | contain the same value. It returns the value encrypted. | ||
109 | 80 | |||
110 | 81 | >>> request = LaunchpadTestRequest(form={ | ||
111 | 82 | ... 'field.foo': u'My Password', | ||
112 | 83 | ... 'field.foo_dupe': u'My Password', | ||
113 | 84 | ... }) | ||
114 | 85 | >>> widget = PasswordChangeWidget(field, request) | ||
115 | 86 | >>> crypted_pw = widget.getInputValue() | ||
116 | 87 | >>> encryptor = getUtility(IPasswordEncryptor) | ||
117 | 88 | >>> encryptor.validate(u'My Password', crypted_pw) | ||
118 | 89 | True | ||
119 | 90 | |||
120 | 91 | Otherwise it raises the exception required by IInputWidget | ||
121 | 92 | |||
122 | 93 | >>> request = LaunchpadTestRequest(form={ | ||
123 | 94 | ... 'field.foo': u'My Password', 'field.foo_dupe': u'No Match'}) | ||
124 | 95 | >>> widget = PasswordChangeWidget(field, request) | ||
125 | 96 | >>> widget.getInputValue() | ||
126 | 97 | Traceback (most recent call last): | ||
127 | 98 | [...] | ||
128 | 99 | WidgetInputError: ('foo', u'Foo', u'Passwords do not match.') | ||
129 | 100 | """ | ||
130 | 101 | value1 = self.request.form_ng.getOne(self.name, None) | ||
131 | 102 | value2 = self.request.form_ng.getOne('%s_dupe' % self.name, None) | ||
132 | 103 | if value1 != value2: | ||
133 | 104 | self._error = WidgetInputError( | ||
134 | 105 | self.context.__name__, self.label, PasswordMismatch() | ||
135 | 106 | ) | ||
136 | 107 | raise self._error | ||
137 | 108 | |||
138 | 109 | # If the user hasn't entered a password, we use the existing one | ||
139 | 110 | # if it is there. If it is not there, we are creating a new password | ||
140 | 111 | # and we raise an error | ||
141 | 112 | if not value1: | ||
142 | 113 | current_password = self._getCurrentPassword() | ||
143 | 114 | if current_password: | ||
144 | 115 | return current_password | ||
145 | 116 | else: | ||
146 | 117 | self._error = WidgetInputError( | ||
147 | 118 | self.context.__name__, self.label, | ||
148 | 119 | RequiredPasswordMissing() | ||
149 | 120 | ) | ||
150 | 121 | raise self._error | ||
151 | 122 | |||
152 | 123 | # Do any other validation | ||
153 | 124 | value = PasswordWidget.getInputValue(self) | ||
154 | 125 | assert value == value1, 'Form system has changed' | ||
155 | 126 | |||
156 | 127 | # If we have matching plaintext, encrypt it and return the password | ||
157 | 128 | encryptor = getUtility(IPasswordEncryptor) | ||
158 | 129 | return encryptor.encrypt(value) | ||
159 | 130 | 0 | ||
160 | === removed file 'lib/lp/app/widgets/templates/passwordchange.pt' | |||
161 | --- lib/lp/app/widgets/templates/passwordchange.pt 2009-07-28 22:44:12 +0000 | |||
162 | +++ lib/lp/app/widgets/templates/passwordchange.pt 1970-01-01 00:00:00 +0000 | |||
163 | @@ -1,35 +0,0 @@ | |||
164 | 1 | <tal:comment condition="nothing"> | ||
165 | 2 | Note that we don't use view/_getFormValue to fill in the value | ||
166 | 3 | for security reasons | ||
167 | 4 | </tal:comment> | ||
168 | 5 | <table style="margin-top:1em;"> | ||
169 | 6 | <tr> | ||
170 | 7 | <td colspan="2"> | ||
171 | 8 | <label tal:content="string: ${view/label}:" | ||
172 | 9 | tal:attributes="for view/name;">Password:</label><br /> | ||
173 | 10 | <input type="password" value="" tal:attributes=" | ||
174 | 11 | name view/name; | ||
175 | 12 | id view/name; | ||
176 | 13 | class view/cssClass; | ||
177 | 14 | style view/style; | ||
178 | 15 | size view/displayWidth; | ||
179 | 16 | maxlength view/displayMaxWidth|nothing; | ||
180 | 17 | "/> | ||
181 | 18 | </td> | ||
182 | 19 | </tr> | ||
183 | 20 | <tr> | ||
184 | 21 | <td colspan="2"> | ||
185 | 22 | <label tal:attributes="for string:${view/name}_dupe;"> | ||
186 | 23 | Retype the password: | ||
187 | 24 | </label><br /> | ||
188 | 25 | <input type="password" value="" tal:attributes=" | ||
189 | 26 | name string:${view/name}_dupe; | ||
190 | 27 | id string:${view/name}_dupe; | ||
191 | 28 | class view/cssClass; | ||
192 | 29 | style view/style; | ||
193 | 30 | size view/displayWidth; | ||
194 | 31 | maxlength view/displayMaxWidth|nothing; | ||
195 | 32 | "/> | ||
196 | 33 | </td> | ||
197 | 34 | </tr> | ||
198 | 35 | </table> | ||
199 | 36 | 0 | ||
200 | === modified file 'lib/lp/app/widgets/tests/test_widget_doctests.py' | |||
201 | --- lib/lp/app/widgets/tests/test_widget_doctests.py 2011-12-28 17:03:06 +0000 | |||
202 | +++ lib/lp/app/widgets/tests/test_widget_doctests.py 2012-01-17 00:44:26 +0000 | |||
203 | @@ -12,7 +12,6 @@ | |||
204 | 12 | def test_suite(): | 12 | def test_suite(): |
205 | 13 | suite = unittest.TestSuite() | 13 | suite = unittest.TestSuite() |
206 | 14 | suite.layer = DatabaseFunctionalLayer | 14 | suite.layer = DatabaseFunctionalLayer |
207 | 15 | suite.addTest(doctest.DocTestSuite('lp.app.widgets.password')) | ||
208 | 16 | suite.addTest(doctest.DocTestSuite('lp.app.widgets.textwidgets')) | 15 | suite.addTest(doctest.DocTestSuite('lp.app.widgets.textwidgets')) |
209 | 17 | suite.addTest(doctest.DocTestSuite('lp.app.widgets.date')) | 16 | suite.addTest(doctest.DocTestSuite('lp.app.widgets.date')) |
210 | 18 | return suite | 17 | return suite |
211 | 19 | 18 | ||
212 | === modified file 'lib/lp/registry/browser/person.py' | |||
213 | --- lib/lp/registry/browser/person.py 2011-12-30 06:14:56 +0000 | |||
214 | +++ lib/lp/registry/browser/person.py 2012-01-17 00:44:26 +0000 | |||
215 | @@ -155,7 +155,6 @@ | |||
216 | 155 | LaunchpadRadioWidgetWithDescription, | 155 | LaunchpadRadioWidgetWithDescription, |
217 | 156 | ) | 156 | ) |
218 | 157 | from lp.app.widgets.location import LocationWidget | 157 | from lp.app.widgets.location import LocationWidget |
219 | 158 | from lp.app.widgets.password import PasswordChangeWidget | ||
220 | 159 | from lp.blueprints.browser.specificationtarget import HasSpecificationsView | 158 | from lp.blueprints.browser.specificationtarget import HasSpecificationsView |
221 | 160 | from lp.blueprints.enums import SpecificationFilter | 159 | from lp.blueprints.enums import SpecificationFilter |
222 | 161 | from lp.bugs.browser.bugtask import BugTaskSearchListingView | 160 | from lp.bugs.browser.bugtask import BugTaskSearchListingView |
223 | @@ -971,8 +970,7 @@ | |||
224 | 971 | 970 | ||
225 | 972 | usedfor = IPersonEditMenu | 971 | usedfor = IPersonEditMenu |
226 | 973 | facet = 'overview' | 972 | facet = 'overview' |
229 | 974 | links = ('personal', 'email_settings', | 973 | links = ('personal', 'email_settings', 'sshkeys', 'gpgkeys') |
228 | 975 | 'sshkeys', 'gpgkeys', 'passwords') | ||
230 | 976 | 974 | ||
231 | 977 | def personal(self): | 975 | def personal(self): |
232 | 978 | target = '+edit' | 976 | target = '+edit' |
233 | @@ -1277,7 +1275,6 @@ | |||
234 | 1277 | label = "Review person's account" | 1275 | label = "Review person's account" |
235 | 1278 | custom_widget( | 1276 | custom_widget( |
236 | 1279 | 'status_comment', TextAreaWidget, height=5, width=60) | 1277 | 'status_comment', TextAreaWidget, height=5, width=60) |
237 | 1280 | custom_widget('password', PasswordChangeWidget) | ||
238 | 1281 | 1278 | ||
239 | 1282 | def __init__(self, context, request): | 1279 | def __init__(self, context, request): |
240 | 1283 | """See `LaunchpadEditFormView`.""" | 1280 | """See `LaunchpadEditFormView`.""" |
241 | @@ -1290,7 +1287,7 @@ | |||
242 | 1290 | # Set fields to be displayed. | 1287 | # Set fields to be displayed. |
243 | 1291 | self.field_names = ['status', 'status_comment'] | 1288 | self.field_names = ['status', 'status_comment'] |
244 | 1292 | if self.viewed_by_admin: | 1289 | if self.viewed_by_admin: |
246 | 1293 | self.field_names = ['displayname', 'password'] + self.field_names | 1290 | self.field_names = ['displayname'] + self.field_names |
247 | 1294 | 1291 | ||
248 | 1295 | @property | 1292 | @property |
249 | 1296 | def is_viewing_person(self): | 1293 | def is_viewing_person(self): |
250 | @@ -1334,10 +1331,8 @@ | |||
251 | 1334 | """Update the IAccount.""" | 1331 | """Update the IAccount.""" |
252 | 1335 | if (data['status'] == AccountStatus.SUSPENDED | 1332 | if (data['status'] == AccountStatus.SUSPENDED |
253 | 1336 | and self.context.status != AccountStatus.SUSPENDED): | 1333 | and self.context.status != AccountStatus.SUSPENDED): |
258 | 1337 | # Setting the password to a clear value makes it impossible to | 1334 | # The preferred email address is removed to ensure no email |
259 | 1338 | # login. The preferred email address is removed to ensure no | 1335 | # is sent to the user. |
256 | 1339 | # email is sent to the user. | ||
257 | 1340 | data['password'] = 'invalid' | ||
260 | 1341 | self.person.setPreferredEmail(None) | 1336 | self.person.setPreferredEmail(None) |
261 | 1342 | self.request.response.addInfoNotification( | 1337 | self.request.response.addInfoNotification( |
262 | 1343 | u'The account "%s" has been suspended.' % ( | 1338 | u'The account "%s" has been suspended.' % ( |
263 | 1344 | 1339 | ||
264 | === modified file 'lib/lp/registry/browser/tests/person-admin-views.txt' | |||
265 | --- lib/lp/registry/browser/tests/person-admin-views.txt 2011-12-24 17:49:30 +0000 | |||
266 | +++ lib/lp/registry/browser/tests/person-admin-views.txt 2012-01-17 00:44:26 +0000 | |||
267 | @@ -31,7 +31,6 @@ | |||
268 | 31 | The PersonAdministerView allows Launchpad admins to change some | 31 | The PersonAdministerView allows Launchpad admins to change some |
269 | 32 | of a user's attributes. | 32 | of a user's attributes. |
270 | 33 | 33 | ||
271 | 34 | >>> old_password = user.password | ||
272 | 35 | >>> form = { | 34 | >>> form = { |
273 | 36 | ... 'field.name': 'zaphod', | 35 | ... 'field.name': 'zaphod', |
274 | 37 | ... 'field.displayname': 'Zaphod Beeblebrox', | 36 | ... 'field.displayname': 'Zaphod Beeblebrox', |
275 | @@ -78,7 +77,7 @@ | |||
276 | 78 | >>> print view.errors | 77 | >>> print view.errors |
277 | 79 | [] | 78 | [] |
278 | 80 | >>> view.field_names | 79 | >>> view.field_names |
280 | 81 | ['displayname', 'password', 'status', 'status_comment'] | 80 | ['displayname', 'status', 'status_comment'] |
281 | 82 | >>> view.label | 81 | >>> view.label |
282 | 83 | "Review person's account" | 82 | "Review person's account" |
283 | 84 | 83 | ||
284 | @@ -97,32 +96,25 @@ | |||
285 | 97 | 96 | ||
286 | 98 | The admin can change the user's account information. | 97 | The admin can change the user's account information. |
287 | 99 | 98 | ||
288 | 100 | >>> old_password = user.password | ||
289 | 101 | >>> form = { | 99 | >>> form = { |
290 | 102 | ... 'field.displayname': 'The Zaphod Beeblebrox', | 100 | ... 'field.displayname': 'The Zaphod Beeblebrox', |
291 | 103 | ... 'field.password': 'secret1', | ||
292 | 104 | ... 'field.password_dupe': 'secret1', | ||
293 | 105 | ... 'field.status': 'ACTIVE', | 101 | ... 'field.status': 'ACTIVE', |
294 | 106 | ... 'field.actions.change': 'Change', | 102 | ... 'field.actions.change': 'Change', |
295 | 107 | ... } | 103 | ... } |
296 | 108 | >>> view = create_initialized_view(user, '+reviewaccount', form=form) | 104 | >>> view = create_initialized_view(user, '+reviewaccount', form=form) |
297 | 109 | >>> print view.errors | 105 | >>> print view.errors |
298 | 110 | [] | 106 | [] |
299 | 111 | >>> user.password != old_password | ||
300 | 112 | True | ||
301 | 113 | >>> print user.displayname | 107 | >>> print user.displayname |
302 | 114 | Zaphod Beeblebrox | 108 | Zaphod Beeblebrox |
303 | 115 | 109 | ||
304 | 116 | An admin can suspend a user's account using the +reviewaccount view. When | 110 | An admin can suspend a user's account using the +reviewaccount view. When |
307 | 117 | an account is suspended, the preferred email address is disabled and the | 111 | an account is suspended, the preferred email address is disabled. |
306 | 118 | password is changed too. | ||
308 | 119 | 112 | ||
309 | 120 | >>> user.account_status | 113 | >>> user.account_status |
310 | 121 | <DBItem AccountStatus.ACTIVE, ...> | 114 | <DBItem AccountStatus.ACTIVE, ...> |
311 | 122 | >>> print user.account_status_comment | 115 | >>> print user.account_status_comment |
312 | 123 | None | 116 | None |
313 | 124 | 117 | ||
314 | 125 | >>> old_password = user.password | ||
315 | 126 | >>> form = { | 118 | >>> form = { |
316 | 127 | ... 'field.displayname': 'Zaphod Beeblebrox', | 119 | ... 'field.displayname': 'Zaphod Beeblebrox', |
317 | 128 | ... 'field.status': 'SUSPENDED', | 120 | ... 'field.status': 'SUSPENDED', |
318 | @@ -137,16 +129,13 @@ | |||
319 | 137 | <DBItem AccountStatus.SUSPENDED, ...> | 129 | <DBItem AccountStatus.SUSPENDED, ...> |
320 | 138 | >>> print user.account_status_comment | 130 | >>> print user.account_status_comment |
321 | 139 | Wanted by the galactic police. | 131 | Wanted by the galactic police. |
322 | 140 | >>> user.password != old_password | ||
323 | 141 | True | ||
324 | 142 | >>> print user.preferredemail | 132 | >>> print user.preferredemail |
325 | 143 | None | 133 | None |
326 | 144 | 134 | ||
327 | 145 | An admin can reactivate a disabled user's account too. Unlike the act of | 135 | An admin can reactivate a disabled user's account too. Unlike the act of |
330 | 146 | suspension, reactivation does not change the user's password or email | 136 | suspension, reactivation does not change the user's email addresses; the |
331 | 147 | addresses; the user must use the reset password feature to restore these. | 137 | user must log in to restore these. |
332 | 148 | 138 | ||
333 | 149 | >>> old_password = user.password | ||
334 | 150 | >>> form = { | 139 | >>> form = { |
335 | 151 | ... 'field.displayname': 'Zaphod Beeblebrox', | 140 | ... 'field.displayname': 'Zaphod Beeblebrox', |
336 | 152 | ... 'field.status': 'ACTIVE', | 141 | ... 'field.status': 'ACTIVE', |
337 | @@ -160,7 +149,5 @@ | |||
338 | 160 | <DBItem AccountStatus.ACTIVE, ...> | 149 | <DBItem AccountStatus.ACTIVE, ...> |
339 | 161 | >>> print user.account_status_comment | 150 | >>> print user.account_status_comment |
340 | 162 | Zaphod's a hoopy frood. | 151 | Zaphod's a hoopy frood. |
341 | 163 | >>> user.password == old_password | ||
342 | 164 | True | ||
343 | 165 | >>> print user.preferredemail | 152 | >>> print user.preferredemail |
344 | 166 | None | 153 | None |
345 | 167 | 154 | ||
346 | === modified file 'lib/lp/registry/doc/person-account.txt' | |||
347 | --- lib/lp/registry/doc/person-account.txt 2012-01-03 12:44:03 +0000 | |||
348 | +++ lib/lp/registry/doc/person-account.txt 2012-01-17 00:44:26 +0000 | |||
349 | @@ -12,7 +12,9 @@ | |||
350 | 12 | process. Matsubara's account was created during a code import. | 12 | process. Matsubara's account was created during a code import. |
351 | 13 | 13 | ||
352 | 14 | >>> from zope.component import getUtility | 14 | >>> from zope.component import getUtility |
354 | 15 | >>> from lp.services.identity.interfaces.emailaddress import IEmailAddressSet | 15 | >>> from lp.services.identity.interfaces.emailaddress import ( |
355 | 16 | ... IEmailAddressSet, | ||
356 | 17 | ... ) | ||
357 | 16 | >>> from lp.registry.interfaces.person import IPersonSet | 18 | >>> from lp.registry.interfaces.person import IPersonSet |
358 | 17 | 19 | ||
359 | 18 | >>> emailset = getUtility(IEmailAddressSet) | 20 | >>> emailset = getUtility(IEmailAddressSet) |
360 | @@ -22,8 +24,6 @@ | |||
361 | 22 | False | 24 | False |
362 | 23 | >>> matsubara.account_status | 25 | >>> matsubara.account_status |
363 | 24 | <DBItem AccountStatus.NOACCOUNT, ...> | 26 | <DBItem AccountStatus.NOACCOUNT, ...> |
364 | 25 | >>> print matsubara.password | ||
365 | 26 | None | ||
366 | 27 | >>> print matsubara.preferredemail | 27 | >>> print matsubara.preferredemail |
367 | 28 | None | 28 | None |
368 | 29 | 29 | ||
369 | 30 | 30 | ||
370 | === modified file 'lib/lp/registry/doc/person.txt' | |||
371 | --- lib/lp/registry/doc/person.txt 2012-01-05 00:23:45 +0000 | |||
372 | +++ lib/lp/registry/doc/person.txt 2012-01-17 00:44:26 +0000 | |||
373 | @@ -142,7 +142,7 @@ | |||
374 | 142 | >>> from lp.services.identity.model.account import Account | 142 | >>> from lp.services.identity.model.account import Account |
375 | 143 | >>> from lp.services.database.lpstorm import IMasterStore | 143 | >>> from lp.services.database.lpstorm import IMasterStore |
376 | 144 | >>> account = IMasterStore(Account).get(Account, p.accountID) | 144 | >>> account = IMasterStore(Account).get(Account, p.accountID) |
378 | 145 | >>> account.reactivate("Activated by doc test.", password=p.password) | 145 | >>> account.reactivate("Activated by doc test.", account.password) |
379 | 146 | >>> p.account_status | 146 | >>> p.account_status |
380 | 147 | <DBItem AccountStatus.ACTIVE... | 147 | <DBItem AccountStatus.ACTIVE... |
381 | 148 | 148 | ||
382 | @@ -485,9 +485,6 @@ | |||
383 | 485 | >>> verifyObject(ITeam, not_a_person) | 485 | >>> verifyObject(ITeam, not_a_person) |
384 | 486 | True | 486 | True |
385 | 487 | 487 | ||
386 | 488 | >>> print removeSecurityProxy(not_a_person).password | ||
387 | 489 | None | ||
388 | 490 | |||
389 | 491 | The team owner is also added as an administrator of its team. | 488 | The team owner is also added as an administrator of its team. |
390 | 492 | 489 | ||
391 | 493 | >>> [member.name for member in not_a_person.adminmembers] | 490 | >>> [member.name for member in not_a_person.adminmembers] |
392 | 494 | 491 | ||
393 | === modified file 'lib/lp/registry/interfaces/person.py' | |||
394 | --- lib/lp/registry/interfaces/person.py 2011-12-30 06:14:56 +0000 | |||
395 | +++ lib/lp/registry/interfaces/person.py 2012-01-17 00:44:26 +0000 | |||
396 | @@ -145,7 +145,6 @@ | |||
397 | 145 | is_public_person_or_closed_team, | 145 | is_public_person_or_closed_team, |
398 | 146 | LogoImageUpload, | 146 | LogoImageUpload, |
399 | 147 | MugshotImageUpload, | 147 | MugshotImageUpload, |
400 | 148 | PasswordField, | ||
401 | 149 | PersonChoice, | 148 | PersonChoice, |
402 | 150 | PublicPersonChoice, | 149 | PublicPersonChoice, |
403 | 151 | StrippedTextLine, | 150 | StrippedTextLine, |
404 | @@ -766,8 +765,6 @@ | |||
405 | 766 | """IPerson attributes that require launchpad.View permission.""" | 765 | """IPerson attributes that require launchpad.View permission.""" |
406 | 767 | account = Object(schema=IAccount) | 766 | account = Object(schema=IAccount) |
407 | 768 | accountID = Int(title=_('Account ID'), required=True, readonly=True) | 767 | accountID = Int(title=_('Account ID'), required=True, readonly=True) |
408 | 769 | password = PasswordField( | ||
409 | 770 | title=_('Password'), required=True, readonly=False) | ||
410 | 771 | karma = exported( | 768 | karma = exported( |
411 | 772 | Int(title=_('Karma'), readonly=True, | 769 | Int(title=_('Karma'), readonly=True, |
412 | 773 | description=_('The cached total karma for this person.'))) | 770 | description=_('The cached total karma for this person.'))) |
413 | 774 | 771 | ||
414 | === modified file 'lib/lp/registry/model/person.py' | |||
415 | --- lib/lp/registry/model/person.py 2012-01-12 08:13:59 +0000 | |||
416 | +++ lib/lp/registry/model/person.py 2012-01-17 00:44:26 +0000 | |||
417 | @@ -260,10 +260,7 @@ | |||
418 | 260 | IEmailAddressSet, | 260 | IEmailAddressSet, |
419 | 261 | InvalidEmailAddress, | 261 | InvalidEmailAddress, |
420 | 262 | ) | 262 | ) |
425 | 263 | from lp.services.identity.model.account import ( | 263 | from lp.services.identity.model.account import Account |
422 | 264 | Account, | ||
423 | 265 | AccountPassword, | ||
424 | 266 | ) | ||
426 | 267 | from lp.services.identity.model.emailaddress import ( | 264 | from lp.services.identity.model.emailaddress import ( |
427 | 268 | EmailAddress, | 265 | EmailAddress, |
428 | 269 | HasOwnerMixin, | 266 | HasOwnerMixin, |
429 | @@ -528,24 +525,6 @@ | |||
430 | 528 | mugshot = ForeignKey( | 525 | mugshot = ForeignKey( |
431 | 529 | dbName='mugshot', foreignKey='LibraryFileAlias', default=None) | 526 | dbName='mugshot', foreignKey='LibraryFileAlias', default=None) |
432 | 530 | 527 | ||
433 | 531 | def _get_password(self): | ||
434 | 532 | # We have to remove the security proxy because the password is | ||
435 | 533 | # needed before we are authenticated. I'm not overly worried because | ||
436 | 534 | # this method is scheduled for demolition -- StuartBishop 20080514 | ||
437 | 535 | password = IStore(AccountPassword).find( | ||
438 | 536 | AccountPassword, accountID=self.accountID).one() | ||
439 | 537 | if password is None: | ||
440 | 538 | return None | ||
441 | 539 | else: | ||
442 | 540 | return password.password | ||
443 | 541 | |||
444 | 542 | def _set_password(self, value): | ||
445 | 543 | account = IMasterStore(Account).get(Account, self.accountID) | ||
446 | 544 | assert account is not None, 'No account for this Person.' | ||
447 | 545 | account.password = value | ||
448 | 546 | |||
449 | 547 | password = property(_get_password, _set_password) | ||
450 | 548 | |||
451 | 549 | def _get_account_status(self): | 528 | def _get_account_status(self): |
452 | 550 | account = IStore(Account).get(Account, self.accountID) | 529 | account = IStore(Account).get(Account, self.accountID) |
453 | 551 | if account is not None: | 530 | if account is not None: |
454 | 552 | 531 | ||
455 | === modified file 'lib/lp/testing/factory.py' | |||
456 | --- lib/lp/testing/factory.py 2012-01-05 09:02:37 +0000 | |||
457 | +++ lib/lp/testing/factory.py 2012-01-17 00:44:26 +0000 | |||
458 | @@ -647,9 +647,6 @@ | |||
459 | 647 | if homepage_content is not None: | 647 | if homepage_content is not None: |
460 | 648 | naked_person.homepage_content = homepage_content | 648 | naked_person.homepage_content = homepage_content |
461 | 649 | 649 | ||
462 | 650 | assert person.password is not None, ( | ||
463 | 651 | 'Password not set. Wrong default auth Store?') | ||
464 | 652 | |||
465 | 653 | if (time_zone is not None or latitude is not None or | 650 | if (time_zone is not None or latitude is not None or |
466 | 654 | longitude is not None): | 651 | longitude is not None): |
467 | 655 | naked_person.setLocation(latitude, longitude, time_zone, person) | 652 | naked_person.setLocation(latitude, longitude, time_zone, person) |
Thank you very much for this branch. This fixes a few bugs. I will link the ones that I find.