Merge lp:~adiroiban/launchpad/bug-340662-take-2 into lp:launchpad/db-devel
- bug-340662-take-2
- Merge into db-devel
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Brad Crittenden | ||||||||
Approved revision: | not available | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | lp:~adiroiban/launchpad/bug-340662-take-2 | ||||||||
Merge into: | lp:launchpad/db-devel | ||||||||
Diff against target: |
275 lines (+72/-52) 5 files modified
lib/canonical/launchpad/security.py (+3/-28) lib/lp/translations/browser/configure.zcml (+2/-2) lib/lp/translations/browser/potemplate.py (+9/-8) lib/lp/translations/configure.zcml (+2/-2) lib/lp/translations/stories/standalone/xx-potemplate-edit.txt (+56/-12) |
||||||||
To merge this branch: | bzr merge lp:~adiroiban/launchpad/bug-340662-take-2 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brad Crittenden (community) | code | Approve | |
Review via email: mp+17598@code.launchpad.net |
Commit message
Remove "name" field from POTemplate edit view. On POTemplateSubset url traversal, check permission on POFile. Remove EditPOTemplateS
Description of the change
Adi Roiban (adiroiban) wrote : | # |
Brad Crittenden (bac) wrote : | # |
Hi Adi,
Thanks for this branch.
As noted in IRC, there is an import violation that is clearly not your fault but needs to be fixed before landing this branch. Thanks for agreeing to look into it.
Also, when attempting the demo you describe in the MP I get an unauthorized error changing any field b/c date_last_updated requires launchpad.
Adi Roiban (adiroiban) wrote : | # |
Hi,
Thanks for the review.
The permission problem should be fixed now and test extended to check all fields.
Not sure if there will be branch for fixing the import problem. I will leave the import problem as a final commit.
Brad Crittenden (bac) wrote : | # |
Hi Adi,
Thanks again for your changes. Your use of removeSecurityProxy was a bit flawed, though, so date_last_updated was not being changed. This flaw also exposes a hole in the testing as there is not test to ensure the value actually got changed.
If you apply the following change it will work:
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -533,9 +533,8 @@
# We only update date_last_updated when translation_domain field
# is changed because is the only significative change that,
# somehow, affects the content of the potemplate.
- UTC = pytz.timezone(
- date_last_updated = removeSecurityP
- date_last_updated = datetime.
+ naked_context = removeSecurityP
+ naked_context.
Also, while you're there could you fix the preceding comment so that it reads:
We only change date_last_updated when the translation_domain field is changed because it is the only relevant field we care about regarding the date of last update.
In your test it would be great to grab the value of date_last_updated before and after the change and show that new > old. It's kind of messy in a story test but should be ok.
Please post a diff when you're done and I'll have a final look at it.
Adi Roiban (adiroiban) wrote : | # |
Hi,
Here is the diff. Hope everything is OK now.
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -530,12 +530,12 @@
if old_translation
- # We only update date_last_updated when translation_domain field
- # is changed because is the only significative change that,
- # somehow, affects the content of the potemplate.
+ # We only change date_last_updated when the translation_domain
+ # field is changed because it is the only relevant field we
+ # care about regarding the date of last update.
UTC = pytz.timezone(
- date_last_updated = removeSecurityP
- date_last_updated = datetime.
+ naked_context = removeSecurityP
+ naked_context.
@property
def cancel_url(self):
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -90,6 +90,22 @@
>>> browser.
'evolution-2.2'
+We remember the 'last_update_date' in order to check if it was changed
+after updating the template.
+
+ >>> from zope.component import getUtility
+ >>> from canonical.
+ >>> from lp.translations
+ >>> from lp.registry.
+ >>> login('<email address hidden>')
+ >>> evolution = getUtility(
+ >>> evolution_trunk = evolution.
+ >>> hoary_subset = POTemplateSubse
+ >>> evolution_template = hoary_subset.
+ ... 'evolution-2.2')
+ >>> previous_
+ >>> logout()
+
The visible fields can be changed and saved.
>>> browser.
@@ -120,3 +136,6 @@
'name12'
>>> browser.
'foo'
+ >>> previous_
+ True
+
Brad Crittenden (bac) wrote : | # |
Adi it looks very good now. Thanks for the fixes.
One tiny thing: remove the UTC = line and just use 'pytz.UTC' instead, as shown in my previous comment. pytz is nice enough to export a pre-defined UTC constant so we should use it rather than looking it up again.
Adi Roiban (adiroiban) wrote : | # |
Here is the diff after running the syntactic check for modified files.
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
@@ -469,7 +469,7 @@
return (user.inTeam(
- ['owner','drafter', 'assignee', 'approver']) or
+ ['owner', 'drafter', 'assignee', 'approver']) or
@@ -530,6 +530,7 @@
return True
return user.isOwner(
+
class AdminMilestoneB
permission = 'launchpad.Admin'
usedfor = IMilestone
@@ -574,7 +575,7 @@
def checkAuthentica
"""Is the user a privileged team member or Launchpad staff?
-
+
Return true when the user is a member of Launchpad admins,
registry experts, team admins, or the team owners.
"""
@@ -1431,7 +1432,6 @@
# This code MUST match the logic in IBuildSet.
# otherwise users are likely to get 403 errors, or worse.
-
def checkAuthentica
"""Private restricts to admins and archive members."""
if not self.obj.
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -391,9 +391,9 @@
-
+
<!-- Potemplate Portlets -->
-
+
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -533,9 +533,8 @@
# We only change date_last_updated when the translation_domain
# field is changed because it is the only relevant field we
# care about regarding the date of last update.
- UTC = pytz.timezone(
- naked_context.
+ naked_context.
@property
def cancel_url(self):
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -415,7 +415,7 @@
- name productseries distroseries sourcepackagename
+ name productseries distroseries sourcepackagename
Preview Diff
1 | === modified file 'lib/canonical/launchpad/security.py' |
2 | --- lib/canonical/launchpad/security.py 2010-01-14 11:36:51 +0000 |
3 | +++ lib/canonical/launchpad/security.py 2010-01-20 20:38:18 +0000 |
4 | @@ -469,7 +469,7 @@ |
5 | return (user.inTeam(self.obj.person) or |
6 | user.isOneOf( |
7 | self.obj.specification, |
8 | - ['owner','drafter', 'assignee', 'approver']) or |
9 | + ['owner', 'drafter', 'assignee', 'approver']) or |
10 | user.in_admin) |
11 | |
12 | |
13 | @@ -530,6 +530,7 @@ |
14 | return True |
15 | return user.isOwner(self.obj.target) |
16 | |
17 | + |
18 | class AdminMilestoneByLaunchpadAdmins(AuthorizationBase): |
19 | permission = 'launchpad.Admin' |
20 | usedfor = IMilestone |
21 | @@ -574,7 +575,7 @@ |
22 | |
23 | def checkAuthenticated(self, user): |
24 | """Is the user a privileged team member or Launchpad staff? |
25 | - |
26 | + |
27 | Return true when the user is a member of Launchpad admins, |
28 | registry experts, team admins, or the team owners. |
29 | """ |
30 | @@ -1144,8 +1145,6 @@ |
31 | self, user) |
32 | |
33 | |
34 | -# Please keep EditPOTemplateSubset in sync with this, unless you |
35 | -# know exactly what you are doing. |
36 | class EditPOTemplateDetails(AdminPOTemplateDetails, EditByOwnersOrAdmins): |
37 | permission = 'launchpad.Edit' |
38 | usedfor = IPOTemplate |
39 | @@ -1433,7 +1432,6 @@ |
40 | |
41 | # This code MUST match the logic in IBuildSet.getBuildsForBuilder() |
42 | # otherwise users are likely to get 403 errors, or worse. |
43 | - |
44 | def checkAuthenticated(self, user): |
45 | """Private restricts to admins and archive members.""" |
46 | if not self.obj.archive.private: |
47 | @@ -1648,29 +1646,6 @@ |
48 | return OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) |
49 | |
50 | |
51 | -# Please keep EditPOTemplate in sync with this, unless you |
52 | -# know exactly what you are doing. Note that this permission controls |
53 | -# access to browsing into individual potemplates, but it's on a different |
54 | -# object (POTemplateSubset) from EditPOTemplateDetails, |
55 | -# even though it looks almost identical |
56 | -class EditPOTemplateSubset(AuthorizationBase): |
57 | - permission = 'launchpad.Edit' |
58 | - usedfor = IPOTemplateSubset |
59 | - |
60 | - def checkAuthenticated(self, user): |
61 | - """Allow anyone with admin rights; owners, product owners and |
62 | - distribution owners; and for distros, translation group owners. |
63 | - """ |
64 | - if (self.obj.productseries is not None and |
65 | - user.inTeam(self.obj.productseries.product.owner)): |
66 | - # The user is the owner of the product. |
67 | - return True |
68 | - |
69 | - return ( |
70 | - AdminPOTemplateSubset(self.obj).checkAuthenticated(user) or |
71 | - EditByOwnersOrAdmins(self.obj).checkAuthenticated(user)) |
72 | - |
73 | - |
74 | class AdminDistroSeriesTranslations(AuthorizationBase): |
75 | permission = 'launchpad.TranslationsAdmin' |
76 | usedfor = IDistroSeries |
77 | |
78 | === modified file 'lib/lp/translations/browser/configure.zcml' |
79 | --- lib/lp/translations/browser/configure.zcml 2009-12-12 05:47:41 +0000 |
80 | +++ lib/lp/translations/browser/configure.zcml 2010-01-20 20:38:18 +0000 |
81 | @@ -391,9 +391,9 @@ |
82 | <browser:page |
83 | name="+chart" |
84 | template="../templates/potemplate-chart.pt"/> |
85 | - |
86 | + |
87 | <!-- Potemplate Portlets --> |
88 | - |
89 | + |
90 | <browser:page |
91 | name="+portlet-details" |
92 | template="../templates/potemplate-portlet-details.pt"/> |
93 | |
94 | === modified file 'lib/lp/translations/browser/potemplate.py' |
95 | --- lib/lp/translations/browser/potemplate.py 2009-12-28 22:58:18 +0000 |
96 | +++ lib/lp/translations/browser/potemplate.py 2010-01-20 20:38:18 +0000 |
97 | @@ -31,6 +31,7 @@ |
98 | from zope.component import getUtility |
99 | from zope.interface import implements |
100 | from zope.publisher.browser import FileUpload |
101 | +from zope.security.proxy import removeSecurityProxy |
102 | |
103 | from canonical.lazr.utils import smartquote |
104 | |
105 | @@ -291,7 +292,7 @@ |
106 | # SourcePackageName is needed to avoid hardcoding this URL. |
107 | url = (canonical_url( |
108 | self.context.distroseries, rootsite="translations") + |
109 | - "/+source/" + self.context.sourcepackagename.name + |
110 | + "/+source/" + self.context.sourcepackagename.name + |
111 | "/+translations") |
112 | else: |
113 | url = canonical_url( |
114 | @@ -512,7 +513,7 @@ |
115 | """View class that lets you edit a POTemplate object.""" |
116 | |
117 | schema = IPOTemplate |
118 | - field_names = ['name', 'translation_domain', 'description', 'priority', |
119 | + field_names = ['translation_domain', 'description', 'priority', |
120 | 'path', 'owner', 'iscurrent'] |
121 | label = 'Edit translation template details' |
122 | page_title = 'Edit details' |
123 | @@ -529,11 +530,11 @@ |
124 | product=context.product, distribution=context.distribution, |
125 | sourcepackagename=context.sourcepackagename) |
126 | if old_translation_domain != context.translation_domain: |
127 | - # We only update date_last_updated when translation_domain field |
128 | - # is changed because is the only significative change that, |
129 | - # somehow, affects the content of the potemplate. |
130 | - UTC = pytz.timezone('UTC') |
131 | - context.date_last_updated = datetime.datetime.now(UTC) |
132 | + # We only change date_last_updated when the translation_domain |
133 | + # field is changed because it is the only relevant field we |
134 | + # care about regarding the date of last update. |
135 | + naked_context = removeSecurityProxy(context) |
136 | + naked_context.date_last_updated = datetime.datetime.now(pytz.UTC) |
137 | |
138 | @property |
139 | def cancel_url(self): |
140 | @@ -713,7 +714,7 @@ |
141 | raise AssertionError('Unknown context for %s' % potemplate.title) |
142 | |
143 | if ((official_rosetta and potemplate.iscurrent) or |
144 | - check_permission('launchpad.Edit', self.context)): |
145 | + check_permission('launchpad.Edit', potemplate)): |
146 | # The target is using officially Launchpad Translations and the |
147 | # template is available to be translated, or the user is a is a |
148 | # Launchpad administrator in which case we show everything. |
149 | |
150 | === modified file 'lib/lp/translations/configure.zcml' |
151 | --- lib/lp/translations/configure.zcml 2010-01-12 23:25:16 +0000 |
152 | +++ lib/lp/translations/configure.zcml 2010-01-20 20:38:18 +0000 |
153 | @@ -411,11 +411,11 @@ |
154 | permission="launchpad.Edit" |
155 | set_attributes=" |
156 | owner priority description translation_domain path |
157 | - iscurrent name"/> |
158 | + iscurrent"/> |
159 | <require |
160 | permission="launchpad.TranslationsAdmin" |
161 | set_attributes=" |
162 | - productseries distroseries sourcepackagename |
163 | + name productseries distroseries sourcepackagename |
164 | sourcepackageversion binarypackagename languagepack |
165 | source_file_format source_file date_last_updated |
166 | from_sourcepackagename header"/> |
167 | |
168 | === modified file 'lib/lp/translations/stories/standalone/xx-potemplate-edit.txt' |
169 | --- lib/lp/translations/stories/standalone/xx-potemplate-edit.txt 2009-12-29 18:21:37 +0000 |
170 | +++ lib/lp/translations/stories/standalone/xx-potemplate-edit.txt 2010-01-20 20:38:18 +0000 |
171 | @@ -33,6 +33,8 @@ |
172 | >>> print browser.url |
173 | http://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2/+edit |
174 | |
175 | +The owner of a product has access to edit page for PO templates. |
176 | + |
177 | >>> browser = setupBrowser(auth='Basic test@canonical.com:test') |
178 | >>> browser.open( |
179 | ... 'http://translations.launchpad.dev/evolution/trunk/+pots/' |
180 | @@ -40,20 +42,17 @@ |
181 | >>> print browser.url |
182 | http://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2/+edit |
183 | |
184 | -We should be sure that the edit form get only the non admin fields. |
185 | +Owner will not see admin fields, only those fields designated for the |
186 | +edit page. |
187 | |
188 | >>> browser.getControl(name='field.name').value |
189 | - 'evolution-2.2' |
190 | - >>> browser.getControl(name='field.description').value |
191 | - 'Template for evolution in hoary' |
192 | + Traceback (most recent call last): |
193 | + ... |
194 | + LookupError:... |
195 | >>> browser.getControl(name='field.header').value |
196 | Traceback (most recent call last): |
197 | ... |
198 | LookupError:... |
199 | - >>> browser.getControl(name='field.iscurrent').value |
200 | - True |
201 | - >>> browser.getControl(name='field.owner').value |
202 | - 'rosetta-admins' |
203 | >>> browser.getControl(name='field.productseries').value |
204 | Traceback (most recent call last): |
205 | ... |
206 | @@ -78,20 +77,65 @@ |
207 | Traceback (most recent call last): |
208 | ... |
209 | LookupError:... |
210 | + >>> browser.getControl(name='field.description').value |
211 | + 'Template for evolution in hoary' |
212 | >>> browser.getControl(name='field.path').value |
213 | 'po/evolution-2.2.pot' |
214 | - |
215 | -And that we are able to POST it. |
216 | - |
217 | + >>> browser.getControl(name='field.iscurrent').value |
218 | + True |
219 | + >>> browser.getControl(name='field.owner').value |
220 | + 'rosetta-admins' |
221 | + >>> browser.getControl(name='field.priority').value |
222 | + '0' |
223 | + >>> browser.getControl(name='field.translation_domain').value |
224 | + 'evolution-2.2' |
225 | + |
226 | +We remember the 'last_update_date' in order to check if it was changed |
227 | +after updating the template. |
228 | + |
229 | + >>> from zope.component import getUtility |
230 | + >>> from canonical.launchpad.interfaces import ILaunchpadCelebrities |
231 | + >>> from lp.translations.model.potemplate import POTemplateSubset |
232 | + >>> from lp.registry.interfaces.product import IProductSet |
233 | + >>> login('foo.bar@canonical.com') |
234 | + >>> evolution = getUtility(IProductSet).getByName('evolution') |
235 | + >>> evolution_trunk = evolution.getSeries('trunk') |
236 | + >>> hoary_subset = POTemplateSubset(productseries=evolution_trunk) |
237 | + >>> evolution_template = hoary_subset.getPOTemplateByName( |
238 | + ... 'evolution-2.2') |
239 | + >>> previous_date_last_updated = evolution_template.date_last_updated |
240 | + >>> logout() |
241 | + |
242 | +The visible fields can be changed and saved. |
243 | + |
244 | + >>> browser.getControl(name='field.translation_domain').value = u'evo' |
245 | + >>> browser.getControl(name='field.priority').value = '100' |
246 | + >>> browser.getControl(name='field.iscurrent').value = False |
247 | + >>> browser.getControl(name='field.path').value = 'po/evolution.pot' |
248 | + >>> browser.getControl(name='field.owner').value = u'name12' |
249 | >>> browser.getControl(name='field.description').value = u'foo' |
250 | >>> browser.getControl('Change').click() |
251 | >>> print browser.url |
252 | http://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2 |
253 | |
254 | -And the changed value is stored. |
255 | +The changed values will be stored and visible by accesing again the |
256 | +edit page. |
257 | |
258 | >>> browser.open( |
259 | ... 'http://translations.launchpad.dev/evolution/trunk/+pots/' |
260 | ... 'evolution-2.2/+edit') |
261 | + >>> browser.getControl(name='field.translation_domain').value |
262 | + 'evo' |
263 | + >>> browser.getControl(name='field.priority').value |
264 | + '100' |
265 | + >>> browser.getControl(name='field.iscurrent').value |
266 | + False |
267 | + >>> browser.getControl(name='field.path').value |
268 | + 'po/evolution.pot' |
269 | + >>> browser.getControl(name='field.owner').value |
270 | + 'name12' |
271 | >>> browser.getControl(name='field.description').value |
272 | 'foo' |
273 | + >>> previous_date_last_updated != evolution_template.date_last_updated |
274 | + True |
275 | + |
= Bug #340662 and #507498= /code.edge. launchpad. net/~adiroiban/ launchpad/ bug-340662/ +merge/ 16629
This branch should fix the issues raised in this MP
https:/
== Proposed fix ==
Remove "name" field from PoTemplate edit page.
In the PoTemplateSubset URL traversal code, check permissions for the POTemplate and not for the context.
== Pre-implementation notes ==
Danilo asked to remove the "name" field from the edit page since it is to fragile to be edited by project owners.
Henning suggest that permission checking for POTemplateSubset URL traversal can be done for POTemplate and in this way we can remove the EditPOTemplateS ubSet from security.py
== Implementation details ==
Nothing special here.
== Tests ==
lp-test -t templates
== Demo and Q/A == /translations. launchpad. dev/evolution/ trunk/+ pots/evolution- 2.2/
As a product owner (ex. <email address hidden>) for a project (ex. evolution) go to a template page for that project
https:/
You should see the „Change details” that links to the +edit page
From this page you should be able to change template name, domain name, path, details, priority, owner and „accept translation”.
Change some values and then save them.
From the series +template page you will see „Edit” links for each template, including disabled templates: /translations. launchpad. dev/evolution/ trunk/+ templates
https:/
Disabling a template, it should still be in the list (with a red background) and you can still edit it.
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: /launchpad/ security. py translations/ configure. zcml translations/ browser/ potemplate. py translations/ stories/ standalone/ xx-potemplate- edit.txt
lib/canonical
lib/lp/
lib/lp/
lib/lp/