Merge lp:~stevenk/launchpad/local-codeimports-bad into lp:launchpad

Proposed by Steve Kowalik on 2012-11-07
Status: Merged
Approved by: Steve Kowalik on 2012-11-07
Approved revision: no longer in the source branch.
Merged at revision: 16245
Proposed branch: lp:~stevenk/launchpad/local-codeimports-bad
Merge into: lp:launchpad
Diff against target: 382 lines (+74/-74)
7 files modified
lib/lp/code/browser/codeimport.py (+26/-24)
lib/lp/code/enums.py (+5/-0)
lib/lp/code/model/codeimport.py (+3/-6)
lib/lp/code/stories/codeimport/xx-create-codeimport.txt (+13/-0)
lib/lp/registry/browser/productseries.py (+12/-37)
lib/lp/registry/browser/tests/productseries-setbranch-view.txt (+13/-3)
lib/lp/translations/browser/tests/productseries-views.txt (+2/-4)
To merge this branch: bzr merge lp:~stevenk/launchpad/local-codeimports-bad
Reviewer Review Type Date Requested Status
Ian Booth (community) 2012-11-07 Approve on 2012-11-07
Review via email: mp+133183@code.launchpad.net

Commit Message

Deny creation of a code import if it is for a launchpad.net URL.

Description of the Change

Deny creation of a code import if it is for a launchpad.net URL. I have refactored the portion of the validation method for both ProductSeries:+setbranch and CodeImport:+new-import (and CodeImport:+edit-import) that check the URL passed it.

I have also forced this branch to net-neutral by performing a bunch of whitespace cleanup as well as defining a new code enum, much like the same thing we did with PUBLIC_INFORMATION_TYPES and friends while on Disclosure.

This branch does not completely close the bug, since it can still occur after this lands. The next step is to clean up the production data.

To post a comment you must log in.
Ian Booth (wallyworld) wrote :

Looks good. I like the refactoring.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/codeimport.py'
2--- lib/lp/code/browser/codeimport.py 2012-10-09 01:07:52 +0000
3+++ lib/lp/code/browser/codeimport.py 2012-11-08 02:13:22 +0000
4@@ -12,8 +12,10 @@
5 'CodeImportSetBreadcrumb',
6 'CodeImportSetNavigation',
7 'CodeImportSetView',
8+ 'validate_import_url',
9 ]
10
11+from urlparse import urlparse
12
13 from BeautifulSoup import BeautifulSoup
14 from lazr.restful.interface import (
15@@ -49,6 +51,7 @@
16 BranchSubscriptionNotificationLevel,
17 CodeImportReviewStatus,
18 CodeReviewNotificationLevel,
19+ NON_CVS_RCS_TYPES,
20 RevisionControlSystems,
21 )
22 from lp.code.errors import BranchExists
23@@ -167,10 +170,7 @@
24
25 def setSecondaryFieldError(self, field, error):
26 """Set the field error only if there isn't an error already."""
27- if self.getFieldError(field):
28- # Leave this one as it is often required or a validator error.
29- pass
30- else:
31+ if not self.getFieldError(field):
32 self.setFieldError(field, error)
33
34 def _validateCVS(self, cvs_root, cvs_module, existing_import=None):
35@@ -201,16 +201,9 @@
36 self.setSecondaryFieldError(
37 field_name, 'Enter the URL of a foreign VCS branch.')
38 else:
39- code_import = getUtility(ICodeImportSet).getByURL(url)
40- if (code_import is not None and
41- code_import != existing_import):
42- self.setFieldError(
43- field_name,
44- structured("""
45- This foreign branch URL is already specified for
46- the imported branch <a href="%s">%s</a>.""",
47- canonical_url(code_import.branch),
48- code_import.branch.unique_name))
49+ reason = validate_import_url(url, existing_import)
50+ if reason:
51+ self.setFieldError(field_name, reason)
52
53
54 class NewCodeImportForm(Interface):
55@@ -540,12 +533,8 @@
56 # fields, and vice versa.
57 if self.code_import.rcs_type == RevisionControlSystems.CVS:
58 self.form_fields = self.form_fields.omit('url')
59- elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
60- RevisionControlSystems.BZR_SVN,
61- RevisionControlSystems.GIT,
62- RevisionControlSystems.BZR):
63- self.form_fields = self.form_fields.omit(
64- 'cvs_root', 'cvs_module')
65+ elif self.code_import.rcs_type in NON_CVS_RCS_TYPES:
66+ self.form_fields = self.form_fields.omit('cvs_root', 'cvs_module')
67 else:
68 raise AssertionError('Unknown rcs_type for code import.')
69
70@@ -575,10 +564,7 @@
71 self._validateCVS(
72 data.get('cvs_root'), data.get('cvs_module'),
73 self.code_import)
74- elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
75- RevisionControlSystems.BZR_SVN,
76- RevisionControlSystems.GIT,
77- RevisionControlSystems.BZR):
78+ elif self.code_import.rcs_type in NON_CVS_RCS_TYPES:
79 self._validateURL(data.get('url'), self.code_import)
80 else:
81 raise AssertionError('Unknown rcs_type for code import.')
82@@ -593,3 +579,19 @@
83 def machines(self):
84 """Get the machines, sorted alphabetically by hostname."""
85 return getUtility(ICodeImportMachineSet).getAll()
86+
87+
88+def validate_import_url(url, existing_import=None):
89+ """Validate the given import URL."""
90+ if urlparse(url).netloc.endswith('launchpad.net'):
91+ return (
92+ "You can not create imports for branches that are hosted by "
93+ "Launchpad.")
94+ code_import = getUtility(ICodeImportSet).getByURL(url)
95+ if code_import is not None:
96+ if existing_import and code_import == existing_import:
97+ return None
98+ return structured(
99+ "This foreign branch URL is already specified for the imported "
100+ "branch <a href='%s'>%s</a>.", canonical_url(code_import.branch),
101+ code_import.branch.unique_name)
102
103=== modified file 'lib/lp/code/enums.py'
104--- lib/lp/code/enums.py 2012-10-11 04:14:37 +0000
105+++ lib/lp/code/enums.py 2012-11-08 02:13:22 +0000
106@@ -20,6 +20,7 @@
107 'CodeImportReviewStatus',
108 'CodeReviewNotificationLevel',
109 'CodeReviewVote',
110+ 'NON_CVS_RCS_TYPES',
111 'RevisionControlSystems',
112 'UICreatableBranchType',
113 ]
114@@ -856,3 +857,7 @@
115
116 The reviewer needs more information before making a decision.
117 """)
118+
119+NON_CVS_RCS_TYPES = (
120+ RevisionControlSystems.SVN, RevisionControlSystems.BZR_SVN,
121+ RevisionControlSystems.GIT, RevisionControlSystems.BZR)
122
123=== modified file 'lib/lp/code/model/codeimport.py'
124--- lib/lp/code/model/codeimport.py 2012-10-10 22:18:41 +0000
125+++ lib/lp/code/model/codeimport.py 2012-11-08 02:13:22 +0000
126@@ -38,6 +38,7 @@
127 CodeImportJobState,
128 CodeImportResultStatus,
129 CodeImportReviewStatus,
130+ NON_CVS_RCS_TYPES,
131 RevisionControlSystems,
132 )
133 from lp.code.errors import (
134@@ -71,8 +72,7 @@
135 _defaultOrder = ['id']
136
137 date_created = UtcDateTimeCol(notNull=True, default=DEFAULT)
138- branch = ForeignKey(dbName='branch', foreignKey='Branch',
139- notNull=True)
140+ branch = ForeignKey(dbName='branch', foreignKey='Branch', notNull=True)
141 registrant = ForeignKey(
142 dbName='registrant', foreignKey='Person',
143 storm_validator=validate_public_person, notNull=True)
144@@ -243,10 +243,7 @@
145 if rcs_type == RevisionControlSystems.CVS:
146 assert cvs_root is not None and cvs_module is not None
147 assert url is None
148- elif rcs_type in (RevisionControlSystems.SVN,
149- RevisionControlSystems.BZR_SVN,
150- RevisionControlSystems.GIT,
151- RevisionControlSystems.BZR):
152+ elif rcs_type in NON_CVS_RCS_TYPES:
153 assert cvs_root is None and cvs_module is None
154 assert url is not None
155 else:
156
157=== modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'
158--- lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2012-10-09 01:07:52 +0000
159+++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2012-11-08 02:13:22 +0000
160@@ -97,6 +97,19 @@
161 at http://user:password@bzr.example.com/firefox/trunk.
162 The next import is scheduled to run
163 as soon as possible.
164+
165+Specifing a Launchpad URL results in an error.
166+
167+ >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
168+ >>> browser.getControl('Branch Name').value = "invalid"
169+ >>> browser.getControl('Branch URL', index=0).value = (
170+ ... "http://bazaar.launchpad.net/firefox/trunk")
171+ >>> browser.getControl('Project').value = "firefox"
172+ >>> browser.getControl('Request Import').click()
173+ >>> for message in get_feedback_messages(browser.contents):
174+ ... print extract_text(message)
175+ There is 1 error.
176+ You can not create imports for branches that are hosted by Launchpad.
177
178 Requesting a Subversion import
179 ==============================
180
181=== modified file 'lib/lp/registry/browser/productseries.py'
182--- lib/lp/registry/browser/productseries.py 2012-10-16 15:12:09 +0000
183+++ lib/lp/registry/browser/productseries.py 2012-11-08 02:13:22 +0000
184@@ -86,6 +86,7 @@
185 from lp.bugs.interfaces.bugtask import IBugTaskSet
186 from lp.code.browser.branch import BranchNameValidationMixin
187 from lp.code.browser.branchref import BranchRef
188+from lp.code.browser.codeimport import validate_import_url
189 from lp.code.enums import (
190 BranchType,
191 RevisionControlSystems,
192@@ -156,8 +157,6 @@
193 """Return the series branch."""
194 if self.context.branch:
195 return BranchRef(self.context.branch)
196- else:
197- return None
198
199 @stepto('+pots')
200 def pots(self):
201@@ -660,10 +659,7 @@
202 return 'Edit %s %s series' % (
203 self.context.product.displayname, self.context.name)
204
205- @property
206- def page_title(self):
207- """The page title."""
208- return self.label
209+ page_title = label
210
211 def validate(self, data):
212 """See `LaunchpadFormView`."""
213@@ -697,10 +693,7 @@
214 return 'Delete %s %s series' % (
215 self.context.product.displayname, self.context.name)
216
217- @property
218- def page_title(self):
219- """The page title."""
220- return self.label
221+ page_title = label
222
223 @cachedproperty
224 def milestones(self):
225@@ -906,8 +899,7 @@
226 """Validate data for link-lp-bzr case."""
227 if 'branch_location' not in data:
228 self.setFieldError(
229- 'branch_location',
230- 'The branch location must be set.')
231+ 'branch_location', 'The branch location must be set.')
232
233 def _validateImportExternal(self, data):
234 """Validate data for import external case."""
235@@ -925,16 +917,9 @@
236 self.setFieldError(
237 'repo_url', 'You must set the external repository URL.')
238 else:
239- # Ensure this URL has not been imported before.
240- code_import = getUtility(ICodeImportSet).getByURL(repo_url)
241- if code_import is not None:
242- self.setFieldError(
243- 'repo_url',
244- structured("""
245- This foreign branch URL is already specified for
246- the imported branch <a href="%s">%s</a>.""",
247- canonical_url(code_import.branch),
248- code_import.branch.unique_name))
249+ reason = validate_import_url(repo_url)
250+ if reason:
251+ self.setFieldError('repo_url', reason)
252
253 # RCS type is mandatory.
254 # This condition should never happen since an initial value is set.
255@@ -945,19 +930,15 @@
256 'You must specify the type of RCS for the remote host.')
257 elif rcs_type == RevisionControlSystems.CVS:
258 if 'cvs_module' not in data:
259- self.setFieldError(
260- 'cvs_module',
261- 'The CVS module must be set.')
262+ self.setFieldError('cvs_module', 'The CVS module must be set.')
263 self._validateBranch(data)
264
265 def _validateBranch(self, data):
266 """Validate that branch name and owner are set."""
267 if 'branch_name' not in data:
268- self.setFieldError(
269- 'branch_name', 'The branch name must be set.')
270+ self.setFieldError('branch_name', 'The branch name must be set.')
271 if 'branch_owner' not in data:
272- self.setFieldError(
273- 'branch_owner', 'The branch owner must be set.')
274+ self.setFieldError('branch_owner', 'The branch owner must be set.')
275
276 def _setRequired(self, names, value):
277 """Mark the widget field as optional."""
278@@ -1043,12 +1024,8 @@
279 branch_name = data.get('branch_name')
280 branch_owner = data.get('branch_owner')
281
282- # Import or mirror an external branch.
283 if branch_type == IMPORT_EXTERNAL:
284- # Either create an externally hosted bzr branch
285- # (a.k.a. 'mirrored') or create a new code import.
286 rcs_type = data.get('rcs_type')
287- # We need to create an import request.
288 if rcs_type == RevisionControlSystems.CVS:
289 cvs_root = data.get('repo_url')
290 cvs_module = data.get('cvs_module')
291@@ -1069,8 +1046,7 @@
292 cvs_root=cvs_root,
293 cvs_module=cvs_module)
294 except BranchExists as e:
295- self._setBranchExists(
296- e.existing_branch, 'branch_name')
297+ self._setBranchExists(e.existing_branch, 'branch_name')
298 self.errors_in_action = True
299 # Abort transaction. This is normally handled
300 # by LaunchpadFormView, but we are already in
301@@ -1177,8 +1153,7 @@
302 class ProductSeriesRdfView(BaseRdfView):
303 """A view that sets its mime-type to application/rdf+xml"""
304
305- template = ViewPageTemplateFile(
306- '../templates/productseries-rdf.pt')
307+ template = ViewPageTemplateFile('../templates/productseries-rdf.pt')
308
309 @property
310 def filename(self):
311
312=== modified file 'lib/lp/registry/browser/tests/productseries-setbranch-view.txt'
313--- lib/lp/registry/browser/tests/productseries-setbranch-view.txt 2012-10-09 01:07:52 +0000
314+++ lib/lp/registry/browser/tests/productseries-setbranch-view.txt 2012-11-08 02:13:22 +0000
315@@ -138,7 +138,6 @@
316 ('repo_url'...The URI scheme "bzr+ssh" is not allowed. Only URIs with
317 the following schemes may be used: bzr, http, https'))
318
319-
320 A correct URL is accepted.
321
322 >>> form = {
323@@ -161,6 +160,17 @@
324 blazer-branch
325 >>> series.branch.registrant.name == driver.name
326 True
327+
328+External imports can not use an Launchpad URL.
329+
330+ >>> form['field.repo_url'] = 'http://bazaar.launchpad.net/firefox/foo'
331+ >>> form['field.branch_name'] = 'chevette-branch'
332+ >>> view = create_initialized_view(
333+ ... series, name='+setbranch', principal=driver, form=form)
334+ >>> transaction.commit()
335+ >>> for error in view.errors:
336+ ... print error
337+ You can not create imports for branches that are hosted by Launchpad.
338
339 Git branches cannnot use svn.
340
341@@ -303,7 +313,7 @@
342 <BLANKLINE>
343 This foreign branch URL is already specified for
344 the imported branch
345- <a href="http://.../chevy/chevette-branch">~.../chevy/chevette-branch</a>.
346+ <a href='http://.../chevy/chevette-branch'>~.../chevy/chevette-branch</a>.
347 >>> for notification in view.request.response.notifications:
348 ... print notification.message
349
350@@ -348,7 +358,7 @@
351 ... print error
352 <BLANKLINE>
353 This foreign branch URL is already specified for the imported branch
354- <a href="http://.../blazer-branch">...</a>.
355+ <a href='http://.../blazer-branch'>...</a>.
356 >>> print view.errors_in_action
357 False
358 >>> print view.next_url
359
360=== modified file 'lib/lp/translations/browser/tests/productseries-views.txt'
361--- lib/lp/translations/browser/tests/productseries-views.txt 2011-12-24 17:49:30 +0000
362+++ lib/lp/translations/browser/tests/productseries-views.txt 2012-11-08 02:13:22 +0000
363@@ -4,8 +4,7 @@
364 let's put this check in a convenient function.
365
366 >>> from zope.component import getUtility
367- >>> from lp.code.interfaces.branchjob import (
368- ... IRosettaUploadJobSource)
369+ >>> from lp.code.interfaces.branchjob import IRosettaUploadJobSource
370 >>> job_counter = 0
371 >>> def isUploadJobCreatedForBranch(productseries,
372 ... force_translations_upload=None):
373@@ -57,8 +56,7 @@
374 ... method='POST',
375 ... form={'field.branch': branch.unique_name,
376 ... 'field.actions.update': 'Update'})
377- >>> view = ProductSeriesLinkBranchView(
378- ... productseries, request)
379+ >>> view = ProductSeriesLinkBranchView(productseries, request)
380 >>> view.initialize()
381 >>> print isUploadJobCreatedForBranch(productseries)
382 True