test_pagure_flask_ui_issues_acl_checks.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. """
  4. (c) 2017 - Copyright Red Hat Inc
  5. Authors:
  6. Vivek Anand <vivekanand1101@gmail.com>
  7. """
  8. from __future__ import unicode_literals, absolute_import
  9. from unittest.case import SkipTest
  10. import json
  11. import unittest
  12. import shutil
  13. import sys
  14. import os
  15. try:
  16. import pyclamd
  17. except ImportError:
  18. pyclamd = None
  19. import tempfile
  20. import pygit2
  21. from mock import patch
  22. sys.path.insert(
  23. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  24. )
  25. import pagure.config
  26. import pagure.lib.query
  27. import tests
  28. class PagureFlaskIssuesACLtests(tests.Modeltests):
  29. """ Tests for flask issues controller of pagure for acls """
  30. @patch("pagure.lib.git.update_git")
  31. @patch("pagure.lib.notify.send_email")
  32. def test_view_issue_no_access(self, p_send_email, p_ugt):
  33. """ Test the view_issue endpoint. when a user has no access on repo """
  34. p_send_email.return_value = True
  35. p_ugt.return_value = True
  36. output = self.app.get("/foo/issue/1")
  37. self.assertEqual(output.status_code, 404)
  38. tests.create_projects(self.session)
  39. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  40. output = self.app.get("/test/issue/1")
  41. self.assertEqual(output.status_code, 404)
  42. # Create issues to play with
  43. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  44. msg = pagure.lib.query.new_issue(
  45. session=self.session,
  46. repo=repo,
  47. title="Test issue",
  48. content="We should work on this",
  49. user="pingou",
  50. )
  51. self.session.commit()
  52. self.assertEqual(msg.title, "Test issue")
  53. # Add milestone
  54. repo.milestones = {"77": None}
  55. self.session.add(repo)
  56. issue = pagure.lib.query.search_issues(
  57. self.session, repo=repo, issueid=1
  58. )
  59. pagure.lib.query.edit_issue(
  60. self.session, issue, user="pingou", milestone="77"
  61. )
  62. self.session.add(repo)
  63. self.session.add(issue)
  64. msg = pagure.lib.query.set_custom_key_fields(
  65. self.session,
  66. project=repo,
  67. fields=["abc", "xyz"],
  68. types=["boolean", "boolean"],
  69. data=[None, None],
  70. )
  71. self.assertEqual(msg, "List of custom fields updated")
  72. self.session.add(repo)
  73. msg = pagure.lib.query.set_custom_key_value(
  74. self.session,
  75. issue=issue,
  76. key=pagure.lib.query.get_custom_key(self.session, repo, "abc"),
  77. value=1,
  78. )
  79. self.session.add(issue)
  80. self.session.commit()
  81. output = self.app.get("/test/issue/1")
  82. self.assertEqual(output.status_code, 200)
  83. # Not authentified = No edit
  84. self.assertNotIn(
  85. '<a class="btn btn-outline-secondary btn-sm border-0" '
  86. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  87. output.get_data(as_text=True),
  88. )
  89. self.assertTrue(
  90. '<a href="/login/?next=http%3A%2F%2Flocalhost%2Ftest%2Fissue%2F1">'
  91. "Login</a>\n to comment on this ticket."
  92. in output.get_data(as_text=True)
  93. )
  94. user = tests.FakeUser()
  95. with tests.user_set(self.app.application, user):
  96. output = self.app.get("/test/issue/1")
  97. self.assertEqual(output.status_code, 200)
  98. output_text = output.get_data(as_text=True)
  99. # Not author nor admin = No edit
  100. self.assertNotIn(
  101. '<a class="btn btn-outline-secondary btn-sm border-0" '
  102. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  103. output_text,
  104. )
  105. self.assertNotIn(
  106. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  107. ' title="Delete this ticket">\n',
  108. output.get_data(as_text=True),
  109. )
  110. # no edit metadata
  111. self.assertNotIn(
  112. '<a class="btn btn-outline-primary border-0 btn-sm issue-metadata-display'
  113. ' editmetadatatoggle pointer inline-block"">'
  114. '<i class="fa fa-fw fa-pencil">',
  115. output_text,
  116. )
  117. self.assertNotIn(
  118. '<a href="/login/">Login</a> to comment on this ticket.',
  119. output_text,
  120. )
  121. # can view the milestone
  122. self.assertIn("<strong>Milestone</strong>", output_text)
  123. self.assertIn(
  124. '\n <a href="/test/roadmap/77/">'
  125. "\n 77\n",
  126. output_text,
  127. )
  128. # but can't edit them
  129. self.assertNotIn(
  130. '<select class="form-control c-select" id="milestone" '
  131. ' name="milestone"><option value=""></option><option '
  132. 'selected value="77">77</option></select>',
  133. output_text,
  134. )
  135. # can view depending
  136. self.assertIn(
  137. "<strong>Depending on</strong>", output.get_data(as_text=True)
  138. )
  139. # can't edit depending on
  140. self.assertNotIn(
  141. '<input class="form-control" id="depending" type="text"\n\
  142. placeholder="issue depending" name="depending"\n\
  143. value="" />',
  144. output_text,
  145. )
  146. # no checkbox for private
  147. self.assertNotIn(
  148. '<input id="private" name="private" type="checkbox" value="y">',
  149. output_text,
  150. )
  151. user.username = "foo"
  152. with tests.user_set(self.app.application, user):
  153. output = self.app.get("/test/issue/1")
  154. self.assertEqual(output.status_code, 200)
  155. output_text = output.get_data(as_text=True)
  156. self.assertNotIn(
  157. '<a class="btn btn-outline-secondary btn-sm border-0" '
  158. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  159. output_text,
  160. )
  161. self.assertNotIn(
  162. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  163. ' title="Delete this ticket">\n',
  164. output_text,
  165. )
  166. csrf_token = self.get_csrf(output=output)
  167. # no edit metadata
  168. self.assertNotIn(
  169. '<a class="btn btn-outline-primary border-0 btn-sm issue-metadata-display'
  170. ' editmetadatatoggle pointer inline-block">'
  171. '<i class="fa fa-fw fa-pencil">',
  172. output_text,
  173. )
  174. self.assertNotIn(
  175. '<a href="/login/">Login</a> to comment on this ticket.',
  176. output_text,
  177. )
  178. # can't see the custom field as a checkbox
  179. self.assertNotIn(
  180. '<input type="checkbox" '
  181. 'class="form-control" name="abc" id="abc"checked/>',
  182. output_text,
  183. )
  184. # can view the milestone
  185. self.assertIn(
  186. "<strong>Milestone</strong>", output.get_data(as_text=True)
  187. )
  188. self.assertIn(
  189. '<a href="/test/roadmap/77/">\n 77',
  190. output.get_data(as_text=True),
  191. )
  192. # but can't edit them
  193. self.assertNotIn(
  194. '<select class="form-control c-select" id="milestone" '
  195. ' name="milestone"><option value=""></option><option '
  196. 'selected value="77">77</option></select>',
  197. output_text,
  198. )
  199. # can view depending
  200. self.assertIn("<strong>Depending on</strong>", output_text)
  201. # can't edit depending on
  202. self.assertNotIn(
  203. '<input class="form-control" id="depending" type="text"\n\
  204. placeholder="issue depending" name="depending"\n\
  205. value="" />',
  206. output_text,
  207. )
  208. # no checkbox for private
  209. self.assertNotIn(
  210. '<input id="private" name="private" type="checkbox" value="y">',
  211. output_text,
  212. )
  213. # Create private issue
  214. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  215. msg = pagure.lib.query.new_issue(
  216. session=self.session,
  217. repo=repo,
  218. title="Test issue",
  219. content="We should work on this",
  220. user="pingou",
  221. private=True,
  222. )
  223. self.session.commit()
  224. self.assertEqual(msg.title, "Test issue")
  225. # Not logged in
  226. output = self.app.get("/test/issue/2")
  227. self.assertEqual(output.status_code, 404)
  228. # Wrong user
  229. user = tests.FakeUser()
  230. with tests.user_set(self.app.application, user):
  231. output = self.app.get("/test/issue/2")
  232. self.assertEqual(output.status_code, 404)
  233. # reporter
  234. user.username = "pingou"
  235. with tests.user_set(self.app.application, user):
  236. output = self.app.get("/test/issue/2")
  237. self.assertEqual(output.status_code, 200)
  238. output_text = output.get_data(as_text=True)
  239. self.assertIn(
  240. "<title>Issue #2: Test issue - test - Pagure</title>",
  241. output_text,
  242. )
  243. self.assertIn(
  244. '<span title="Private ticket" class="text-danger '
  245. 'fa fa-fw fa-lock"></span>',
  246. output_text,
  247. )
  248. self.assertIn(
  249. '<a class="btn btn-outline-secondary btn-sm border-0" '
  250. 'href="/test/issue/2/edit" title="Edit this issue">\n',
  251. output_text,
  252. )
  253. @patch("pagure.lib.git.update_git")
  254. @patch("pagure.lib.notify.send_email")
  255. def test_view_issue_ticket_access(self, p_send_email, p_ugt):
  256. """ Test the view_issue endpoint. when a user has ticket access on repo """
  257. p_send_email.return_value = True
  258. p_ugt.return_value = True
  259. output = self.app.get("/foo/issue/1")
  260. self.assertEqual(output.status_code, 404)
  261. tests.create_projects(self.session)
  262. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  263. output = self.app.get("/test/issue/1")
  264. self.assertEqual(output.status_code, 404)
  265. # Create issues to play with
  266. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  267. # Add user 'foo' with ticket access on repo
  268. msg = pagure.lib.query.add_user_to_project(
  269. self.session, repo, new_user="foo", user="pingou", access="ticket"
  270. )
  271. self.assertEqual(msg, "User added")
  272. self.session.commit()
  273. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  274. msg = pagure.lib.query.new_issue(
  275. session=self.session,
  276. repo=repo,
  277. title="Test issue",
  278. content="We should work on this",
  279. user="pingou",
  280. )
  281. self.session.commit()
  282. self.assertEqual(msg.title, "Test issue")
  283. # Add milestone
  284. repo.milestones = {"77": None}
  285. self.session.add(repo)
  286. issue = pagure.lib.query.search_issues(
  287. self.session, repo=repo, issueid=1
  288. )
  289. pagure.lib.query.edit_issue(
  290. self.session, issue, user="pingou", milestone="77"
  291. )
  292. self.session.add(repo)
  293. self.session.add(issue)
  294. msg = pagure.lib.query.set_custom_key_fields(
  295. self.session,
  296. project=repo,
  297. fields=["abc", "xyz"],
  298. types=["boolean", "boolean"],
  299. data=[None, None],
  300. )
  301. self.assertEqual(msg, "List of custom fields updated")
  302. self.session.add(repo)
  303. msg = pagure.lib.query.set_custom_key_value(
  304. self.session,
  305. issue=issue,
  306. key=pagure.lib.query.get_custom_key(self.session, repo, "abc"),
  307. value=1,
  308. )
  309. self.session.add(issue)
  310. self.session.commit()
  311. output = self.app.get("/test/issue/1")
  312. self.assertEqual(output.status_code, 200)
  313. output_text = output.get_data(as_text=True)
  314. # Not authentified = No edit
  315. self.assertNotIn(
  316. '<a class="btn btn-outline-secondary btn-sm border-0" '
  317. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  318. output_text,
  319. )
  320. self.assertIn(
  321. '<a href="/login/?next=http%3A%2F%2Flocalhost%2Ftest%2Fissue%2F1">'
  322. "Login</a>\n to comment on this ticket.",
  323. output_text,
  324. )
  325. user = tests.FakeUser()
  326. with tests.user_set(self.app.application, user):
  327. output = self.app.get("/test/issue/1")
  328. self.assertEqual(output.status_code, 200)
  329. output_text = output.get_data(as_text=True)
  330. # Not author nor admin = No edit
  331. self.assertNotIn(
  332. '<a class="btn btn-outline-secondary btn-sm border-0" '
  333. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  334. output_text,
  335. )
  336. self.assertNotIn(
  337. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  338. ' title="Delete this ticket">\n',
  339. output_text,
  340. )
  341. # no edit metadata
  342. self.assertNotIn(
  343. '<a class="btn btn-outline-primary border-0 btn-sm issue-metadata-display'
  344. ' editmetadatatoggle pointer inline-block">'
  345. '<i class="fa fa-fw fa-pencil">',
  346. output_text,
  347. )
  348. self.assertNotIn(
  349. '<a href="/login/">Login</a> to comment on this ticket.',
  350. output_text,
  351. )
  352. # can view the milestone
  353. self.assertIn("<strong>Milestone</strong>", output_text)
  354. self.assertIn(
  355. '<a href="/test/roadmap/77/">\n 77',
  356. output_text,
  357. )
  358. # but can't edit them
  359. self.assertNotIn(
  360. '<select class="form-control c-select" id="milestone" '
  361. ' name="milestone"><option value=""></option><option '
  362. 'selected value="77">77</option></select>',
  363. output_text,
  364. )
  365. # can view depending
  366. self.assertIn("<strong>Depending on</strong>", output_text)
  367. # can't edit depending on
  368. self.assertNotIn(
  369. '<input class="form-control" id="depending" type="text"\n\
  370. placeholder="issue depending" name="depending"\n\
  371. value="" />',
  372. output_text,
  373. )
  374. # no checkbox for private
  375. self.assertNotIn(
  376. '<input id="private" name="private" type="checkbox" value="y">',
  377. output_text,
  378. )
  379. user.username = "foo"
  380. with tests.user_set(self.app.application, user):
  381. output = self.app.get("/test/issue/1")
  382. self.assertEqual(output.status_code, 200)
  383. output_text = output.get_data(as_text=True)
  384. # the user can't edit the issue
  385. self.assertNotIn(
  386. '<a class="btn btn-outline-secondary btn-sm border-0" '
  387. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  388. output_text,
  389. )
  390. # the user still can't delete the ticket
  391. self.assertNotIn(
  392. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  393. ' title="Delete this ticket">\n',
  394. output_text,
  395. )
  396. csrf_token = self.get_csrf(output=output)
  397. # the user can do the following things
  398. # edit metadata
  399. self.assertIn(
  400. '<a class="btn btn-outline-primary border-0 btn-sm '
  401. "issue-metadata-display editmetadatatoggle pointer inline-block"
  402. '"><i class="fa fa-fw fa-pencil"></i></a>',
  403. output_text,
  404. )
  405. # can view the milestone
  406. self.assertIn("<strong>Milestone</strong>", output_text)
  407. self.assertIn(
  408. '<a href="/test/roadmap/77/">\n 77',
  409. output_text,
  410. )
  411. # can edit them
  412. self.assertIn(
  413. '<select class="form-control c-select" id="milestone" '
  414. 'name="milestone"><option value=""></option><option selected '
  415. 'value="77">77</option></select>\n <div>\n',
  416. output_text,
  417. )
  418. # can view depending
  419. self.assertIn("<strong>Depending on</strong>", output_text)
  420. # can edit depending on
  421. self.assertIn(
  422. '<input class="form-control" id="depending" type="text"'
  423. '\n placeholder="issue depending" name="depending"\n',
  424. output_text,
  425. )
  426. # the user should be able to do public -> private
  427. # the other way round won't be possible since GET and POST
  428. # to this endpoint for this user will be blocked
  429. # checkbox for private
  430. self.assertIn(
  431. '<input id="private" name="private" type="checkbox" value="y">',
  432. output_text,
  433. )
  434. # Create private issue
  435. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  436. msg = pagure.lib.query.new_issue(
  437. session=self.session,
  438. repo=repo,
  439. title="Test issue",
  440. content="We should work on this",
  441. user="pingou",
  442. private=True,
  443. )
  444. self.session.commit()
  445. self.assertEqual(msg.title, "Test issue")
  446. # Not logged in
  447. output = self.app.get("/test/issue/2")
  448. self.assertEqual(output.status_code, 404)
  449. # Wrong user
  450. user = tests.FakeUser()
  451. with tests.user_set(self.app.application, user):
  452. output = self.app.get("/test/issue/2")
  453. self.assertEqual(output.status_code, 404)
  454. # reporter
  455. user.username = "pingou"
  456. with tests.user_set(self.app.application, user):
  457. output = self.app.get("/test/issue/2")
  458. self.assertEqual(output.status_code, 200)
  459. output_text = output.get_data(as_text=True)
  460. self.assertIn(
  461. "<title>Issue #2: Test issue - test - Pagure</title>",
  462. output_text,
  463. )
  464. self.assertIn(
  465. '<span title="Private ticket" class="text-danger '
  466. 'fa fa-fw fa-lock"></span>',
  467. output_text,
  468. )
  469. self.assertIn(
  470. '<a class="btn btn-outline-secondary btn-sm border-0" '
  471. 'href="/test/issue/2/edit" title="Edit this issue">\n',
  472. output_text,
  473. )
  474. @patch("pagure.lib.git.update_git")
  475. @patch("pagure.lib.notify.send_email")
  476. def test_view_issue_commit_access(self, p_send_email, p_ugt):
  477. """ Test the view_issue endpoint. when a user has commit access on repo """
  478. p_send_email.return_value = True
  479. p_ugt.return_value = True
  480. output = self.app.get("/foo/issue/1")
  481. self.assertEqual(output.status_code, 404)
  482. tests.create_projects(self.session)
  483. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  484. output = self.app.get("/test/issue/1")
  485. self.assertEqual(output.status_code, 404)
  486. # Create issues to play with
  487. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  488. # Add user 'foo' with ticket access on repo
  489. msg = pagure.lib.query.add_user_to_project(
  490. self.session, repo, new_user="foo", user="pingou", access="commit"
  491. )
  492. self.assertEqual(msg, "User added")
  493. self.session.commit()
  494. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  495. msg = pagure.lib.query.new_issue(
  496. session=self.session,
  497. repo=repo,
  498. title="Test issue",
  499. content="We should work on this",
  500. user="pingou",
  501. )
  502. self.session.commit()
  503. self.assertEqual(msg.title, "Test issue")
  504. # Add milestone
  505. repo.milestones = {"77": None}
  506. self.session.add(repo)
  507. issue = pagure.lib.query.search_issues(
  508. self.session, repo=repo, issueid=1
  509. )
  510. pagure.lib.query.edit_issue(
  511. self.session, issue, user="pingou", milestone="77"
  512. )
  513. self.session.add(repo)
  514. self.session.add(issue)
  515. msg = pagure.lib.query.set_custom_key_fields(
  516. self.session,
  517. project=repo,
  518. fields=["abc", "xyz"],
  519. types=["boolean", "boolean"],
  520. data=[None, None],
  521. )
  522. self.assertEqual(msg, "List of custom fields updated")
  523. self.session.add(repo)
  524. msg = pagure.lib.query.set_custom_key_value(
  525. self.session,
  526. issue=issue,
  527. key=pagure.lib.query.get_custom_key(self.session, repo, "abc"),
  528. value=1,
  529. )
  530. self.session.add(issue)
  531. self.session.commit()
  532. output = self.app.get("/test/issue/1")
  533. self.assertEqual(output.status_code, 200)
  534. output_text = output.get_data(as_text=True)
  535. # Not authentified = No edit
  536. self.assertNotIn(
  537. '<a class="btn btn-outline-secondary btn-sm border-0" '
  538. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  539. output_text,
  540. )
  541. self.assertTrue(
  542. '<a href="/login/?next=http%3A%2F%2Flocalhost%2Ftest%2Fissue%2F1">'
  543. "Login</a>\n to comment on this ticket.",
  544. output_text,
  545. )
  546. user = tests.FakeUser()
  547. with tests.user_set(self.app.application, user):
  548. output = self.app.get("/test/issue/1")
  549. self.assertEqual(output.status_code, 200)
  550. output_text = output.get_data(as_text=True)
  551. # Not author nor admin = No edit
  552. self.assertNotIn(
  553. '<a class="btn btn-outline-secondary btn-sm border-0" '
  554. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  555. output_text,
  556. )
  557. self.assertNotIn(
  558. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  559. ' title="Delete this ticket">\n',
  560. output_text,
  561. )
  562. # no edit metadata
  563. self.assertNotIn(
  564. '<a class="btn btn-outline-primary border-0 btn-sm issue-metadata-display'
  565. ' editmetadatatoggle pointer inline-block">'
  566. '<i class="fa fa-fw fa-pencil">',
  567. output_text,
  568. )
  569. self.assertNotIn(
  570. '<a href="/login/">Login</a> to comment on this ticket.',
  571. output_text,
  572. )
  573. # can view the milestone
  574. self.assertIn("<strong>Milestone</strong>", output_text)
  575. self.assertIn(
  576. '<a href="/test/roadmap/77/">\n 77',
  577. output_text,
  578. )
  579. # but can't edit them
  580. self.assertNotIn(
  581. '<select class="form-control c-select" id="milestone" '
  582. ' name="milestone"><option value=""></option><option '
  583. 'selected value="77">77</option></select>',
  584. output_text,
  585. )
  586. # can view depending
  587. self.assertIn("<strong>Depending on</strong>", output_text)
  588. # can't edit depending on
  589. self.assertNotIn(
  590. '<input class="form-control" id="depending" type="text"\n\
  591. placeholder="issue depending" name="depending"\n\
  592. value="" />',
  593. output_text,
  594. )
  595. # no checkbox for private
  596. self.assertNotIn(
  597. '<input id="private" name="private" type="checkbox" value="y">',
  598. output_text,
  599. )
  600. user.username = "foo"
  601. with tests.user_set(self.app.application, user):
  602. output = self.app.get("/test/issue/1")
  603. self.assertEqual(output.status_code, 200)
  604. output_text = output.get_data(as_text=True)
  605. # the user can edit the issue
  606. self.assertIn(
  607. '<a class="btn btn-outline-secondary btn-sm border-0" '
  608. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  609. output_text,
  610. )
  611. # the user can delete the ticket
  612. self.assertIn(
  613. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  614. ' title="Delete this ticket">\n',
  615. output_text,
  616. )
  617. csrf_token = self.get_csrf(output=output)
  618. # the user can do the following things
  619. # edit metadata
  620. self.assertIn(
  621. '<a class="btn btn-outline-primary border-0 btn-sm '
  622. "issue-metadata-display editmetadatatoggle pointer inline-block"
  623. '"><i class="fa fa-fw fa-pencil"></i></a>',
  624. output_text,
  625. )
  626. # can view the milestone
  627. self.assertIn("<strong>Milestone</strong>", output_text)
  628. self.assertIn(
  629. '<a href="/test/roadmap/77/">\n 77',
  630. output_text,
  631. )
  632. # can edit them
  633. self.assertIn(
  634. '<select class="form-control c-select" id="milestone" '
  635. 'name="milestone"><option value=""></option><option selected '
  636. 'value="77">77</option></select>\n <div>\n',
  637. output_text,
  638. )
  639. # can view depending
  640. self.assertIn("<strong>Depending on</strong>", output_text)
  641. # can edit depending on
  642. self.assertIn(
  643. '<input class="form-control" id="depending" type="text"'
  644. '\n placeholder="issue depending" name="depending"\n',
  645. output_text,
  646. )
  647. # the user should be able to do public -> private
  648. # the other way round won't be possible since GET and POST
  649. # to this endpoint for this user will be blocked
  650. # checkbox for private
  651. self.assertIn(
  652. '<input id="private" name="private" type="checkbox" value="y">',
  653. output_text,
  654. )
  655. # Create private issue
  656. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  657. msg = pagure.lib.query.new_issue(
  658. session=self.session,
  659. repo=repo,
  660. title="Test issue",
  661. content="We should work on this",
  662. user="pingou",
  663. private=True,
  664. )
  665. self.session.commit()
  666. self.assertEqual(msg.title, "Test issue")
  667. # Not logged in
  668. output = self.app.get("/test/issue/2")
  669. self.assertEqual(output.status_code, 404)
  670. # Wrong user
  671. user = tests.FakeUser()
  672. with tests.user_set(self.app.application, user):
  673. output = self.app.get("/test/issue/2")
  674. self.assertEqual(output.status_code, 404)
  675. # reporter
  676. user.username = "pingou"
  677. with tests.user_set(self.app.application, user):
  678. output = self.app.get("/test/issue/2")
  679. self.assertEqual(output.status_code, 200)
  680. output_text = output.get_data(as_text=True)
  681. self.assertIn(
  682. "<title>Issue #2: Test issue - test - Pagure</title>",
  683. output_text,
  684. )
  685. self.assertIn(
  686. '<span title="Private ticket" class="text-danger '
  687. 'fa fa-fw fa-lock"></span>',
  688. output_text,
  689. )
  690. self.assertIn(
  691. '<a class="btn btn-outline-secondary btn-sm border-0" '
  692. 'href="/test/issue/2/edit" title="Edit this issue">\n',
  693. output_text,
  694. )
  695. @patch("pagure.lib.git.update_git")
  696. @patch("pagure.lib.notify.send_email")
  697. def test_view_issue_admin_access(self, p_send_email, p_ugt):
  698. """ Test the view_issue endpoint. when a user has admin access on repo """
  699. p_send_email.return_value = True
  700. p_ugt.return_value = True
  701. output = self.app.get("/foo/issue/1")
  702. self.assertEqual(output.status_code, 404)
  703. tests.create_projects(self.session)
  704. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  705. output = self.app.get("/test/issue/1")
  706. self.assertEqual(output.status_code, 404)
  707. # Create issues to play with
  708. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  709. # Add user 'foo' with ticket access on repo
  710. msg = pagure.lib.query.add_user_to_project(
  711. self.session, repo, new_user="foo", user="pingou", access="admin"
  712. )
  713. self.assertEqual(msg, "User added")
  714. self.session.commit()
  715. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  716. msg = pagure.lib.query.new_issue(
  717. session=self.session,
  718. repo=repo,
  719. title="Test issue",
  720. content="We should work on this",
  721. user="pingou",
  722. )
  723. self.session.commit()
  724. self.assertEqual(msg.title, "Test issue")
  725. # Add milestone
  726. repo.milestones = {"77": None}
  727. self.session.add(repo)
  728. issue = pagure.lib.query.search_issues(
  729. self.session, repo=repo, issueid=1
  730. )
  731. pagure.lib.query.edit_issue(
  732. self.session, issue, user="pingou", milestone="77"
  733. )
  734. self.session.add(repo)
  735. self.session.add(issue)
  736. msg = pagure.lib.query.set_custom_key_fields(
  737. self.session,
  738. project=repo,
  739. fields=["abc", "xyz"],
  740. types=["boolean", "boolean"],
  741. data=[None, None],
  742. )
  743. self.assertEqual(msg, "List of custom fields updated")
  744. self.session.add(repo)
  745. msg = pagure.lib.query.set_custom_key_value(
  746. self.session,
  747. issue=issue,
  748. key=pagure.lib.query.get_custom_key(self.session, repo, "abc"),
  749. value=1,
  750. )
  751. self.session.add(issue)
  752. self.session.commit()
  753. output = self.app.get("/test/issue/1")
  754. self.assertEqual(output.status_code, 200)
  755. # Not authentified = No edit
  756. self.assertNotIn(
  757. '<a class="btn btn-outline-secondary btn-sm border-0" '
  758. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  759. output.get_data(as_text=True),
  760. )
  761. self.assertTrue(
  762. '<a href="/login/?next=http%3A%2F%2Flocalhost%2Ftest%2Fissue%2F1">'
  763. "Login</a>\n to comment on this ticket."
  764. in output.get_data(as_text=True)
  765. )
  766. user = tests.FakeUser()
  767. with tests.user_set(self.app.application, user):
  768. output = self.app.get("/test/issue/1")
  769. self.assertEqual(output.status_code, 200)
  770. output_text = output.get_data(as_text=True)
  771. # Not author nor admin = No edit
  772. self.assertNotIn(
  773. '<a class="btn btn-outline-secondary btn-sm border-0" '
  774. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  775. output_text,
  776. )
  777. self.assertNotIn(
  778. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  779. ' title="Delete this ticket">\n',
  780. output_text,
  781. )
  782. # no edit metadata
  783. self.assertNotIn(
  784. '<a class="btn btn-outline-primary border-0 btn-sm issue-metadata-display'
  785. ' editmetadatatoggle pointer inline-block">'
  786. '<i class="fa fa-fw fa-pencil">',
  787. output_text,
  788. )
  789. self.assertNotIn(
  790. '<a href="/login/">Login</a> to comment on this ticket.',
  791. output_text,
  792. )
  793. # can view the milestone
  794. self.assertIn("<strong>Milestone</strong>", output_text)
  795. self.assertIn(
  796. '<a href="/test/roadmap/77/">\n 77',
  797. output_text,
  798. )
  799. # but can't edit them
  800. self.assertNotIn(
  801. '<select class="form-control c-select" id="milestone" '
  802. ' name="milestone"><option value=""></option><option '
  803. 'selected value="77">77</option></select>',
  804. output_text,
  805. )
  806. # can view depending
  807. self.assertIn("<strong>Depending on</strong>", output_text)
  808. # can't edit depending on
  809. self.assertNotIn(
  810. '<input class="form-control" id="depending" type="text"\n\
  811. placeholder="issue depending" name="depending"\n\
  812. value="" />',
  813. output_text,
  814. )
  815. # no checkbox for private
  816. self.assertNotIn(
  817. '<input id="private" name="private" type="checkbox" value="y">',
  818. output_text,
  819. )
  820. user.username = "foo"
  821. with tests.user_set(self.app.application, user):
  822. output = self.app.get("/test/issue/1")
  823. self.assertEqual(output.status_code, 200)
  824. output_text = output.get_data(as_text=True)
  825. # the user can edit the issue
  826. self.assertIn(
  827. '<a class="btn btn-outline-secondary btn-sm border-0" '
  828. 'href="/test/issue/1/edit" title="Edit this issue">\n',
  829. output_text,
  830. )
  831. self.assertIn(
  832. '<a class="dropdown-item text-danger pointer" id="closeticket"\n'
  833. ' title="Delete this ticket">\n',
  834. output_text,
  835. )
  836. csrf_token = self.get_csrf(output=output)
  837. # the user can do the following things
  838. # edit metadata
  839. self.assertIn(
  840. '<a class="btn btn-outline-primary border-0 btn-sm '
  841. "issue-metadata-display editmetadatatoggle pointer inline-block"
  842. '"><i class="fa fa-fw fa-pencil"></i></a>',
  843. output_text,
  844. )
  845. # can view the milestone
  846. self.assertIn("<strong>Milestone</strong>", output_text)
  847. self.assertIn(
  848. '<a href="/test/roadmap/77/">\n 77',
  849. output_text,
  850. )
  851. # can edit them
  852. self.assertIn(
  853. '<select class="form-control c-select" id="milestone" '
  854. 'name="milestone"><option value=""></option><option selected '
  855. 'value="77">77</option></select>\n <div>\n',
  856. output_text,
  857. )
  858. # can view depending
  859. self.assertIn("<strong>Depending on</strong>", output_text)
  860. # can edit depending on
  861. self.assertIn(
  862. '<input class="form-control" id="depending" type="text"'
  863. '\n placeholder="issue depending" name="depending"\n',
  864. output_text,
  865. )
  866. # the user should be able to do public -> private
  867. # the other way round won't be possible since GET and POST
  868. # to this endpoint for this user will be blocked
  869. # checkbox for private
  870. self.assertIn(
  871. '<input id="private" name="private" type="checkbox" value="y">',
  872. output_text,
  873. )
  874. # Create private issue
  875. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  876. msg = pagure.lib.query.new_issue(
  877. session=self.session,
  878. repo=repo,
  879. title="Test issue",
  880. content="We should work on this",
  881. user="pingou",
  882. private=True,
  883. )
  884. self.session.commit()
  885. self.assertEqual(msg.title, "Test issue")
  886. # Not logged in
  887. output = self.app.get("/test/issue/2")
  888. self.assertEqual(output.status_code, 404)
  889. # Wrong user
  890. user = tests.FakeUser()
  891. with tests.user_set(self.app.application, user):
  892. output = self.app.get("/test/issue/2")
  893. self.assertEqual(output.status_code, 404)
  894. # reporter
  895. user.username = "pingou"
  896. with tests.user_set(self.app.application, user):
  897. output = self.app.get("/test/issue/2")
  898. self.assertEqual(output.status_code, 200)
  899. output_text = output.get_data(as_text=True)
  900. self.assertIn(
  901. "<title>Issue #2: Test issue - test - Pagure</title>",
  902. output_text,
  903. )
  904. self.assertIn(
  905. '<span title="Private ticket" class="text-danger '
  906. 'fa fa-fw fa-lock"></span>',
  907. output_text,
  908. )
  909. self.assertIn(
  910. '<a class="btn btn-outline-secondary btn-sm border-0" '
  911. 'href="/test/issue/2/edit" title="Edit this issue">\n',
  912. output_text,
  913. )
  914. if __name__ == "__main__":
  915. unittest.main(verbosity=2)