blob: 0c816946d780111b790f2fe5456bbeaa290e7e97 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Unit tests for the monorailrequest module."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import endpoints
11import mock
12import re
13import unittest
14
Adrià Vilanova Martínez9f9ade52022-10-10 23:20:11 +020015try:
16 from mox3 import mox
17except ImportError:
18 import mox
Copybara854996b2021-09-07 19:36:02 +000019import six
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010020import werkzeug
Copybara854996b2021-09-07 19:36:02 +000021
22from google.appengine.api import oauth
23from google.appengine.api import users
24
Copybara854996b2021-09-07 19:36:02 +000025from framework import exceptions
26from framework import monorailrequest
27from framework import permissions
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010028from mrproto import project_pb2
29from mrproto import tracker_pb2
Copybara854996b2021-09-07 19:36:02 +000030from services import service_manager
31from testing import fake
32from testing import testing_helpers
33from tracker import tracker_constants
34
35
36class HostportReTest(unittest.TestCase):
37
38 def testGood(self):
39 test_data = [
40 'localhost:8080',
41 'app.appspot.com',
42 'bugs-staging.chromium.org',
43 'vers10n-h3x-dot-app-id.appspot.com',
44 ]
45 for hostport in test_data:
46 self.assertTrue(monorailrequest._HOSTPORT_RE.match(hostport),
47 msg='Incorrectly rejected %r' % hostport)
48
49 def testBad(self):
50 test_data = [
51 '',
52 ' ',
53 '\t',
54 '\n',
55 '\'',
56 '"',
57 'version"cruft-dot-app-id.appspot.com',
58 '\nother header',
59 'version&cruft-dot-app-id.appspot.com',
60 ]
61 for hostport in test_data:
62 self.assertFalse(monorailrequest._HOSTPORT_RE.match(hostport),
63 msg='Incorrectly accepted %r' % hostport)
64
65
66class MonorailApiRequestUnitTest(unittest.TestCase):
67
68 def setUp(self):
69 self.cnxn = 'fake cnxn'
70 self.services = service_manager.Services(
71 config=fake.ConfigService(),
72 issue=fake.IssueService(),
73 project=fake.ProjectService(),
74 user=fake.UserService(),
75 usergroup=fake.UserGroupService())
76 self.project = self.services.project.TestAddProject(
77 'proj', project_id=789)
78 self.services.user.TestAddUser('requester@example.com', 111)
79 self.issue = fake.MakeTestIssue(
80 789, 1, 'sum', 'New', 111)
81 self.services.issue.TestAddIssue(self.issue)
82
83 self.patcher_1 = mock.patch('endpoints.get_current_user')
84 self.mock_endpoints_gcu = self.patcher_1.start()
85 self.mock_endpoints_gcu.return_value = None
86 self.patcher_2 = mock.patch('google.appengine.api.oauth.get_current_user')
87 self.mock_oauth_gcu = self.patcher_2.start()
88 self.mock_oauth_gcu.return_value = testing_helpers.Blank(
89 email=lambda: 'requester@example.com')
90
91 def tearDown(self):
92 mock.patch.stopall()
93
94 def testInit_NoProjectIssueOrViewedUser(self):
95 request = testing_helpers.Blank()
96 mar = monorailrequest.MonorailApiRequest(
97 request, self.services, cnxn=self.cnxn)
98 self.assertIsNone(mar.project)
99 self.assertIsNone(mar.issue)
100
101 def testInit_WithProject(self):
102 request = testing_helpers.Blank(projectId='proj')
103 mar = monorailrequest.MonorailApiRequest(
104 request, self.services, cnxn=self.cnxn)
105 self.assertEqual(self.project, mar.project)
106 self.assertIsNone(mar.issue)
107
108 def testInit_WithProjectAndIssue(self):
109 request = testing_helpers.Blank(
110 projectId='proj', issueId=1)
111 mar = monorailrequest.MonorailApiRequest(
112 request, self.services, cnxn=self.cnxn)
113 self.assertEqual(self.project, mar.project)
114 self.assertEqual(self.issue, mar.issue)
115
116 def testGetParam_Normal(self):
117 request = testing_helpers.Blank(q='owner:me')
118 mar = monorailrequest.MonorailApiRequest(
119 request, self.services, cnxn=self.cnxn)
120 self.assertEqual(None, mar.GetParam('unknown'))
121 self.assertEqual(100, mar.GetParam('num'))
122 self.assertEqual('owner:me', mar.GetParam('q'))
123
124 request = testing_helpers.Blank(q='owner:me', maxResults=200)
125 mar = monorailrequest.MonorailApiRequest(
126 request, self.services, cnxn=self.cnxn)
127 self.assertEqual(200, mar.GetParam('num'))
128
129
130class MonorailRequestUnitTest(unittest.TestCase):
131
132 def setUp(self):
133 self.services = service_manager.Services(
134 project=fake.ProjectService(),
135 user=fake.UserService(),
136 usergroup=fake.UserGroupService(),
137 features=fake.FeaturesService())
138 self.project = self.services.project.TestAddProject('proj')
139 self.hotlist = self.services.features.TestAddHotlist(
140 'TestHotlist', owner_ids=[111])
141 self.services.user.TestAddUser('jrobbins@example.com', 111)
142
143 self.mox = mox.Mox()
144 self.mox.StubOutWithMock(users, 'get_current_user')
145 users.get_current_user().AndReturn(None)
146 self.mox.ReplayAll()
147
148 def tearDown(self):
149 self.mox.UnsetStubs()
150
151 def testGetIntParam_ConvertsQueryParamToInt(self):
152 notice_id = 12345
153 mr = testing_helpers.MakeMonorailRequest(
154 path='/foo?notice=%s' % notice_id)
155
156 value = mr.GetIntParam('notice')
157 self.assertTrue(isinstance(value, int))
158 self.assertEqual(notice_id, value)
159
160 def testGetIntParam_ConvertsQueryParamToLong(self):
161 notice_id = 12345678901234567890
162 mr = testing_helpers.MakeMonorailRequest(
163 path='/foo?notice=%s' % notice_id)
164
165 value = mr.GetIntParam('notice')
166 self.assertTrue(isinstance(value, six.integer_types))
167 self.assertEqual(notice_id, value)
168
169 def testGetIntListParam_NoParam(self):
170 mr = monorailrequest.MonorailRequest(self.services)
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100171 mr.ParseRequest(testing_helpers.RequestStub('servlet'), self.services)
Copybara854996b2021-09-07 19:36:02 +0000172 self.assertEqual(mr.GetIntListParam('ids'), None)
173 self.assertEqual(mr.GetIntListParam('ids', default_value=['test']),
174 ['test'])
175
176 def testGetIntListParam_OneValue(self):
177 mr = monorailrequest.MonorailRequest(self.services)
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100178 request = testing_helpers.RequestStub('servlet?ids=11')
179 mr.ParseRequest(request, self.services)
Copybara854996b2021-09-07 19:36:02 +0000180 self.assertEqual(mr.GetIntListParam('ids'), [11])
181 self.assertEqual(mr.GetIntListParam('ids', default_value=['test']),
182 [11])
183
184 def testGetIntListParam_MultiValue(self):
185 mr = monorailrequest.MonorailRequest(self.services)
186 mr.ParseRequest(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100187 testing_helpers.RequestStub('servlet?ids=21,22,23'), self.services)
Copybara854996b2021-09-07 19:36:02 +0000188 self.assertEqual(mr.GetIntListParam('ids'), [21, 22, 23])
189 self.assertEqual(mr.GetIntListParam('ids', default_value=['test']),
190 [21, 22, 23])
191
192 def testGetIntListParam_BogusValue(self):
193 mr = monorailrequest.MonorailRequest(self.services)
194 with self.assertRaises(exceptions.InputException):
195 mr.ParseRequest(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100196 testing_helpers.RequestStub('servlet?ids=not_an_int'), self.services)
Copybara854996b2021-09-07 19:36:02 +0000197
198 def testGetIntListParam_Malformed(self):
199 mr = monorailrequest.MonorailRequest(self.services)
200 with self.assertRaises(exceptions.InputException):
201 mr.ParseRequest(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100202 testing_helpers.RequestStub('servlet?ids=31,32,,'), self.services)
Copybara854996b2021-09-07 19:36:02 +0000203
204 def testDefaultValuesNoUrl(self):
205 """If request has no param, default param values should be used."""
206 mr = monorailrequest.MonorailRequest(self.services)
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100207 mr.ParseRequest(testing_helpers.RequestStub('servlet'), self.services)
Copybara854996b2021-09-07 19:36:02 +0000208 self.assertEqual(mr.GetParam('r', 3), 3)
209 self.assertEqual(mr.GetIntParam('r', 3), 3)
210 self.assertEqual(mr.GetPositiveIntParam('r', 3), 3)
211 self.assertEqual(mr.GetIntListParam('r', [3, 4]), [3, 4])
212
213 def _MRWithMockRequest(
214 self, path, headers=None, *mr_args, **mr_kwargs):
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100215 request = testing_helpers.RequestStub(path, headers=headers)
Copybara854996b2021-09-07 19:36:02 +0000216 mr = monorailrequest.MonorailRequest(self.services, *mr_args, **mr_kwargs)
217 mr.ParseRequest(request, self.services)
218 return mr
219
220 def testParseQueryParameters(self):
221 mr = self._MRWithMockRequest(
222 '/p/proj/issues/list?q=foo+OR+bar&num=50')
223 self.assertEqual('foo OR bar', mr.query)
224 self.assertEqual(50, mr.num)
225
226 def testParseQueryParameters_ModeMissing(self):
227 mr = self._MRWithMockRequest(
228 '/p/proj/issues/list?q=foo+OR+bar&num=50')
229 self.assertEqual('list', mr.mode)
230
231 def testParseQueryParameters_ModeList(self):
232 mr = self._MRWithMockRequest(
233 '/p/proj/issues/list?q=foo+OR+bar&num=50&mode=')
234 self.assertEqual('list', mr.mode)
235
236 def testParseQueryParameters_ModeGrid(self):
237 mr = self._MRWithMockRequest(
238 '/p/proj/issues/list?q=foo+OR+bar&num=50&mode=grid')
239 self.assertEqual('grid', mr.mode)
240
241 def testParseQueryParameters_ModeChart(self):
242 mr = self._MRWithMockRequest(
243 '/p/proj/issues/list?q=foo+OR+bar&num=50&mode=chart')
244 self.assertEqual('chart', mr.mode)
245
246 def testParseRequest_Scheme(self):
247 mr = self._MRWithMockRequest('/p/proj/')
248 self.assertEqual('http', mr.request.scheme)
249
250 def testParseRequest_HostportAndCurrentPageURL(self):
251 mr = self._MRWithMockRequest('/p/proj/', headers={
252 'Host': 'example.com',
253 'Cookie': 'asdf',
254 })
255 self.assertEqual('http', mr.request.scheme)
256 self.assertEqual('example.com', mr.request.host)
257 self.assertEqual('http://example.com/p/proj/', mr.current_page_url)
258
259 def testParseRequest_ProjectFound(self):
260 mr = self._MRWithMockRequest('/p/proj/')
261 self.assertEqual(mr.project, self.project)
262
263 def testParseRequest_ProjectNotFound(self):
264 with self.assertRaises(exceptions.NoSuchProjectException):
265 self._MRWithMockRequest('/p/no-such-proj/')
266
267 def testViewedUser_WithEmail(self):
268 mr = self._MRWithMockRequest('/u/jrobbins@example.com/')
269 self.assertEqual('jrobbins@example.com', mr.viewed_username)
270 self.assertEqual(111, mr.viewed_user_auth.user_id)
271 self.assertEqual(
272 self.services.user.GetUser('fake cnxn', 111),
273 mr.viewed_user_auth.user_pb)
274
275 def testViewedUser_WithUserID(self):
276 mr = self._MRWithMockRequest('/u/111/')
277 self.assertEqual('jrobbins@example.com', mr.viewed_username)
278 self.assertEqual(111, mr.viewed_user_auth.user_id)
279 self.assertEqual(
280 self.services.user.GetUser('fake cnxn', 111),
281 mr.viewed_user_auth.user_pb)
282
283 def testViewedUser_NoSuchEmail(self):
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100284 with self.assertRaises(werkzeug.exceptions.HTTPException) as cm:
Copybara854996b2021-09-07 19:36:02 +0000285 self._MRWithMockRequest('/u/unknownuser@example.com/')
286 self.assertEqual(404, cm.exception.code)
287
288 def testViewedUser_NoSuchUserID(self):
289 with self.assertRaises(exceptions.NoSuchUserException):
290 self._MRWithMockRequest('/u/234521111/')
291
292 def testGetParam(self):
293 mr = testing_helpers.MakeMonorailRequest(
294 path='/foo?syn=error!&a=a&empty=',
295 params=dict(over1='over_value1', over2='over_value2'))
296
297 # test tampering
298 self.assertRaises(exceptions.InputException, mr.GetParam, 'a',
299 antitamper_re=re.compile(r'^$'))
300 self.assertRaises(exceptions.InputException, mr.GetParam,
301 'undefined', default_value='default',
302 antitamper_re=re.compile(r'^$'))
303
304 # test empty value
305 self.assertEqual('', mr.GetParam(
306 'empty', default_value='default', antitamper_re=re.compile(r'^$')))
307
308 # test default
309 self.assertEqual('default', mr.GetParam(
310 'undefined', default_value='default'))
311
312 def testComputeColSpec(self):
313 # No config passed, and nothing in URL
314 mr = testing_helpers.MakeMonorailRequest(
315 path='/p/proj/issues/detail?id=123')
316 mr.ComputeColSpec(None)
317 self.assertEqual(tracker_constants.DEFAULT_COL_SPEC, mr.col_spec)
318
319 # No config passed, but set in URL
320 mr = testing_helpers.MakeMonorailRequest(
321 path='/p/proj/issues/detail?id=123&colspec=a b C')
322 mr.ComputeColSpec(None)
323 self.assertEqual('a b C', mr.col_spec)
324
325 config = tracker_pb2.ProjectIssueConfig()
326
327 # No default in the config, and nothing in URL
328 mr = testing_helpers.MakeMonorailRequest(
329 path='/p/proj/issues/detail?id=123')
330 mr.ComputeColSpec(config)
331 self.assertEqual(tracker_constants.DEFAULT_COL_SPEC, mr.col_spec)
332
333 # No default in the config, but set in URL
334 mr = testing_helpers.MakeMonorailRequest(
335 path='/p/proj/issues/detail?id=123&colspec=a b C')
336 mr.ComputeColSpec(config)
337 self.assertEqual('a b C', mr.col_spec)
338
339 config.default_col_spec = 'd e f'
340
341 # Default in the config, and nothing in URL
342 mr = testing_helpers.MakeMonorailRequest(
343 path='/p/proj/issues/detail?id=123')
344 mr.ComputeColSpec(config)
345 self.assertEqual('d e f', mr.col_spec)
346
347 # Default in the config, but overrided via URL
348 mr = testing_helpers.MakeMonorailRequest(
349 path='/p/proj/issues/detail?id=123&colspec=a b C')
350 mr.ComputeColSpec(config)
351 self.assertEqual('a b C', mr.col_spec)
352
353 # project colspec contains hotlist columns
354 mr = testing_helpers.MakeMonorailRequest(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100355 path='/p/proj/issues/detail?id=123&colspec=Rank Adder Adder Owner')
Copybara854996b2021-09-07 19:36:02 +0000356 mr.ComputeColSpec(None)
357 self.assertEqual(tracker_constants.DEFAULT_COL_SPEC, mr.col_spec)
358
359 # hotlist columns are not deleted when page is a hotlist page
360 mr = testing_helpers.MakeMonorailRequest(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100361 path='/u/jrobbins@example.com/hotlists/TestHotlist?colspec=Rank Adder',
Copybara854996b2021-09-07 19:36:02 +0000362 hotlist=self.hotlist)
363 mr.ComputeColSpec(None)
364 self.assertEqual('Rank Adder', mr.col_spec)
365
366 def testComputeColSpec_XSS(self):
367 config_1 = tracker_pb2.ProjectIssueConfig()
368 config_2 = tracker_pb2.ProjectIssueConfig()
369 config_2.default_col_spec = "id '+alert(1)+'"
370 mr_1 = testing_helpers.MakeMonorailRequest(
371 path='/p/proj/issues/detail?id=123')
372 mr_2 = testing_helpers.MakeMonorailRequest(
373 path="/p/proj/issues/detail?id=123&colspec=id '+alert(1)+'")
374
375 # Normal colspec in config but malicious request
376 self.assertRaises(
377 exceptions.InputException,
378 mr_2.ComputeColSpec, config_1)
379
380 # Malicious colspec in config but normal request
381 self.assertRaises(
382 exceptions.InputException,
383 mr_1.ComputeColSpec, config_2)
384
385 # Malicious colspec in config and malicious request
386 self.assertRaises(
387 exceptions.InputException,
388 mr_2.ComputeColSpec, config_2)
389
390
391class CalcDefaultQueryTest(unittest.TestCase):
392
393 def setUp(self):
394 self.project = project_pb2.Project()
395 self.project.project_name = 'proj'
396 self.project.owner_ids = [111]
397 self.config = tracker_pb2.ProjectIssueConfig()
398
399 def testIssueListURL_NotDefaultCan(self):
400 mr = monorailrequest.MonorailRequest(None)
401 mr.query = None
402 mr.can = 1
403 self.assertEqual('', mr._CalcDefaultQuery())
404
405 def testIssueListURL_NoProject(self):
406 mr = monorailrequest.MonorailRequest(None)
407 mr.query = None
408 mr.can = 2
409 self.assertEqual('', mr._CalcDefaultQuery())
410
411 def testIssueListURL_NoConfig(self):
412 mr = monorailrequest.MonorailRequest(None)
413 mr.query = None
414 mr.can = 2
415 mr.project = self.project
416 self.assertEqual('', mr._CalcDefaultQuery())
417
418 def testIssueListURL_NotCustomized(self):
419 mr = monorailrequest.MonorailRequest(None)
420 mr.query = None
421 mr.can = 2
422 mr.project = self.project
423 mr.config = self.config
424 self.assertEqual('', mr._CalcDefaultQuery())
425
426 def testIssueListURL_Customized_Nonmember(self):
427 mr = monorailrequest.MonorailRequest(None)
428 mr.query = None
429 mr.can = 2
430 mr.project = self.project
431 mr.config = self.config
432 mr.config.member_default_query = 'owner:me'
433 self.assertEqual('', mr._CalcDefaultQuery())
434
435 mr.auth = testing_helpers.Blank(effective_ids=set())
436 self.assertEqual('', mr._CalcDefaultQuery())
437
438 mr.auth = testing_helpers.Blank(effective_ids={999})
439 self.assertEqual('', mr._CalcDefaultQuery())
440
441 def testIssueListURL_Customized_Member(self):
442 mr = monorailrequest.MonorailRequest(None)
443 mr.query = None
444 mr.can = 2
445 mr.project = self.project
446 mr.config = self.config
447 mr.config.member_default_query = 'owner:me'
448 mr.auth = testing_helpers.Blank(effective_ids={111})
449 self.assertEqual('owner:me', mr._CalcDefaultQuery())
450
451
452class TestMonorailRequestFunctions(unittest.TestCase):
453
454 def testExtractPathIdentifiers_ProjectOnly(self):
455 (username, project_name, hotlist_id,
456 hotlist_name) = monorailrequest._ParsePathIdentifiers(
457 '/p/proj/issues/list?q=foo+OR+bar&ts=1234')
458 self.assertIsNone(username)
459 self.assertIsNone(hotlist_id)
460 self.assertIsNone(hotlist_name)
461 self.assertEqual('proj', project_name)
462
463 def testExtractPathIdentifiers_ViewedUserOnly(self):
464 (username, project_name, hotlist_id,
465 hotlist_name) = monorailrequest._ParsePathIdentifiers(
466 '/u/jrobbins@example.com/')
467 self.assertEqual('jrobbins@example.com', username)
468 self.assertIsNone(project_name)
469 self.assertIsNone(hotlist_id)
470 self.assertIsNone(hotlist_name)
471
472 def testExtractPathIdentifiers_ViewedUserURLSpace(self):
473 (username, project_name, hotlist_id,
474 hotlist_name) = monorailrequest._ParsePathIdentifiers(
475 '/u/jrobbins@example.com/updates')
476 self.assertEqual('jrobbins@example.com', username)
477 self.assertIsNone(project_name)
478 self.assertIsNone(hotlist_id)
479 self.assertIsNone(hotlist_name)
480
481 def testExtractPathIdentifiers_ViewedGroupURLSpace(self):
482 (username, project_name, hotlist_id,
483 hotlist_name) = monorailrequest._ParsePathIdentifiers(
484 '/g/user-group@example.com/updates')
485 self.assertEqual('user-group@example.com', username)
486 self.assertIsNone(project_name)
487 self.assertIsNone(hotlist_id)
488 self.assertIsNone(hotlist_name)
489
490 def testExtractPathIdentifiers_HotlistIssuesURLSpaceById(self):
491 (username, project_name, hotlist_id,
492 hotlist_name) = monorailrequest._ParsePathIdentifiers(
493 '/u/jrobbins@example.com/hotlists/13124?q=stuff&ts=more')
494 self.assertIsNone(hotlist_name)
495 self.assertIsNone(project_name)
496 self.assertEqual('jrobbins@example.com', username)
497 self.assertEqual(13124, hotlist_id)
498
499 def testExtractPathIdentifiers_HotlistIssuesURLSpaceByName(self):
500 (username, project_name, hotlist_id,
501 hotlist_name) = monorailrequest._ParsePathIdentifiers(
502 '/u/jrobbins@example.com/hotlists/testname?q=stuff&ts=more')
503 self.assertIsNone(project_name)
504 self.assertIsNone(hotlist_id)
505 self.assertEqual('jrobbins@example.com', username)
506 self.assertEqual('testname', hotlist_name)
507
508 def testParseColSpec(self):
509 parse = monorailrequest.ParseColSpec
510 self.assertEqual(['PageName', 'Summary', 'Changed', 'ChangedBy'],
511 parse(u'PageName Summary Changed ChangedBy'))
512 self.assertEqual(['Foo-Bar', 'Foo-Bar-Baz', 'Release-1.2', 'Hey', 'There'],
513 parse('Foo-Bar Foo-Bar-Baz Release-1.2 Hey!There'))
514 self.assertEqual(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100515 [
516 b'\xe7\xaa\xbf\xe8\x8b\xa5\xe7\xb9\xb9'.decode('utf-8'),
517 b'\xe5\x9f\xba\xe5\x9c\xb0\xe3\x81\xaf'.decode('utf-8')
518 ],
519 parse(
520 b'\xe7\xaa\xbf\xe8\x8b\xa5\xe7\xb9\xb9 '
521 b'\xe5\x9f\xba\xe5\x9c\xb0\xe3\x81\xaf'.decode('utf-8')))
Copybara854996b2021-09-07 19:36:02 +0000522
523 def testParseColSpec_Dedup(self):
524 """An attacker cannot inflate response size by repeating a column."""
525 parse = monorailrequest.ParseColSpec
526 self.assertEqual([], parse(''))
527 self.assertEqual(
528 ['Aa', 'b', 'c/d'],
529 parse(u'Aa Aa AA AA AA b Aa aa c/d d c aA b aa B C/D D/aa/c'))
530 self.assertEqual(
531 ['A', 'b', 'c/d', 'e', 'f'],
532 parse(u'A b c/d e f g h i j a/k l m/c/a n/o'))
533
534 def testParseColSpec_Huge(self):
535 """An attacker cannot inflate response size with a huge column name."""
536 parse = monorailrequest.ParseColSpec
537 self.assertEqual(
538 ['Aa', 'b', 'c/d'],
539 parse(u'Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa b c/d'))
540
541 def testParseColSpec_Ignore(self):
542 """We ignore groupby and grid axes that would be useless."""
543 parse = monorailrequest.ParseColSpec
544 self.assertEqual(
545 ['Aa', 'b', 'c/d'],
546 parse(u'Aa AllLabels alllabels Id b opened/summary c/d',
547 ignore=tracker_constants.NOT_USED_IN_GRID_AXES))
548
549
550class TestPermissionLookup(unittest.TestCase):
551 OWNER_ID = 1
552 OTHER_USER_ID = 2
553
554 def setUp(self):
555 self.services = service_manager.Services(
556 project=fake.ProjectService(),
557 user=fake.UserService(),
558 usergroup=fake.UserGroupService())
559 self.services.user.TestAddUser('owner@gmail.com', self.OWNER_ID)
560 self.services.user.TestAddUser('user@gmail.com', self.OTHER_USER_ID)
561 self.live_project = self.services.project.TestAddProject(
562 'live', owner_ids=[self.OWNER_ID])
563 self.archived_project = self.services.project.TestAddProject(
564 'archived', owner_ids=[self.OWNER_ID],
565 state=project_pb2.ProjectState.ARCHIVED)
566 self.members_only_project = self.services.project.TestAddProject(
567 'members-only', owner_ids=[self.OWNER_ID],
568 access=project_pb2.ProjectAccess.MEMBERS_ONLY)
569
570 self.mox = mox.Mox()
571
572 def tearDown(self):
573 self.mox.UnsetStubs()
574
575 def CheckPermissions(self, perms, expect_view, expect_commit, expect_edit):
576 may_view = perms.HasPerm(permissions.VIEW, None, None)
577 self.assertEqual(expect_view, may_view)
578 may_commit = perms.HasPerm(permissions.COMMIT, None, None)
579 self.assertEqual(expect_commit, may_commit)
580 may_edit = perms.HasPerm(permissions.EDIT_PROJECT, None, None)
581 self.assertEqual(expect_edit, may_edit)
582
583 def MakeRequestAsUser(self, project_name, email):
584 self.mox.StubOutWithMock(users, 'get_current_user')
585 users.get_current_user().AndReturn(testing_helpers.Blank(
586 email=lambda: email))
587 self.mox.ReplayAll()
588
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100589 request = testing_helpers.RequestStub('/p/' + project_name)
Copybara854996b2021-09-07 19:36:02 +0000590 mr = monorailrequest.MonorailRequest(self.services)
591 with mr.profiler.Phase('parse user info'):
592 mr.ParseRequest(request, self.services)
593 print('mr.auth is %r' % mr.auth)
594 return mr
595
596 def testOwnerPermissions_Live(self):
597 mr = self.MakeRequestAsUser('live', 'owner@gmail.com')
598 self.CheckPermissions(mr.perms, True, True, True)
599
600 def testOwnerPermissions_Archived(self):
601 mr = self.MakeRequestAsUser('archived', 'owner@gmail.com')
602 self.CheckPermissions(mr.perms, True, False, True)
603
604 def testOwnerPermissions_MembersOnly(self):
605 mr = self.MakeRequestAsUser('members-only', 'owner@gmail.com')
606 self.CheckPermissions(mr.perms, True, True, True)
607
608 def testExternalUserPermissions_Live(self):
609 mr = self.MakeRequestAsUser('live', 'user@gmail.com')
610 self.CheckPermissions(mr.perms, True, False, False)
611
612 def testExternalUserPermissions_Archived(self):
613 mr = self.MakeRequestAsUser('archived', 'user@gmail.com')
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100614 self.CheckPermissions(mr.perms, True, False, False)
Copybara854996b2021-09-07 19:36:02 +0000615
616 def testExternalUserPermissions_MembersOnly(self):
617 mr = self.MakeRequestAsUser('members-only', 'user@gmail.com')
618 self.CheckPermissions(mr.perms, False, False, False)