test_pagure_flask_ui_clone.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015-2018 - Copyright Red Hat Inc
  4. Authors:
  5. Patrick Uiterwijk <puiterwijk@redhat.com>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import base64
  9. import datetime
  10. import unittest
  11. import shutil
  12. import sys
  13. import tempfile
  14. import os
  15. import six
  16. import json
  17. import pygit2
  18. from mock import patch, MagicMock
  19. sys.path.insert(
  20. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  21. )
  22. import pagure.lib.query
  23. import tests
  24. class PagureFlaskAppClonetests(tests.Modeltests):
  25. """ Tests for the clone bridging. """
  26. def setUp(self):
  27. super(PagureFlaskAppClonetests, self).setUp()
  28. tests.create_projects(self.session)
  29. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  30. tests.add_content_git_repo(
  31. os.path.join(self.path, "repos", "test.git")
  32. )
  33. tests.create_tokens(self.session)
  34. tests.create_tokens_acl(self.session)
  35. self.create_project_full("clonetest", {"create_readme": "y"})
  36. @patch.dict("pagure.config.config", {"ALLOW_HTTP_PULL_PUSH": False})
  37. def test_http_clone_disabled(self):
  38. """ Test that the HTTP clone endpoint gets correctly closed. """
  39. output = self.app.get(
  40. "/clonetest.git/info/refs?service=git-upload-pack"
  41. )
  42. self.assertEqual(output.status_code, 403)
  43. self.assertIn("not allowed", output.get_data(as_text=True))
  44. @patch.dict("pagure.config.config", {"ALLOW_HTTP_PULL_PUSH": True})
  45. def test_http_clone_invalid_service(self):
  46. """ Test that the HTTP endpoint refuses invalid services. """
  47. output = self.app.get("/clonetest.git/info/refs?service=myservice")
  48. self.assertEqual(output.status_code, 400)
  49. self.assertIn("Unknown service", output.get_data(as_text=True))
  50. @patch.dict("pagure.config.config", {"ALLOW_HTTP_PULL_PUSH": True})
  51. def test_http_clone_invalid_project(self):
  52. """ Test that the HTTP endpoint refuses invalid projects. """
  53. output = self.app.get(
  54. "/nosuchrepo.git/info/refs?service=git-upload-pack"
  55. )
  56. self.assertEqual(output.status_code, 404)
  57. self.assertIn("Project not found", output.get_data(as_text=True))
  58. @patch.dict("pagure.config.config", {"ALLOW_HTTP_PULL_PUSH": True})
  59. def test_http_clone_dumb(self):
  60. """ Test that the HTTP endpoint refuses dumb service request. """
  61. output = self.app.get("/clonetest.git/info/refs")
  62. self.assertEqual(output.status_code, 400)
  63. self.assertIn("Please switch", output.get_data(as_text=True))
  64. @patch.dict(
  65. "pagure.config.config",
  66. {
  67. "ALLOW_HTTP_PULL_PUSH": True,
  68. "ALLOW_HTTP_PUSH": False,
  69. "HTTP_REPO_ACCESS_GITOLITE": None,
  70. },
  71. )
  72. def test_http_push_disabled(self):
  73. """ Test that the HTTP push gets refused. """
  74. output = self.app.get(
  75. "/clonetest.git/info/refs?service=git-receive-pack"
  76. )
  77. self.assertEqual(output.status_code, 403)
  78. self.assertIn("pushing disabled", output.get_data(as_text=True))
  79. output = self.app.post("/clonetest.git/git-receive-pack")
  80. self.assertEqual(output.status_code, 403)
  81. self.assertIn("pushing disabled", output.get_data(as_text=True))
  82. @patch.dict(
  83. "pagure.config.config",
  84. {
  85. "ALLOW_HTTP_PULL_PUSH": True,
  86. "ALLOW_HTTP_PUSH": True,
  87. "HTTP_REPO_ACCESS_GITOLITE": None,
  88. },
  89. )
  90. def test_http_push_unauthed(self):
  91. """ Test that the HTTP push gets refused unauthed. """
  92. output = self.app.get(
  93. "/clonetest.git/info/refs?service=git-receive-pack"
  94. )
  95. self.assertEqual(output.status_code, 401)
  96. self.assertIn("Authorization Required", output.get_data(as_text=True))
  97. @patch.dict("pagure.config.config", {"ALLOW_HTTP_PULL_PUSH": True})
  98. def test_http_clone_private_project_unauthed(self):
  99. """ Test that the HTTP endpoint enforced project.private. """
  100. project = pagure.lib.query._get_project(self.session, "clonetest")
  101. project.private = True
  102. self.session.add(project)
  103. self.session.commit()
  104. output = self.app.get(
  105. "/clonetest.git/info/refs?service=git-upload-pack"
  106. )
  107. self.assertEqual(output.status_code, 404)
  108. self.assertIn("Project not found", output.get_data(as_text=True))
  109. @patch.dict(
  110. "pagure.config.config",
  111. {
  112. "ALLOW_HTTP_PULL_PUSH": True,
  113. "ALLOW_HTTP_PUSH": False,
  114. "HTTP_REPO_ACCESS_GITOLITE": None,
  115. },
  116. )
  117. def test_http_clone(self):
  118. """ Test that HTTP cloning gives reasonable output. """
  119. # Unfortunately, actually testing a git clone would need the app to
  120. # run on a TCP port, which the test environment doesn't do.
  121. output = self.app.get(
  122. "/clonetest.git/info/refs?service=git-upload-pack"
  123. )
  124. self.assertEqual(output.status_code, 200)
  125. output_text = output.get_data(as_text=True)
  126. self.assertIn("# service=git-upload-pack", output_text)
  127. self.assertIn(" refs/heads/master\n0000", output_text)
  128. output = self.app.post(
  129. "/clonetest.git/git-upload-pack",
  130. headers={"Content-Type": "application/x-git-upload-pack-request"},
  131. )
  132. # Git 2.17 returns 415, older return 200
  133. # Either means we didn't fully crash when returning the response
  134. self.assertIn(output.status_code, (200, 415))
  135. @patch.dict(
  136. "pagure.config.config",
  137. {
  138. "ALLOW_HTTP_PULL_PUSH": True,
  139. "ALLOW_HTTP_PUSH": False,
  140. "HTTP_REPO_ACCESS_GITOLITE": None,
  141. },
  142. )
  143. def test_http_clone_private(self):
  144. """ Test that HTTP cloning gives reasonable output with project.private. """
  145. # Unfortunately, actually testing a git clone would need the app to
  146. # run on a TCP port, which the test environment doesn't do.
  147. project = pagure.lib.query._get_project(self.session, "clonetest")
  148. project.private = True
  149. self.session.add(project)
  150. self.session.commit()
  151. output = self.app.get(
  152. "/clonetest.git/info/refs?service=git-upload-pack"
  153. )
  154. self.assertEqual(output.status_code, 404)
  155. self.assertIn("Project not found", output.get_data(as_text=True))
  156. output = self.app.get(
  157. "/clonetest.git/info/refs?service=git-upload-pack",
  158. environ_overrides={"REMOTE_USER": "pingou"},
  159. )
  160. self.assertEqual(output.status_code, 200)
  161. output_text = output.get_data(as_text=True)
  162. self.assertIn("# service=git-upload-pack", output_text)
  163. self.assertIn(" refs/heads/master\n0000", output_text)
  164. @patch.dict(
  165. "pagure.config.config",
  166. {
  167. "ALLOW_HTTP_PULL_PUSH": True,
  168. "ALLOW_HTTP_PUSH": True,
  169. "HTTP_REPO_ACCESS_GITOLITE": None,
  170. },
  171. )
  172. def test_http_push(self):
  173. """ Test that the HTTP push gets accepted. """
  174. output = self.app.get(
  175. "/clonetest.git/info/refs?service=git-receive-pack",
  176. environ_overrides={"REMOTE_USER": "pingou"},
  177. )
  178. self.assertEqual(output.status_code, 200)
  179. output_text = output.get_data(as_text=True)
  180. self.assertIn("# service=git-receive-pack", output_text)
  181. self.assertIn(" refs/heads/master\x00", output_text)
  182. @patch.dict(
  183. "pagure.config.config",
  184. {
  185. "ALLOW_HTTP_PULL_PUSH": True,
  186. "ALLOW_HTTP_PUSH": True,
  187. "HTTP_REPO_ACCESS_GITOLITE": None,
  188. },
  189. )
  190. def test_http_push_api_token(self):
  191. """ Test that the HTTP push gets accepted. """
  192. headers = {
  193. "Authorization": b"Basic %s"
  194. % base64.b64encode(b"pingou:aaabbbcccddd")
  195. }
  196. output = self.app.get(
  197. "/test.git/info/refs?service=git-receive-pack", headers=headers,
  198. )
  199. self.assertEqual(output.status_code, 200)
  200. output_text = output.get_data(as_text=True)
  201. self.assertIn("# service=git-receive-pack", output_text)
  202. self.assertIn(" refs/heads/master\x00", output_text)
  203. @patch.dict(
  204. "pagure.config.config",
  205. {
  206. "ALLOW_HTTP_PULL_PUSH": True,
  207. "ALLOW_HTTP_PUSH": True,
  208. "HTTP_REPO_ACCESS_GITOLITE": None,
  209. },
  210. )
  211. def test_http_push_projectless_api_token(self):
  212. """ Test that the HTTP push gets accepted. """
  213. tests.create_tokens(self.session, project_id=None, suffix="2")
  214. tests.create_tokens_acl(
  215. self.session, token_id="aaabbbcccddd2", acl_name="commit"
  216. )
  217. headers = {
  218. "Authorization": b"Basic %s"
  219. % base64.b64encode(b"pingou:aaabbbcccddd2")
  220. }
  221. output = self.app.get(
  222. "/clonetest.git/info/refs?service=git-receive-pack",
  223. headers=headers,
  224. )
  225. self.assertEqual(output.status_code, 200)
  226. output_text = output.get_data(as_text=True)
  227. self.assertIn("# service=git-receive-pack", output_text)
  228. self.assertIn(" refs/heads/master\x00", output_text)
  229. @patch.dict(
  230. "pagure.config.config",
  231. {
  232. "ALLOW_HTTP_PULL_PUSH": True,
  233. "ALLOW_HTTP_PUSH": True,
  234. "HTTP_REPO_ACCESS_GITOLITE": None,
  235. },
  236. )
  237. def test_http_push__invalid_project_for_api_token(self):
  238. """ Test that the HTTP push gets accepted. """
  239. headers = {
  240. "Authorization": b"Basic %s"
  241. % base64.b64encode(b"pingou:aaabbbcccddd")
  242. }
  243. output = self.app.get(
  244. "/clonetest.git/info/refs?service=git-receive-pack",
  245. headers=headers,
  246. )
  247. self.assertEqual(output.status_code, 401)
  248. self.assertIn("Authorization Required", output.get_data(as_text=True))
  249. @patch.dict(
  250. "pagure.config.config",
  251. {
  252. "ALLOW_HTTP_PULL_PUSH": True,
  253. "ALLOW_HTTP_PUSH": True,
  254. "HTTP_REPO_ACCESS_GITOLITE": None,
  255. },
  256. )
  257. def test_http_push_api_token_invalid_user(self):
  258. """ Test that the HTTP push gets accepted. """
  259. headers = {
  260. "Authorization": b"Basic %s"
  261. % base64.b64encode(b"invalid:aaabbbcccddd")
  262. }
  263. output = self.app.get(
  264. "/clonetest.git/info/refs?service=git-receive-pack",
  265. headers=headers,
  266. )
  267. self.assertEqual(output.status_code, 401)
  268. self.assertIn("Authorization Required", output.get_data(as_text=True))
  269. @patch.dict(
  270. "pagure.config.config",
  271. {
  272. "ALLOW_HTTP_PULL_PUSH": True,
  273. "ALLOW_HTTP_PUSH": True,
  274. "HTTP_REPO_ACCESS_GITOLITE": None,
  275. },
  276. )
  277. def test_http_push_invalid_api_token(self):
  278. """ Test that the HTTP push gets accepted. """
  279. headers = {
  280. "Authorization": b"Basic %s"
  281. % base64.b64encode(b"pingou:invalid_token")
  282. }
  283. output = self.app.get(
  284. "/clonetest.git/info/refs?service=git-receive-pack",
  285. headers=headers,
  286. )
  287. self.assertEqual(output.status_code, 401)
  288. self.assertIn("Authorization Required", output.get_data(as_text=True))
  289. @patch.dict(
  290. "pagure.config.config",
  291. {
  292. "ALLOW_HTTP_PULL_PUSH": True,
  293. "ALLOW_HTTP_PUSH": True,
  294. "HTTP_REPO_ACCESS_GITOLITE": None,
  295. },
  296. )
  297. def test_http_push_invalid_acl_on_token(self):
  298. """ Test that the HTTP push gets accepted. """
  299. tests.create_tokens(self.session, suffix="2")
  300. tests.create_tokens_acl(
  301. self.session, token_id="aaabbbcccddd2", acl_name="commit_flag"
  302. )
  303. headers = {
  304. "Authorization": b"Basic %s"
  305. % base64.b64encode(b"pingou:aaabbbcccddd2")
  306. }
  307. output = self.app.get(
  308. "/test.git/info/refs?service=git-receive-pack", headers=headers,
  309. )
  310. self.assertEqual(output.status_code, 401)
  311. self.assertIn("Authorization Required", output.get_data(as_text=True))
  312. @patch.dict(
  313. "pagure.config.config",
  314. {
  315. "ALLOW_HTTP_PULL_PUSH": True,
  316. "ALLOW_HTTP_PUSH": True,
  317. "HTTP_REPO_ACCESS_GITOLITE": None,
  318. "PAGURE_AUTH": "local",
  319. },
  320. )
  321. def test_http_push_local_auth(self):
  322. """ Test that the HTTP push gets accepted. """
  323. headers = {
  324. "Authorization": b"Basic %s" % base64.b64encode(b"pingou:foo")
  325. }
  326. output = self.app.get(
  327. "/clonetest.git/info/refs?service=git-receive-pack",
  328. headers=headers,
  329. )
  330. self.assertEqual(output.status_code, 200)
  331. output_text = output.get_data(as_text=True)
  332. self.assertIn("# service=git-receive-pack", output_text)
  333. self.assertIn(" refs/heads/master\x00", output_text)
  334. @patch.dict(
  335. "pagure.config.config",
  336. {
  337. "ALLOW_HTTP_PULL_PUSH": True,
  338. "ALLOW_HTTP_PUSH": True,
  339. "HTTP_REPO_ACCESS_GITOLITE": None,
  340. "PAGURE_AUTH": "local",
  341. },
  342. )
  343. def test_http_push_local_auth_invalid_username(self):
  344. """ Test that the HTTP push gets accepted. """
  345. headers = {
  346. "Authorization": b"Basic %s" % base64.b64encode(b"invalid:foo")
  347. }
  348. output = self.app.get(
  349. "/clonetest.git/info/refs?service=git-receive-pack",
  350. headers=headers,
  351. )
  352. self.assertEqual(output.status_code, 401)
  353. self.assertIn("Authorization Required", output.get_data(as_text=True))